From fe7d97a0a608398f500b1c64ff885acecf2541d5 Mon Sep 17 00:00:00 2001 From: Kyle Gunger Date: Thu, 18 May 2023 23:30:18 -0400 Subject: File reading/writing --- tnslc/c_wrap.tnsl | 153 ++++++++++++++++++++++++++++++++++++++++------ tnslc/enum.tnsl | 9 +++ tnslc/hello.tnsl | 31 +++++++++- tnslc/read_file_test.tnsl | 40 ++++++++++++ tnslc/tnslc.tnsl | 71 +++++++++++++-------- 5 files changed, 256 insertions(+), 48 deletions(-) create mode 100644 tnslc/enum.tnsl create mode 100644 tnslc/read_file_test.tnsl diff --git a/tnslc/c_wrap.tnsl b/tnslc/c_wrap.tnsl index ee39cdc..d121967 100644 --- a/tnslc/c_wrap.tnsl +++ b/tnslc/c_wrap.tnsl @@ -1,23 +1,25 @@ +# 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" /; _alloc (uint size) [~void] ~void out # Mov size into proper register, and set all extras to zero - asm "mov rax, 0" - asm "mov rbx, 0" asm "mov rcx, r8" asm "mov rdx, 0" - asm "mov rdi, 0" - asm "mov rsi, 0" asm "mov r8, 0" asm "mov r9, 0" - asm "mov r10, 0" asm "call malloc" # Set out to the returned value # (The compiler assignes spaces sequentially, and we have a uint in r8) @@ -28,15 +30,10 @@ asm "extern printf" /; _realloc (~void ptr, uint new_size) [~void] ~void out # Mov ptr and new size into proper registers, and set all extras to zero - asm "mov rax, 0" - asm "mov rbx, 0" asm "mov rcx, r8" asm "mov rdx, r9" - asm "mov rdi, 0" - asm "mov rsi, 0" asm "mov r8, 0" asm "mov r9, 0" - asm "mov r10, 0" # Do call asm "call realloc" # Set out to the returned value @@ -47,15 +44,10 @@ asm "extern printf" /; _delete (~void ptr) # setup call by clearing most values - asm "mov rax, 0" - asm "mov rbx, 0" asm "mov rcx, rax" asm "mov rdx, 0" - asm "mov rdi, 0" - asm "mov rsi, 0" asm "mov r8, 0" asm "mov r9, 0" - asm "mov r10, 0" # do call asm "call free" # there's no more to do 'cause free returns nothing @@ -65,11 +57,8 @@ asm "extern printf" # setup call by clearing most values asm "mov rcx, rax" asm "mov rdx, 0" - asm "mov rdi, 0" - asm "mov rsi, 0" asm "mov r8, 0" asm "mov r9, 0" - asm "mov r10, 0" # do call asm "call printf" # there's no more to do 'cause printf returns nothing @@ -83,13 +72,139 @@ asm "extern printf" asm "mov rsi, 0" asm "mov r8, 0" asm "mov r9, 0" - asm "mov r10, 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 rcx, r8" # lpFileName + asm "mov rdx, 3" # dwDesiredAccess + asm "shl rdx, 30" # (GENERIC_READ 1 << 31 | GENERIC_WRITE 1 << 30) + asm "mov r8, 0" # 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 rcx, r8" # lpFileName + asm "mov rdx, 3" # dwDesiredAccess + asm "shl rdx, 30" # (GENERIC_READ 1 << 31 | GENERIC_WRITE 1 << 30) + asm "mov r8, 0" # 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, and a uint in r9) + asm "mov r10, rax" + + return out +;/ + +/; _close_file (~void handle) + 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) [bool] + bool out + + 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 ReadFile" + + asm "mov r10, rax" + + return out +;/ + +/; _write_byte (~void handle, ~uint8 byte) [bool] + bool out + + 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" + + asm "mov r10, rax" + + return out +;/ + /; print_alert _printf(~_alert{0}) ;/ +/; cstr_len (~uint8 cstr) [int] + int i = 0 + /; loop (cstr{i} !== 0) [i++] ;/ + return i +;/ diff --git a/tnslc/enum.tnsl b/tnslc/enum.tnsl new file mode 100644 index 0000000..e8c4932 --- /dev/null +++ b/tnslc/enum.tnsl @@ -0,0 +1,9 @@ +# to get working + +enum TEST_ENUM [int] { + i = 0 +} + +/; main [int] + return TEST_ENUM.i +;/ \ No newline at end of file diff --git a/tnslc/hello.tnsl b/tnslc/hello.tnsl index 8fb98a3..0742564 100644 --- a/tnslc/hello.tnsl +++ b/tnslc/hello.tnsl @@ -1,8 +1,33 @@ :include "c_wrap.tnsl" -{}uint8 hello = "Hello World!\n\0" +{}uint8 star = "hello.tnsl\0" +{}uint8 space = " \0" +{}uint8 new_line = "\n\0" -/; main [int] - _printf(~hello{0}) +/; print_triangle (int argc) + /; loop (int i = 0; i < argc) [i++] + /; loop (int j = 0; j < argc - i) [j++] + _printf(~space{0}) + ;/ + /; loop (int j = 0; j < 1 + 2*i) [j++] + _printf(~star{0}) + ;/ + _printf(~new_line{0}) + ;/ +;/ + +/; main (int argc, ~~uint8 argv) [int] + # On windows, the first two arguments are passed in RCX and RDX, so we need to + # update their positions here or else tnsl will have garbage values in r8 and r9 + asm "mov r8, rcx" + asm "mov r9, rdx" + + # If on linux, you would use rdi and rsi instead of rcx and rdx, respectively + # simply comment out the bove asm, and uncomment the below lines + # asm "mov r8, rdi" + # asm "mov r9, rsi" + + print_triangle(argc) + return 0 ;/ \ No newline at end of file diff --git a/tnslc/read_file_test.tnsl b/tnslc/read_file_test.tnsl new file mode 100644 index 0000000..60c4c5b --- /dev/null +++ b/tnslc/read_file_test.tnsl @@ -0,0 +1,40 @@ +:include "c_wrap.tnsl" + +{}uint8 usage_msg = "Usage: read_file.exe [file to read] [file to write]\n\0" +{}uint8 print_one = "\0\0" +{}uint8 nl = "\n\0" + +/; main (int argc, ~~uint8 argv) [int] + + # On windows, the first two arguments are passed in RCX and RDX, so we need to + # update their positions here or else tnsl will have garbage values in r8 and r9 + asm "mov r8, rcx" + asm "mov r9, rdx" + + # If on linux, you would use rdi and rsi instead of rcx and rdx, respectively + # simply comment out the bove asm, and uncomment the below lines + # asm "mov r8, rdi" + # asm "mov r9, rsi" + + /; if (argc < 2) + _printf(~usage_msg{0}) + return 1 + ;/ + + _printf(argv{1}) + _printf(~nl{0}) + + ~void write_handle = _create_file(argv{1}) + + _print_num(~_dec{0}, write_handle) + + int ln = cstr_len(argv{1}) + + /; loop (int i = 0; i < ln) [i++] + _write_byte(write_handle, ~argv{1}{i}) + ;/ + + _close_file(write_handle) + + return 0 +;/ \ No newline at end of file diff --git a/tnslc/tnslc.tnsl b/tnslc/tnslc.tnsl index f75284d..a225041 100644 --- a/tnslc/tnslc.tnsl +++ b/tnslc/tnslc.tnsl @@ -3719,9 +3719,26 @@ ;/ ;/ +/; combine_tok_lists (int cut_a, cut_b, {}Token a, b) [{}Token] + ;{}Token out = {} + /; loop (int i = 0; i < cut_a) [i++] + ;out.append(a{i}) + ;/ + + /; loop (int i = 0; i < len b) [i++] + ;out.append(b{i}) + ;/ + + /; loop (int i = cut_b; i < len a) [i++] + ;out.append(a{i}) + ;/ + + ;return out +;/ + # First compiler pass on a file # Only creates structs, enums, and moduless -/; compile_file_pass_one (Path f, ~Module current) +/; compile_file_pass_one (Path f, ~Module current) [{}Token] ;{}Token tok = tokenize(f) ;log_info(string_add("Number of tokens generated: ", int_to_string(len tok))) @@ -3730,8 +3747,9 @@ /; if (tok{i}.cmp(":")) ;log_debug("INCLUDE") /; if (tok{i + 2}.type_is(TOKEN.LITERAL)) - ;compile_file_pass_one(f.relative(unquote_str(tok{i + 2}.data)), current) - ;i = i + 2 + ;{}Token inc = compile_file_pass_one(f.relative(unquote_str(tok{i + 2}.data)), current) + ;tok = combine_tok_lists(i, i + 3, tok, inc) + ;i = i + len inc ;/ ;continue ;; else if (tok{i}.cmp("/;") || tok{i}.cmp(";;")) @@ -3744,6 +3762,8 @@ ;new_type(~tok, ~i, current) ;/ ;/ + + ;return tok ;/ /; size_struct (~Type t, ~Module m) @@ -3784,50 +3804,49 @@ # Second pass of compiler # Does code generation, ignores structs and enums -/; compile_file_pass_two (Path f, ~Module current) [CompData] +/; compile_file_pass_two (~{}Token tok, ~Module current) [CompData] ;CompData out = {"", "", ""} - ;{}Token tok = tokenize(f) - /; loop (int i = next_non_nl(~tok, 0); i < len tok) [i = next_non_nl(~tok, i+1)] + /; loop (int i = next_non_nl(tok, 0); i < len tok`) [i = next_non_nl(tok, i+1)] ;log_vis(".") - /; if (tok{i}.cmp(":")) + /; if (tok`{i}.cmp(":")) ;log_debug("INCLUDE") - /; if (tok{i + 2}.type_is(TOKEN.LITERAL)) - ;CompData tmp = compile_file_pass_two(f.relative(unquote_str(tok{i + 2}.data)), current) + /; if (tok`{i + 2}.type_is(TOKEN.LITERAL)) + ;CompData tmp = compile_file_pass_two(f.relative(unquote_str(tok`{i + 2}.data)), current) ;out.hsec = string_add(out.hsec, tmp.hsec) ;out.dsec = string_add(out.dsec, tmp.dsec) ;out.csec = string_add(out.csec, tmp.csec) ;i = i + 2 ;/ ;continue - ;; else if (tok{i}.cmp("/;") || tok{i}.cmp(";;")) + ;; else if (tok`{i}.cmp("/;") || tok`{i}.cmp(";;")) ;log_debug("Root block") - /; if (tok{i + 1}.cmp("export") || tok{i + 1}.cmp("module")) - ;module_pass_two(~tok, ~i, current, ~out, f) + /; if (tok`{i + 1}.cmp("export") || tok`{i + 1}.cmp("module")) + ;module_pass_two(tok, ~i, current, ~out, f) ;; else - ;compile_block(~tok, ~i, current, ~out) + ;compile_block(tok, ~i, current, ~out) ;/ - ;; else if (tok{i}.cmp("struct")) + ;; else if (tok`{i}.cmp("struct")) ;log_debug("File struct") - ;skip_struct(~tok, ~i) - ;; else if (tok{i}.cmp("enum")) + ;skip_struct(tok, ~i) + ;; else if (tok`{i}.cmp("enum")) ;log_debug("File enum") - ;compile_enum(~tok, ~i, current, ~out) - ;; else if (is_definition(~tok, ~i, current)) + ;compile_enum(tok, ~i, current, ~out) + ;; else if (is_definition(tok, ~i, current)) ;log_debug("File def") - ;compile_file_def(~tok, ~i, current, ~out) - ;; else if (tok{i}.cmp("asm")) + ;compile_file_def(tok, ~i, current, ~out) + ;; else if (tok`{i}.cmp("asm")) ;log_debug("File asm") /; if (len (out.csec) == 0 && len (out.dsec) == 0) - ;out.hsec = string_add(out.hsec, unquote_str(tok{i + 1}.data)) + ;out.hsec = string_add(out.hsec, unquote_str(tok`{i + 1}.data)) ;out.hsec.append('\n') ;; else - ;out.dsec = string_add(out.dsec, unquote_str(tok{i + 1}.data)) + ;out.dsec = string_add(out.dsec, unquote_str(tok`{i + 1}.data)) ;out.dsec.append('\n') ;/ ;i++ - ;; else if (!(tok{i}.cmp("\n"))) - ;log_err(string_add("Failed to recognize file-level statement", tok{i}.sprint())) + ;; else if (!(tok`{i}.cmp("\n"))) + ;log_err(string_add("Failed to recognize file-level statement", tok`{i}.sprint())) ;/ ;/ @@ -3841,11 +3860,11 @@ ;{}uint8 out = "" ;Module root = {0, true, {}, {}, {}, {}, {}} - ;compile_file_pass_one(f, ~root) + ;{}Token tok = compile_file_pass_one(f, ~root) ;flush_structs(~root) ;log_info("First pass DONE") - ;CompData data = compile_file_pass_two(f, ~root) + ;CompData data = compile_file_pass_two(~tok, ~root) ;log_info("Second pass DONE") ;out = string_join({ -- cgit v1.2.3