summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--algo/algo.tnsl4
-rw-r--r--box/box.tnsl5
-rw-r--r--box/list.tnsl149
-rw-r--r--internal/c_wrap_linux.tnsl272
-rw-r--r--internal/c_wrap_windows.tnsl284
-rw-r--r--io/file.tnsl116
-rw-r--r--io/io.tnsl5
-rw-r--r--io/path.tnsl50
-rw-r--r--lang/lang.tnsl4
-rw-r--r--libtnsl.tnsl13
-rw-r--r--text/cstr.tnsl31
-rw-r--r--text/text.tnsl5
12 files changed, 938 insertions, 0 deletions
diff --git a/algo/algo.tnsl b/algo/algo.tnsl
new file mode 100644
index 0000000..7c261ae
--- /dev/null
+++ b/algo/algo.tnsl
@@ -0,0 +1,4 @@
+
+/; module algo
+;/
+
diff --git a/box/box.tnsl b/box/box.tnsl
new file mode 100644
index 0000000..189392f
--- /dev/null
+++ b/box/box.tnsl
@@ -0,0 +1,5 @@
+
+/; module box
+ import "list.tnsl"
+;/
+
diff --git a/box/list.tnsl b/box/list.tnsl
new file mode 100644
index 0000000..58278e4
--- /dev/null
+++ b/box/list.tnsl
@@ -0,0 +1,149 @@
+
+# List type - array backed list of elements
+struct List {
+ ~void data,
+
+ uint
+ count,
+ size,
+ _elsz
+}
+
+uint VECTOR_MIN_ELEMENTS = 4
+uint VECTOR_MAX_GROW = 256
+
+/; method Vector
+
+ /; init (uint elsz)
+ self._elsz = elsz
+ self.size = VECTOR_MIN_ELEMENTS
+ self.count = 0
+ self.data = _alloc(self.size * self._elsz)
+ ;/
+
+ /; from_cstr(~uint8 cstr)
+ self.init(1)
+ self.push_cstr(cstr)
+ ;/
+
+ /; _grow (uint i)
+ self.size = self.size + i
+ self.data = _realloc(self.data, self.size * self._elsz)
+ ;/
+
+ /; get (uint index) [~void]
+ /; if (index !< self.count)
+ return NULL
+ ;/
+
+ return self.data + index * self._elsz
+ ;/
+
+ /; push (~void el)
+ /; if (self.size == self.count + 1)
+ /; if (self.size < VECTOR_MAX_GROW)
+ self._grow(self.size)
+ ;; else
+ self._grow(VECTOR_MAX_GROW)
+ ;/
+ ;/
+
+ ~void start = self.data + self.count * self._elsz
+ /; loop (int i = 0; i < self._elsz) [i++]
+ ~uint8 to = start + i
+ ~uint8 from = el + i
+ to` = from`
+ ;/
+ self.count++
+ ;/
+
+ /; replace (int index, ~void el)
+ ~uint8 start = self.get(index)
+ /; if (start == NULL)
+ return
+ ;/
+
+ /; loop (int i = 0; i < self._elsz) [i++]
+ ~uint8 to = start + i
+ ~uint8 from = el + i
+ to` = from`
+ ;/
+ ;/
+
+ /; _shrink(uint i)
+ /; if (i !< self.size)
+ self.size = 1
+ ;; else
+ self.size = self.size - i
+ ;/
+
+ self.data = _realloc(self.data, self.size * self._elsz)
+ ;/
+
+ /; pop
+ self.remove(self.count - 1)
+ ;/
+
+ /; remove (int index)
+ /; if (index < 0 || index !< self.count)
+ return
+ ;/
+
+ /; if (self.count > 1)
+ /; loop (int i = index * self._elsz; i < (self.count - 1) * self._elsz) [i++]
+ ~uint8 to = self.data + i
+ ~uint8 from = self.data + i + self._elsz
+ to` = from`
+ ;/
+ ;/
+
+ self.count--
+
+ /; if (self.count < self.size / 2)
+ self._shrink(self.size / 3)
+ ;/
+ ;/
+
+ /; push_char (uint8 ch)
+ self.push(~ch)
+ ;/
+
+ /; push_cstr(~uint8 ch)
+ /; loop (ch` !== 0) [ch++]
+ self.push(ch)
+ ;/
+ ;/
+
+ /; as_cstr [~uint8]
+ ~uint8 z = self.data + self.count
+ z` = 0
+ return self.data
+ ;/
+
+ /; end
+ self.count = 0
+ self.size = 0
+ self._elsz = 0
+ _delete(self.data)
+ ;/
+
+ /; copy [Vector]
+ Vector out
+
+ out.init(self._elsz)
+ /; loop (int i = 0; i < self.count) [i++]
+ ~int tmp = self.get(i)
+ out.push(tmp)
+ ;/
+
+ return out
+ ;/
+
+ /; back [~void]
+ /; if (self.count > 0)
+ return self.get(self.count - 1)
+ ;/
+ return NULL
+ ;/
+;/
+
diff --git a/internal/c_wrap_linux.tnsl b/internal/c_wrap_linux.tnsl
new file mode 100644
index 0000000..31bee1a
--- /dev/null
+++ b/internal/c_wrap_linux.tnsl
@@ -0,0 +1,272 @@
+# Must be included at the top of the file
+asm "extern malloc, realloc, free, printf, putchar, open, close, read, write, lseek, perror"
+
+~void NULL = 0
+
+/; _alloc (uint size) [~void]
+ ~void out
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 128"
+
+ # Mov size into proper register, and set all extras to zero
+ asm "mov rdi, r10"
+ asm "mov rsi, 0"
+ asm "xor rax, rax"
+
+ # Do call
+ asm "call malloc wrt ..plt"
+
+ # Set out to the returned value
+ # (The compiler assignes spaces sequentially, and we have a uint in r10)
+ asm "mov r11, rax"
+
+ return out
+;/
+
+/; _realloc (~void ptr, uint new_size) [~void]
+ ~void out
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 128"
+
+ # Mov ptr and new size into proper registers, and set all extras to zero
+ asm "mov rdi, r10"
+ asm "mov rsi, r11"
+ asm "xor rax, rax"
+
+ # Do call
+ asm "call realloc wrt ..plt"
+
+ # Set out to the returned value
+ # (The compiler assignes spaces sequentially. We have a ptr in r10, and a uint in r11)
+ asm "mov r12, rax"
+
+ return out
+;/
+
+/; _delete (~void ptr)
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 128"
+
+ # setup call by clearing most values
+ asm "mov rdi, r10"
+ asm "mov rsi, 0"
+ asm "xor rax, rax"
+
+ # do call
+ asm "call free wrt ..plt"
+
+ # there's no more to do 'cause free returns nothing
+;/
+
+/; _putchar (uint8 char)
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 128"
+
+ # setup call by clearing most values
+ asm "mov rdi, r10"
+ asm "xor rsi, rsi"
+ asm "xor rax, rax"
+
+ # do call
+ asm "call putchar wrt ..plt"
+
+ # there's no more to do 'cause I don't care about putchar's output
+;/
+
+# wrap around putchar ( I was dumb and was using printf when I shouldn't have been )
+/; _print (~uint8 str)
+ /; loop (str` !== 0) [str++]
+ _putchar(str`)
+ ;/
+;/
+
+/; _print_num (~void str, int num)
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 128"
+
+ # setup call by clearing most values
+ asm "mov rdi, r10"
+ asm "mov rsi, r11"
+ asm "xor rax, rax"
+
+ # do call
+ asm "call printf wrt ..plt"
+
+ # there's no more to do 'cause printf returns nothing
+;/
+
+
+# Create file for writing (overwrite)
+/; _create_file (~void name) [int]
+ int out
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 128"
+
+ # Do call
+ asm "mov rdi, r10"
+ asm "mov rsi, 0q1102"
+ asm "mov rdx, 0q700"
+ asm "call open wrt ..plt"
+
+ # Set out to the returned value
+ # (The compiler assignes spaces sequentially. We have a ptr in r8)
+ asm "mov r11, rax"
+
+ return out
+;/
+
+# Open file for reading or writing (no overwrite)
+/; _open_file (~void name) [int]
+ int out
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 128"
+
+ # Do syscall
+ asm "mov rdi, r10"
+ asm "mov rsi, 2"
+ asm "mov rdx, 0q700"
+ asm "call open wrt ..plt"
+
+ # Set out to the returned value
+ # (The compiler assignes spaces sequentially. We have a ptr in r8, and a uint in r9)
+ asm "mov r11, rax"
+
+ return out
+;/
+
+/; _close_file (int handle)
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 128"
+
+ # Do syscall
+ asm "mov rdi, r10"
+ asm "call close wrt ..plt"
+;/
+
+/; _read_byte (int handle, ~uint8 byte) [int]
+ int out
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 128"
+
+ # Do syscall
+ asm "mov rdi, r10" # handle
+ asm "mov rsi, r11" # buffer
+ asm "mov rdx, 1" # one byte
+ asm "call read wrt ..plt"
+
+ # return number of bytes read
+ asm "mov r12, rax"
+
+ return out
+;/
+
+/; _fseek (int handle, uint pos) [int]
+ int out
+
+ # align stack
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ # add buffer zone to stack
+ asm "sub rsp, 128"
+
+ # Call c func
+ asm "mov rdi, r10"
+ asm "mov rsi, r11"
+ asm "mov rdx, 0" # standard value for SEEK_SET as per GNU libc
+ asm "call lseek wrt ..plt"
+
+ # get return value
+ asm "mov r12, rax"
+
+ return out
+;/
+
+/; _write_byte (int handle, ~uint8 byte) [int]
+ int out
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 128"
+
+ # Do syscall
+ asm "mov rdi, r10" # handle
+ asm "mov rsi, r11" # buffer
+ asm "mov rdx, 1" # one byte
+ asm "call write wrt ..plt"
+
+ asm "mov r12, rax"
+
+ return out
+;/
+
+/; _perror (~void str)
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 16"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 128"
+
+ # setup call by clearing most values
+ asm "mov rdi, r10"
+ asm "xor rsi, rsi"
+ asm "xor rax, rax"
+
+ # do call
+ asm "call perror wrt ..plt"
+
+ # there's no more to do 'cause printf returns nothing
+;/
+
diff --git a/internal/c_wrap_windows.tnsl b/internal/c_wrap_windows.tnsl
new file mode 100644
index 0000000..839c976
--- /dev/null
+++ b/internal/c_wrap_windows.tnsl
@@ -0,0 +1,284 @@
+# Must be included at the top of the file
+asm "extern malloc"
+asm "extern realloc"
+asm "extern free"
+
+asm "extern printf"
+
+asm "extern CreateFileA"
+asm "extern ReadFile"
+asm "extern WriteFile"
+asm "extern CloseHandle"
+
+~uint8 _alert = "Alert!\n\0"
+~uint8 _dec = "%d\n\0"
+~uint8 _ptr = "%p\n\0"
+
+/; _alloc (uint size) [~void]
+ ~void out
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 32"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 32"
+
+ # Mov size into proper register, and set all extras to zero
+ asm "mov rcx, r8"
+ asm "mov rdx, 0"
+ asm "mov r8, 0"
+ asm "mov r9, 0"
+
+ asm "call malloc"
+ # Set out to the returned value
+ # (The compiler assignes spaces sequentially, and we have a uint in r8)
+ asm "mov r9, rax"
+ return out
+;/
+
+/; _realloc (~void ptr, uint new_size) [~void]
+ ~void out
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 32"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 32"
+
+ # Mov ptr and new size into proper registers, and set all extras to zero
+ asm "mov rcx, r8"
+ asm "mov rdx, r9"
+ asm "mov r8, 0"
+ asm "mov r9, 0"
+ # Do call
+ asm "call realloc"
+ # Set out to the returned value
+ # (The compiler assignes spaces sequentially. We have a ptr in r8, and a uint in r9)
+ asm "mov r10, rax"
+ return out
+;/
+
+/; _delete (~void ptr)
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 32"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "sub rsp, 32"
+
+ # setup call by clearing most values
+ asm "mov rcx, r8"
+ asm "mov rdx, 0"
+ asm "mov r8, 0"
+ asm "mov r9, 0"
+ # do call
+ asm "call free"
+ # there's no more to do 'cause free returns nothing
+;/
+
+/; _printf (~void str)
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 32"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "push qword 0"
+ asm "push qword 0"
+ asm "push qword 0"
+ asm "push qword 0"
+
+ # setup call by clearing most values
+ asm "mov rcx, r8"
+ asm "mov rdx, 0"
+ asm "mov r8, 0"
+ asm "mov r9, 0"
+ # do call
+ asm "call printf"
+ # there's no more to do 'cause printf returns nothing
+;/
+
+/; _print_num (~void str, int num)
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 32"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+ asm "push qword 0"
+ asm "push qword 0"
+ asm "push qword 0"
+ asm "push qword 0"
+
+ # setup call by clearing most values
+ asm "mov rcx, r8"
+ asm "mov rdx, r9"
+ asm "mov rdi, 0"
+ asm "mov rsi, 0"
+ asm "mov r8, 0"
+ asm "mov r9, 0"
+ # do call
+ asm "call printf"
+ # there's no more to do 'cause printf returns nothing
+;/
+
+
+# Create file for writing (overwrite)
+/; _create_file (~void name) [~void]
+ ~void out
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 32"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+
+ asm "mov rcx, r8" # lpFileName
+ asm "mov rdx, 1" # dwDesiredAccess
+ asm "shl rdx, 28" # (GENERIC_READ 1 << 31 | GENERIC_WRITE 1 << 30)
+ asm "mov r8, 3" # dwShareMode
+ asm "mov r9, 0" # lpSecurityAttributes
+
+ asm "push qword 0" # hTemplateFile
+ asm "push qword 128" # dwFlagsAndAttributes (NORMAL_FILE = 128)
+ asm "push qword 2" # dwCreationDisposition (CREATE_ALWAYS = 2)
+
+ # Shadow space
+ asm "push r9"
+ asm "push r8"
+ asm "push rdx"
+ asm "push rcx"
+
+ # Do call
+ asm "call CreateFileA"
+
+ # Set out to the returned value
+ # (The compiler assignes spaces sequentially. We have a ptr in r8)
+ asm "mov r9, rax"
+
+ return out
+;/
+
+# Open file for reading or writing (no overwrite)
+/; _open_file (~void name) [~void]
+ ~void out
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 32"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+
+ asm "mov rcx, r8" # lpFileName
+ asm "mov rdx, 1" # dwDesiredAccess
+ asm "shl rdx, 28" # (GENERIC_READ 1 << 31 | GENERIC_WRITE 1 << 30)
+ asm "mov r8, 3" # dwShareMode
+ asm "mov r9, 0" # lpSecurityAttributes
+
+ asm "push qword 0" # hTemplateFile
+ asm "push qword 128" # dwFlagsAndAttributes (NORMAL_FILE = 128)
+ asm "push qword 3" # dwCreationDisposition (OPEN_EXISTING = 3)
+
+ # Shadow space
+ asm "push r9"
+ asm "push r8"
+ asm "push rdx"
+ asm "push rcx"
+
+ # Do call
+ asm "call CreateFileA"
+
+ # Set out to the returned value
+ # (The compiler assignes spaces sequentially. We have a ptr in r8, and a uint in r9)
+ asm "mov r9, rax"
+
+ return out
+;/
+
+/; _close_file (~void handle)
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 32"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+
+ asm "mov rcx, r8" # handle
+ asm "mov rdx, 0"
+ asm "mov r8, 0"
+ asm "mov r9, 0"
+
+ # Shadow space
+ asm "push r9"
+ asm "push r8"
+ asm "push rdx"
+ asm "push rcx"
+
+ asm "call CloseHandle"
+
+ asm "add rsp, 32"
+;/
+
+/; _read_byte (~void handle, ~uint8 byte) [int]
+ ~int out
+ int i = 0
+ out = ~i
+
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 32"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+
+ asm "mov rcx, r8" # handle
+ asm "mov rdx, r9" # buffer
+ asm "mov r8, 1" # one byte
+ asm "mov r9, r10" # read bytes buffer
+ asm "push qword 0"
+
+ # Shadow space
+ asm "push r9"
+ asm "push r8"
+ asm "push rdx"
+ asm "push rcx"
+
+ asm "call ReadFile"
+
+ return out`
+;/
+
+/; _write_byte (~void handle, ~uint8 byte) [int]
+ ~int out
+ int i = 0
+ out = ~i
+
+ asm "mov rax, rsp"
+ asm "xor rdx, rdx"
+ asm "mov rcx, 32"
+ asm "div rcx"
+ asm "sub rsp, rdx"
+
+ asm "mov rcx, r8" # handle
+ asm "mov rdx, r9" # buffer
+ asm "mov r8, 1" # one byte
+ asm "mov r9, 0"
+ asm "push qword 0"
+
+ # Shadow space
+ asm "push r9"
+ asm "push r8"
+ asm "push rdx"
+ asm "push rcx"
+
+ asm "call WriteFile"
+
+ return out`
+;/
+
+/; print_alert
+ _printf(_alert)
+;/
diff --git a/io/file.tnsl b/io/file.tnsl
new file mode 100644
index 0000000..2786179
--- /dev/null
+++ b/io/file.tnsl
@@ -0,0 +1,116 @@
+
+# File type
+struct File {
+ # List containing the path
+ Path path,
+ int32 handle,
+ uint pos,
+ bool at_end
+}
+
+/; method File
+ /; init (~uint8 str)
+ self.path.init()
+ self.path.from_cstr(str)
+ self.handle = 0
+ self.handle--
+ self.at_end = false
+ self.pos = 0
+ ;/
+
+ /; relative (~uint8 path) [File]
+ Path rel = self.path.relative(path)
+ ~uint8 new_path = rel.to_cstr()
+ File out
+ out.init(new_path)
+ _delete(new_path)
+ rel.end()
+ return out
+ ;/
+
+ /; end
+ /; if (self.handle + 1 !== 0)
+ _close_file(self.handle)
+ self.handle = 0
+ self.handle--
+ ;/
+ self.path.end()
+ ;/
+
+ /; open
+ ~uint8 p = self.path.to_cstr()
+ self.handle = _open_file(p)
+
+ /; if (self.handle + 1 == 0)
+ self.at_end = true
+ ;/
+ _delete(p)
+ ;/
+
+ /; create
+ ~uint8 p = self.path.to_cstr()
+ self.handle = _create_file(p)
+ /; if (self.handle + 1 == 0)
+ self.at_end = true
+ ;/
+ _delete(p)
+ ;/
+
+ /; close
+ /; if (self.handle + 1 !== 0)
+ _close_file(self.handle)
+ self.handle = 0
+ self.handle--
+ self.at_end = false
+ ;/
+ ;/
+
+ /; read [uint8]
+ /; if (self.at_end == true)
+ return 0
+ ;/
+
+ uint8 out
+ int bytes = _read_byte(self.handle, ~out)
+ self.pos = self.pos + 1
+
+ /; if (bytes == 0)
+ self.at_end = true
+ return 0
+ ;; else if (bytes !== 1)
+ _perror("Error reading from file\0")
+ _print_num("FD: %ld\n\0", self.handle)
+ self.at_end = true
+ return 0
+ ;/
+
+ return out
+ ;/
+
+ /; unread
+ /; if (self.pos < 1)
+ return
+ ;/
+
+ self.pos = self.pos - 1
+ _fseek(self.handle, self.pos)
+
+ /; if (self.at_end == true)
+ self.at_end = false
+ ;/
+ ;/
+
+ /; write (uint8 byte)
+ int written = _write_byte(self.handle, ~byte)
+ /; if (written == 0)
+ self.at_end = true
+ ;/
+ ;/
+
+ /; write_cstr (~uint8 str)
+ /; loop (self.at_end == false && str` !== 0) [str++]
+ self.write(str`)
+ ;/
+ ;/
+
+;/
diff --git a/io/io.tnsl b/io/io.tnsl
new file mode 100644
index 0000000..5baead5
--- /dev/null
+++ b/io/io.tnsl
@@ -0,0 +1,5 @@
+
+/; module io
+ import "file.tnsl"
+;/
+
diff --git a/io/path.tnsl b/io/path.tnsl
new file mode 100644
index 0000000..f72397d
--- /dev/null
+++ b/io/path.tnsl
@@ -0,0 +1,50 @@
+
+# Path type
+struct Path {
+ box.List strings
+}
+
+uint8 PATH_SEP = '/'
+~uint8 PARENT_DIR = "..\0"
+~uint8 CURRENT_DIR = ".\0"
+
+/; method Path
+ /; init
+ self.strings.init(8)
+ ;/
+
+ /; from_cstr (~uint8 path)
+ ;/
+
+ /; to_cstr [~uint8]
+ box.List out
+ out.from_cstr("\0")
+
+ /; if (self.strings.count < 1)
+ return out.as_cstr()
+ ;/
+
+ ~uint8 str = self.strings.get(0)
+ out.push_cstr(str)
+
+ /; loop (int i = 1; i < self.strings.count) [i++]
+ out.push(~PATH_SEP)
+ out.push_cstr(str)
+ ;/
+
+ return out.as_cstr()
+ ;/
+
+ /; relative (~uint8 path) [Path]
+
+ ;/
+
+ /; end
+ ~~uint8 str
+ /; loop (int i = 0; i < self.strings.count) [i++]
+ str = self.strings.get(i)
+ _delete(str`)
+ ;/
+ self.strings.end()
+ ;/
+;/
diff --git a/lang/lang.tnsl b/lang/lang.tnsl
new file mode 100644
index 0000000..b9cc157
--- /dev/null
+++ b/lang/lang.tnsl
@@ -0,0 +1,4 @@
+
+/; module lang
+;/
+
diff --git a/libtnsl.tnsl b/libtnsl.tnsl
index e69de29..dcad398 100644
--- a/libtnsl.tnsl
+++ b/libtnsl.tnsl
@@ -0,0 +1,13 @@
+# Base module for libtnsl
+/; module tnsl
+ # Wrap c calls for now wrt memory management
+ import "internal/c_wrap_linux.tnsl"
+
+ # Basic modules
+ import "algo/algo.tnsl"
+ import "box/box.tnsl"
+ import "io/io.tnsl"
+ import "lang/lang.tnsl"
+ import "text/text.tnsl"
+;/
+
diff --git a/text/cstr.tnsl b/text/cstr.tnsl
new file mode 100644
index 0000000..67db577
--- /dev/null
+++ b/text/cstr.tnsl
@@ -0,0 +1,31 @@
+
+/; cstr_cmp (~uint8 a, b) [bool]
+ /; loop (a` == b` && a` !== 0 && b` !== 0) [a++; b++] ;/
+
+ /; if (b` == 0 && a == 0)
+ return true
+ ;/
+
+ return false
+;/
+
+/; cstr_len (~uint8 a) [uint]
+ uint out = 0
+ /; loop (str` !== 0) [str++; out++] ;/
+ return out
+;/
+
+/; cstr_starts_with (~uint8 str, prefix) [bool]
+ int chk = cstr_len(suffix)
+ int counter = 0
+
+ /; loop (str` !== 0) [str++]
+ ;/
+
+/; cstr_ends_with (~uint8 str, suffix) [bool]
+ int chk = cstr_len(suffix)
+ int counter = 0
+
+ /; loop (str` !== 0) [str++]
+ ;/
+;/
diff --git a/text/text.tnsl b/text/text.tnsl
new file mode 100644
index 0000000..951e697
--- /dev/null
+++ b/text/text.tnsl
@@ -0,0 +1,5 @@
+
+/; module text
+ import "cstr.tnsl"
+;/
+