diff --git a/a.out b/a.out index c3e95ec..5ea17b9 100755 Binary files a/a.out and b/a.out differ diff --git a/build/hello.asm b/build/hello.asm index 1d5188d..12c9dff 100644 --- a/build/hello.asm +++ b/build/hello.asm @@ -19,7 +19,7 @@ _start: mov rax, 60 syscall word_puts: - ; detects string if top is len>=0 and next is a pointer in [data_start, data_end) + ; detects string if top is len>=0 and next is a pointer in [data_start, data_end] mov rax, [r12] ; len or int value mov rbx, [r12 + 8] ; possible address cmp rax, 0 diff --git a/build/t.asm b/build/t.asm new file mode 100644 index 0000000..f74035a --- /dev/null +++ b/build/t.asm @@ -0,0 +1,382 @@ +section .text +%define DSTK_BYTES 65536 +%define RSTK_BYTES 65536 +%define PRINT_BUF_BYTES 128 +global _start +_start: + ; initialize data/return stack pointers + lea r12, [rel dstack_top] + mov r15, r12 + lea r13, [rel rstack_top] + call word_main + mov rax, 0 + cmp r12, r15 + je .no_exit_value + mov rax, [r12] + add r12, 8 +.no_exit_value: + mov rdi, rax + mov rax, 60 + syscall +word_puts: + ; detects string if top is len>=0 and next is a pointer in [data_start, data_end] + mov rax, [r12] ; len or int value + mov rbx, [r12 + 8] ; possible address + cmp rax, 0 + jl puts_print_int + lea r8, [rel data_start] + lea r9, [rel data_end] + cmp rbx, r8 + jl puts_print_int + cmp rbx, r9 + jge puts_print_int + ; treat as string: (addr below len) + mov rdx, rax ; len + mov rsi, rbx ; addr + add r12, 16 ; pop len + addr + test rdx, rdx + jz puts_str_newline_only + mov rax, 1 + mov rdi, 1 + syscall +puts_str_newline_only: + mov byte [rel print_buf], 10 + mov rax, 1 + mov rdi, 1 + lea rsi, [rel print_buf] + mov rdx, 1 + syscall + ret + +puts_print_int: + mov rax, [r12] + add r12, 8 + mov rbx, rax + mov r8, 0 + cmp rbx, 0 + jge puts_abs + neg rbx + mov r8, 1 +puts_abs: + lea rsi, [rel print_buf_end] + mov rcx, 0 + mov r10, 10 + cmp rbx, 0 + jne puts_digits + dec rsi + mov byte [rsi], '0' + inc rcx + jmp puts_sign +puts_digits: +puts_loop: + xor rdx, rdx + mov rax, rbx + div r10 + add dl, '0' + dec rsi + mov [rsi], dl + inc rcx + mov rbx, rax + test rbx, rbx + jne puts_loop +puts_sign: + cmp r8, 0 + je puts_finish_digits + dec rsi + mov byte [rsi], '-' + inc rcx +puts_finish_digits: + mov byte [rsi + rcx], 10 + inc rcx + mov rax, 1 + mov rdi, 1 + mov rdx, rcx + mov r9, rsi + mov rsi, r9 + syscall + ret +word_dup: + mov rax, [r12] + sub r12, 8 + mov [r12], rax + ret +word_drop: + add r12, 8 + ret +word_over: + mov rax, [r12 + 8] + sub r12, 8 + mov [r12], rax + ret +word_swap: + mov rax, [r12] + mov rbx, [r12 + 8] + mov [r12], rbx + mov [r12 + 8], rax + ret +word_rot: + mov rax, [r12] ; x3 + mov rbx, [r12 + 8] ; x2 + mov rcx, [r12 + 16] ; x1 + mov [r12], rcx ; top = x1 + mov [r12 + 8], rax ; next = x3 + mov [r12 + 16], rbx ; third = x2 + ret +word__2drot: + mov rax, [r12] ; x3 + mov rbx, [r12 + 8] ; x2 + mov rcx, [r12 + 16] ; x1 + mov [r12], rbx ; top = x2 + mov [r12 + 8], rcx ; next = x1 + mov [r12 + 16], rax ; third = x3 + ret +word_nip: + mov rax, [r12] + add r12, 8 ; drop lower element + mov [r12], rax ; keep original top + ret +word_tuck: + mov rax, [r12] ; x2 + mov rbx, [r12 + 8] ; x1 + sub r12, 8 ; make room + mov [r12], rax ; x2 + mov [r12 + 8], rbx ; x1 + mov [r12 + 16], rax ; x2 + ret +word_2dup: + mov rax, [r12] ; b + mov rbx, [r12 + 8] ; a + sub r12, 8 + mov [r12], rbx ; push a + sub r12, 8 + mov [r12], rax ; push b + ret +word_2drop: + add r12, 16 + ret +word_2swap: + mov rax, [r12] ; d + mov rbx, [r12 + 8] ; c + mov rcx, [r12 + 16] ; b + mov rdx, [r12 + 24] ; a + mov [r12], rcx ; top = b + mov [r12 + 8], rdx ; next = a + mov [r12 + 16], rax ; third = d + mov [r12 + 24], rbx ; fourth = c + ret +word_2over: + mov rax, [r12 + 16] ; b + mov rbx, [r12 + 24] ; a + sub r12, 8 + mov [r12], rbx ; push a + sub r12, 8 + mov [r12], rax ; push b + ret +word__2b: + mov rax, [r12] + add r12, 8 + add qword [r12], rax + ret +word__2d: + mov rax, [r12] + add r12, 8 + sub qword [r12], rax + ret +word__2a: + mov rax, [r12] + add r12, 8 + imul qword [r12] + mov [r12], rax + ret +word__2f: + mov rbx, [r12] + add r12, 8 + mov rax, [r12] + cqo + idiv rbx + mov [r12], rax + ret +word__25: + mov rbx, [r12] + add r12, 8 + mov rax, [r12] + cqo + idiv rbx + mov [r12], rdx + ret +word__3d_3d: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + sete bl + mov [r12], rbx + ret +word__21_3d: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + setne bl + mov [r12], rbx + ret +word__3c: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + setl bl + mov [r12], rbx + ret +word__3e: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + setg bl + mov [r12], rbx + ret +word__3c_3d: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + setle bl + mov [r12], rbx + ret +word__3e_3d: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + setge bl + mov [r12], rbx + ret +word__40: + mov rax, [r12] + mov rax, [rax] + mov [r12], rax + ret +word__21: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + mov [rax], rbx + add r12, 8 + ret +word_mmap: + mov r9, [r12] + add r12, 8 + mov r8, [r12] + add r12, 8 + mov r10, [r12] + add r12, 8 + mov rdx, [r12] + add r12, 8 + mov rsi, [r12] + add r12, 8 + mov rdi, [r12] + mov rax, 9 + syscall + mov [r12], rax + ret +word_munmap: + mov rsi, [r12] + add r12, 8 + mov rdi, [r12] + mov rax, 11 + syscall + mov [r12], rax + ret +word_exit: + mov rdi, [r12] + add r12, 8 + mov rax, 60 + syscall + ret +word_and: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + test rax, rax + setz cl + test rbx, rbx + setz dl + movzx rcx, cl + movzx rdx, dl + and rcx, rdx + mov [r12], rcx + ret +word_or: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + test rax, rax + setz cl + test rbx, rbx + setz dl + movzx rcx, cl + movzx rdx, dl + or rcx, rdx + mov [r12], rcx + ret +word_not: + mov rax, [r12] + test rax, rax + setz al + movzx rax, al + mov [r12], rax + ret +word__3er: + mov rax, [r12] + add r12, 8 + sub r13, 8 + mov [r13], rax + ret +word_r_3e: + mov rax, [r13] + add r13, 8 + sub r12, 8 + mov [r12], rax + ret +word_rdrop: + add r13, 8 + ret +word_pick: + mov rcx, [r12] + add r12, 8 + mov rax, [r12 + rcx * 8] + sub r12, 8 + mov [r12], rax + ret +word_rpick: + mov rcx, [r12] + add r12, 8 + mov rax, [r13 + rcx * 8] + sub r12, 8 + mov [r12], rax + ret +word_main: + ; push 0 + sub r12, 8 + mov qword [r12], 0 + ret +section .data +data_start: +data_end: +section .bss +align 16 +dstack: resb DSTK_BYTES +dstack_top: +align 16 +rstack: resb RSTK_BYTES +rstack_top: +align 16 +print_buf: resb PRINT_BUF_BYTES +print_buf_end: \ No newline at end of file diff --git a/build/t.o b/build/t.o new file mode 100644 index 0000000..c1e8527 Binary files /dev/null and b/build/t.o differ diff --git a/build/test.asm b/build/test.asm index e368921..d8efd13 100644 --- a/build/test.asm +++ b/build/test.asm @@ -594,18 +594,18 @@ word_test_2dif: mov rax, [r12] add r12, 8 test rax, rax - jz L_if_false_24 + jz L_if_false_34 ; push 111 sub r12, 8 mov qword [r12], 111 call word_puts - jmp L_if_end_25 -L_if_false_24: + jmp L_if_end_35 +L_if_false_34: ; push 222 sub r12, 8 mov qword [r12], 222 call word_puts -L_if_end_25: +L_if_end_35: ret word_test_2delse_2dif: ; push 2 @@ -619,13 +619,13 @@ word_test_2delse_2dif: mov rax, [r12] add r12, 8 test rax, rax - jz L_if_false_26 + jz L_if_false_36 ; push 50 sub r12, 8 mov qword [r12], 50 call word_puts - jmp L_if_end_27 -L_if_false_26: + jmp L_if_end_37 +L_if_false_36: call word_dup ; push 2 sub r12, 8 @@ -634,19 +634,19 @@ L_if_false_26: mov rax, [r12] add r12, 8 test rax, rax - jz L_if_false_28 + jz L_if_false_38 ; push 60 sub r12, 8 mov qword [r12], 60 call word_puts - jmp L_if_end_29 -L_if_false_28: + jmp L_if_end_39 +L_if_false_38: ; push 70 sub r12, 8 mov qword [r12], 70 call word_puts -L_if_end_29: -L_if_end_27: +L_if_end_39: +L_if_end_37: call word_drop ret word_test_2dfor: @@ -659,10 +659,10 @@ word_test_2dfor: mov rax, [r12] add r12, 8 cmp rax, 0 - jle L_for_end_31 + jle L_for_end_41 sub r13, 8 mov [r13], rax -L_for_loop_30: +L_for_loop_40: ; push 1 sub r12, 8 mov qword [r12], 1 @@ -670,9 +670,9 @@ L_for_loop_30: mov rax, [r13] dec rax mov [r13], rax - jg L_for_loop_30 + jg L_for_loop_40 add r13, 8 -L_for_end_31: +L_for_end_41: call word_puts ret word_test_2dfor_2dzero: @@ -685,17 +685,17 @@ word_test_2dfor_2dzero: mov rax, [r12] add r12, 8 cmp rax, 0 - jle L_for_end_33 + jle L_for_end_43 sub r13, 8 mov [r13], rax -L_for_loop_32: +L_for_loop_42: call word_drop mov rax, [r13] dec rax mov [r13], rax - jg L_for_loop_32 + jg L_for_loop_42 add r13, 8 -L_for_end_33: +L_for_end_43: call word_puts ret word_test_2dstruct: diff --git a/build/test.o b/build/test.o index 450ae02..8ab8ab8 100644 Binary files a/build/test.o and b/build/test.o differ diff --git a/fn.sl b/fn.sl index 6b736c0..e40ca06 100644 --- a/fn.sl +++ b/fn.sl @@ -30,6 +30,7 @@ again immediate compile-only + : extend-syntax "call-syntax-rewrite" set-token-hook ; @@ -127,6 +128,181 @@ compile-only ; compile-only +: fn-emit-prologue # params out -- params out + over list-length # params out n +begin + dup 0 > if + 1 - # params out n-1 + >r # params out (r: n-1) + ">r" list-append # params out' + r> # params out' n-1 + continue + then + drop # params out + exit +again +; +compile-only + +: fn-emit-epilogue # params out -- out + over list-length >r # params out (r: n) +begin + r> dup 0 > if + 1 - >r + "rdrop" list-append + continue + then + drop # drop counter + swap drop # out + exit +again +; +compile-only + +: fn-translate-prologue-loop # count -- + dup 0 > if + 1 - + 0 rpick ">r" list-append drop + fn-translate-prologue-loop + then + drop +; +compile-only + +: fn-translate-epilogue-loop # count -- + dup 0 > if + 1 - + 0 rpick "rdrop" list-append drop + fn-translate-epilogue-loop + then + drop +; +compile-only + +: fn-param-index # params name -- params idx flag + >r # params (r: name) + 0 # params idx + +begin + over list-length over swap >= if # params idx flag (idx >= len?) + drop # params + r> drop # drop name + -1 0 exit # params -1 0 + then # params idx + over over list-get # params idx elem + 0 rpick string= # params idx flag + if + r> drop # drop name + 1 exit # params idx 1 + then + drop # params idx + 1 + # params idx+1 +again +; +compile-only + + +: fn-build-param-map # params -- params map + map-new # params map + 0 # params map idx + begin + 2 pick list-length # params map idx len + over swap >= if # params map idx flag + drop # params map + exit + then # params map idx + 2 pick over list-get # params map idx name + swap # params map name idx + dup >r # params map name idx (r: idx) + map-set # params map' + r> 1 + # params map' idx' + continue + again +; +compile-only + + +: fn-translate-token # out map tok -- out map + # number? + dup string>number # out map tok num ok + if + # drop tok, append num + swap drop # out map num + swap >r # out num (r: map) + list-append # out' + r> # out' map + exit + then + drop # out map tok + + # param? + dup >r # out map tok (r: tok) + map-get # out map idx|nil ok + if + # append idx + swap >r # out idx (r: map tok) + list-append # out' + r> # out' map + # append "rpick" + "rpick" # out' map "rpick" + swap >r # out' "rpick" (r: map tok) + list-append # out'' + r> # out'' map + # drop saved tok + r> drop # out'' map + exit + then + # not a param: drop idx|nil, append original tok + drop # out map + r> # out map tok + swap >r # out tok (r: map) + list-append # out' + r> # out' map +; +compile-only + + +: fn-translate-postfix-loop # map out postfix -- map out + begin + dup list-empty? if + drop + exit + then + list-pop-front # map out postfix' tok + swap >r # map out tok (r: postfix') + >r swap r> # out map tok (r: postfix') + fn-translate-token # out map + swap # map out + r> # map out postfix' + continue + again +; +compile-only + + +: fn-translate-postfix # postfix params -- out + swap # params postfix + list-new # params postfix out + + # prologue: stash args on return stack (emit ">r") + swap >r # params out (r: postfix) + fn-emit-prologue # params out + r> swap # params postfix out + + # build param map (name -> index) + 2 pick fn-build-param-map # params postfix out params map + >r drop r> # params postfix out map + # reorder to: params map out postfix + swap >r swap r> swap # params map out postfix + + # translate tokens + fn-translate-postfix-loop # params map out + # drop map, emit epilogue + swap drop # params out + fn-emit-epilogue # out +; +compile-only + : fn-build-body fn-translate-postfix # words ; diff --git a/main.py b/main.py index 5e8da15..7f96056 100644 --- a/main.py +++ b/main.py @@ -476,6 +476,7 @@ class Parser: def _execute_immediate_word(self, word: Word) -> None: try: + print(f"[ct] invoking {word.name}") self.compile_time_vm.invoke(word) except ParseError: raise @@ -1066,6 +1067,7 @@ class CompileTimeVM: word = self.dictionary.lookup(name) if word is None: raise ParseError(f"unknown word '{name}' during compile-time execution") + print(f"[ct-call] {name} stack={self.stack}") self._call_word(word) def _execute_nodes(self, nodes: Sequence[ASTNode]) -> None: @@ -1741,17 +1743,6 @@ def _ct_nil(vm: CompileTimeVM) -> None: vm.push(None) -def _ct_puts(vm: CompileTimeVM) -> None: - value = vm.pop() - if isinstance(value, str): - print(value) - return - if isinstance(value, int): - print(value) - return - raise ParseError("puts expects string or integer at compile time") - - def _ct_nil_p(vm: CompileTimeVM) -> None: vm.push(1 if vm.pop() is None else 0) @@ -1796,6 +1787,27 @@ def _ct_list_pop_front(vm: CompileTimeVM) -> None: vm.push(value) +def _ct_list_peek_front(vm: CompileTimeVM) -> None: + lst = _ensure_list(vm.pop()) + if not lst: + raise ParseError("cannot peek from empty list") + vm.push(lst) + vm.push(lst[0]) + + +def _ct_list_push_front(vm: CompileTimeVM) -> None: + value = vm.pop() + lst = _ensure_list(vm.pop()) + lst.insert(0, value) + vm.push(lst) + + +def _ct_list_reverse(vm: CompileTimeVM) -> None: + lst = _ensure_list(vm.pop()) + lst.reverse() + vm.push(lst) + + def _ct_list_length(vm: CompileTimeVM) -> None: lst = vm.pop_list() vm.push(len(lst)) @@ -1886,8 +1898,11 @@ def _ct_map_has(vm: CompileTimeVM) -> None: def _ct_string_eq(vm: CompileTimeVM) -> None: - right = vm.pop_str() - left = vm.pop_str() + try: + right = vm.pop_str() + left = vm.pop_str() + except ParseError as exc: + raise ParseError(f"string= expects strings; stack={vm.stack!r}") from exc vm.push(1 if left == right else 0) @@ -1947,47 +1962,6 @@ def _ct_add_token_chars(vm: CompileTimeVM) -> None: vm.parser.reader.add_token_chars(chars) -def _ct_fn_param_index(vm: CompileTimeVM) -> None: - name = vm.pop_str() - params = _ensure_list(vm.pop()) - try: - idx = params.index(name) - vm.push(params) - vm.push(idx) - vm.push(1) - except ValueError: - vm.push(params) - vm.push(-1) - vm.push(0) - - -def _ct_fn_translate_postfix(vm: CompileTimeVM) -> None: - params = _ensure_list(vm.pop()) - postfix = _ensure_list(vm.pop()) - prologue: List[Any] = [">r"] * len(params) - translated: List[Any] = [] - for tok in postfix: - if isinstance(tok, int): - translated.append(tok) - continue - if isinstance(tok, str): - try: - num_value = int(tok, 0) - translated.append(num_value) - continue - except ValueError: - pass - if isinstance(tok, str) and tok in params: - idx = params.index(tok) - translated.append(idx) - translated.append("rpick") - else: - translated.append(tok) - epilogue: List[Any] = ["rdrop"] * len(params) - out: List[Any] = prologue + translated + epilogue - vm.push(out) - - def _ct_shunt(vm: CompileTimeVM) -> None: """Convert an infix token list (strings) to postfix using +,-,*,/,%.""" ops: List[str] = [] @@ -2026,8 +2000,6 @@ def _ct_int_to_string(vm: CompileTimeVM) -> None: vm.push(str(value)) - - def _ct_identifier_p(vm: CompileTimeVM) -> None: value = vm._resolve_handle(vm.pop()) if isinstance(value, Token): @@ -2161,6 +2133,9 @@ def _register_compile_time_primitives(dictionary: Dictionary) -> None: register("drop", _ct_drop) register("list-pop", _ct_list_pop, compile_only=True) register("list-pop-front", _ct_list_pop_front, compile_only=True) + register("list-peek-front", _ct_list_peek_front, compile_only=True) + register("list-push-front", _ct_list_push_front, compile_only=True) + register("list-reverse", _ct_list_reverse, compile_only=True) register("list-length", _ct_list_length, compile_only=True) register("list-empty?", _ct_list_empty, compile_only=True) register("list-get", _ct_list_get, compile_only=True) @@ -2179,8 +2154,6 @@ def _register_compile_time_primitives(dictionary: Dictionary) -> None: register("string-length", _ct_string_length, compile_only=True) register("string-append", _ct_string_append, compile_only=True) register("string>number", _ct_string_to_number, compile_only=True) - register("fn-param-index", _ct_fn_param_index, compile_only=True) - register("fn-translate-postfix", _ct_fn_translate_postfix, compile_only=True) register("int>string", _ct_int_to_string, compile_only=True) register("identifier?", _ct_identifier_p, compile_only=True) register("shunt", _ct_shunt, compile_only=True) diff --git a/t.sl b/t.sl new file mode 100644 index 0000000..4833ba2 --- /dev/null +++ b/t.sl @@ -0,0 +1,13 @@ +import stdlib.sl +import fn.sl + +fn foo(int a){ + return a + 1; +} + +: main + extend-syntax + foo(1) + puts + 0 +; \ No newline at end of file