commit
This commit is contained in:
BIN
__pycache__/main.cpython-314.pyc
Normal file
BIN
__pycache__/main.cpython-314.pyc
Normal file
Binary file not shown.
289
build/call_syntax_parens.asm
Normal file
289
build/call_syntax_parens.asm
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
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:
|
||||||
|
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__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__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 2
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 2
|
||||||
|
; push 40
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 40
|
||||||
|
call word__2b
|
||||||
|
call word_puts
|
||||||
|
; push 1
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 1
|
||||||
|
; push 2
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 2
|
||||||
|
call word_foo
|
||||||
|
call word_puts
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
ret
|
||||||
|
word_foo:
|
||||||
|
call word__3er
|
||||||
|
call word__3er
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
call word_rpick
|
||||||
|
; push 1
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 1
|
||||||
|
call word_rpick
|
||||||
|
call word__2b
|
||||||
|
call word_rdrop
|
||||||
|
call word_rdrop
|
||||||
|
ret
|
||||||
|
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:
|
||||||
BIN
build/call_syntax_parens.o
Normal file
BIN
build/call_syntax_parens.o
Normal file
Binary file not shown.
298
build/loops_and_cmp.asm
Normal file
298
build/loops_and_cmp.asm
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
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:
|
||||||
|
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__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__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
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
cmp rax, 0
|
||||||
|
jle L_for_end_1
|
||||||
|
sub r13, 8
|
||||||
|
mov [r13], rax
|
||||||
|
L_for_loop_0:
|
||||||
|
; push 1
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 1
|
||||||
|
call word__2b
|
||||||
|
mov rax, [r13]
|
||||||
|
dec rax
|
||||||
|
mov [r13], rax
|
||||||
|
jg L_for_loop_0
|
||||||
|
add r13, 8
|
||||||
|
L_for_end_1:
|
||||||
|
call word_puts
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
call word__3d_3d
|
||||||
|
call word_puts
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 4
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 4
|
||||||
|
call word__3d_3d
|
||||||
|
call word_puts
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
ret
|
||||||
|
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:
|
||||||
BIN
build/loops_and_cmp.o
Normal file
BIN
build/loops_and_cmp.o
Normal file
Binary file not shown.
289
build/main.asm
Normal file
289
build/main.asm
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
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:
|
||||||
|
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__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__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 2
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 2
|
||||||
|
; push 40
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 40
|
||||||
|
call word__2b
|
||||||
|
call word_puts
|
||||||
|
; push 1
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 1
|
||||||
|
; push 2
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 2
|
||||||
|
call word_foo
|
||||||
|
call word_puts
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
ret
|
||||||
|
word_foo:
|
||||||
|
call word__3er
|
||||||
|
call word__3er
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
call word_rpick
|
||||||
|
; push 1
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 1
|
||||||
|
call word_rpick
|
||||||
|
call word__2b
|
||||||
|
call word_rdrop
|
||||||
|
call word_rdrop
|
||||||
|
ret
|
||||||
|
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:
|
||||||
BIN
build/main.o
Normal file
BIN
build/main.o
Normal file
Binary file not shown.
262
build/override_dup_compile_time.asm
Normal file
262
build/override_dup_compile_time.asm
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
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:
|
||||||
|
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__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__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 6
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 6
|
||||||
|
call word_puts
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
ret
|
||||||
|
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:
|
||||||
BIN
build/override_dup_compile_time.o
Normal file
BIN
build/override_dup_compile_time.o
Normal file
Binary file not shown.
@@ -73,6 +73,11 @@ word_dup:
|
|||||||
word_drop:
|
word_drop:
|
||||||
add r12, 8
|
add r12, 8
|
||||||
ret
|
ret
|
||||||
|
word_over:
|
||||||
|
mov rax, [r12 + 8]
|
||||||
|
sub r12, 8
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
word_swap:
|
word_swap:
|
||||||
mov rax, [r12]
|
mov rax, [r12]
|
||||||
mov rbx, [r12 + 8]
|
mov rbx, [r12 + 8]
|
||||||
@@ -222,6 +227,13 @@ word_r_3e:
|
|||||||
word_rdrop:
|
word_rdrop:
|
||||||
add r13, 8
|
add r13, 8
|
||||||
ret
|
ret
|
||||||
|
word_pick:
|
||||||
|
mov rcx, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rax, [r12 + rcx * 8]
|
||||||
|
sub r12, 8
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
word_rpick:
|
word_rpick:
|
||||||
mov rcx, [r12]
|
mov rcx, [r12]
|
||||||
add r12, 8
|
add r12, 8
|
||||||
@@ -461,18 +473,18 @@ word_test_2dif:
|
|||||||
mov rax, [r12]
|
mov rax, [r12]
|
||||||
add r12, 8
|
add r12, 8
|
||||||
test rax, rax
|
test rax, rax
|
||||||
jz L_if_false_0
|
jz L_if_false_24
|
||||||
; push 111
|
; push 111
|
||||||
sub r12, 8
|
sub r12, 8
|
||||||
mov qword [r12], 111
|
mov qword [r12], 111
|
||||||
call word_puts
|
call word_puts
|
||||||
jmp L_if_end_1
|
jmp L_if_end_25
|
||||||
L_if_false_0:
|
L_if_false_24:
|
||||||
; push 222
|
; push 222
|
||||||
sub r12, 8
|
sub r12, 8
|
||||||
mov qword [r12], 222
|
mov qword [r12], 222
|
||||||
call word_puts
|
call word_puts
|
||||||
L_if_end_1:
|
L_if_end_25:
|
||||||
ret
|
ret
|
||||||
word_test_2delse_2dif:
|
word_test_2delse_2dif:
|
||||||
; push 2
|
; push 2
|
||||||
@@ -486,13 +498,13 @@ word_test_2delse_2dif:
|
|||||||
mov rax, [r12]
|
mov rax, [r12]
|
||||||
add r12, 8
|
add r12, 8
|
||||||
test rax, rax
|
test rax, rax
|
||||||
jz L_if_false_2
|
jz L_if_false_26
|
||||||
; push 50
|
; push 50
|
||||||
sub r12, 8
|
sub r12, 8
|
||||||
mov qword [r12], 50
|
mov qword [r12], 50
|
||||||
call word_puts
|
call word_puts
|
||||||
jmp L_if_end_3
|
jmp L_if_end_27
|
||||||
L_if_false_2:
|
L_if_false_26:
|
||||||
call word_dup
|
call word_dup
|
||||||
; push 2
|
; push 2
|
||||||
sub r12, 8
|
sub r12, 8
|
||||||
@@ -501,19 +513,19 @@ L_if_false_2:
|
|||||||
mov rax, [r12]
|
mov rax, [r12]
|
||||||
add r12, 8
|
add r12, 8
|
||||||
test rax, rax
|
test rax, rax
|
||||||
jz L_if_false_4
|
jz L_if_false_28
|
||||||
; push 60
|
; push 60
|
||||||
sub r12, 8
|
sub r12, 8
|
||||||
mov qword [r12], 60
|
mov qword [r12], 60
|
||||||
call word_puts
|
call word_puts
|
||||||
jmp L_if_end_5
|
jmp L_if_end_29
|
||||||
L_if_false_4:
|
L_if_false_28:
|
||||||
; push 70
|
; push 70
|
||||||
sub r12, 8
|
sub r12, 8
|
||||||
mov qword [r12], 70
|
mov qword [r12], 70
|
||||||
call word_puts
|
call word_puts
|
||||||
L_if_end_5:
|
L_if_end_29:
|
||||||
L_if_end_3:
|
L_if_end_27:
|
||||||
call word_drop
|
call word_drop
|
||||||
ret
|
ret
|
||||||
word_test_2dfor:
|
word_test_2dfor:
|
||||||
@@ -526,10 +538,10 @@ word_test_2dfor:
|
|||||||
mov rax, [r12]
|
mov rax, [r12]
|
||||||
add r12, 8
|
add r12, 8
|
||||||
cmp rax, 0
|
cmp rax, 0
|
||||||
jle L_for_end_7
|
jle L_for_end_31
|
||||||
sub r13, 8
|
sub r13, 8
|
||||||
mov [r13], rax
|
mov [r13], rax
|
||||||
L_for_loop_6:
|
L_for_loop_30:
|
||||||
; push 1
|
; push 1
|
||||||
sub r12, 8
|
sub r12, 8
|
||||||
mov qword [r12], 1
|
mov qword [r12], 1
|
||||||
@@ -537,9 +549,9 @@ L_for_loop_6:
|
|||||||
mov rax, [r13]
|
mov rax, [r13]
|
||||||
dec rax
|
dec rax
|
||||||
mov [r13], rax
|
mov [r13], rax
|
||||||
jg L_for_loop_6
|
jg L_for_loop_30
|
||||||
add r13, 8
|
add r13, 8
|
||||||
L_for_end_7:
|
L_for_end_31:
|
||||||
call word_puts
|
call word_puts
|
||||||
ret
|
ret
|
||||||
word_test_2dfor_2dzero:
|
word_test_2dfor_2dzero:
|
||||||
@@ -552,17 +564,17 @@ word_test_2dfor_2dzero:
|
|||||||
mov rax, [r12]
|
mov rax, [r12]
|
||||||
add r12, 8
|
add r12, 8
|
||||||
cmp rax, 0
|
cmp rax, 0
|
||||||
jle L_for_end_9
|
jle L_for_end_33
|
||||||
sub r13, 8
|
sub r13, 8
|
||||||
mov [r13], rax
|
mov [r13], rax
|
||||||
L_for_loop_8:
|
L_for_loop_32:
|
||||||
call word_drop
|
call word_drop
|
||||||
mov rax, [r13]
|
mov rax, [r13]
|
||||||
dec rax
|
dec rax
|
||||||
mov [r13], rax
|
mov [r13], rax
|
||||||
jg L_for_loop_8
|
jg L_for_loop_32
|
||||||
add r13, 8
|
add r13, 8
|
||||||
L_for_end_9:
|
L_for_end_33:
|
||||||
call word_puts
|
call word_puts
|
||||||
ret
|
ret
|
||||||
word_test_2dstruct:
|
word_test_2dstruct:
|
||||||
|
|||||||
BIN
build/test.o
BIN
build/test.o
Binary file not shown.
155
fn.sl
Normal file
155
fn.sl
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
: call-syntax-rewrite # ( fnameToken -- handled )
|
||||||
|
dup token-lexeme identifier? 0 == if drop 0 exit then
|
||||||
|
peek-token dup nil? if drop drop 0 exit then
|
||||||
|
dup token-lexeme "(" string= 0 == if drop drop 0 exit then
|
||||||
|
swap >r # stash fnameTok
|
||||||
|
drop # discard peeked '('
|
||||||
|
next-token drop # consume '('
|
||||||
|
list-new # out
|
||||||
|
list-new # out cur
|
||||||
|
begin
|
||||||
|
next-token dup nil? if "unterminated call expression" parse-error then
|
||||||
|
dup token-lexeme ")" string= if
|
||||||
|
drop
|
||||||
|
# flush current arg
|
||||||
|
list-extend # out'
|
||||||
|
r> list-append # out''
|
||||||
|
inject-tokens
|
||||||
|
1 exit
|
||||||
|
then
|
||||||
|
dup token-lexeme "," string= if
|
||||||
|
drop
|
||||||
|
list-extend # out'
|
||||||
|
list-new # out' cur
|
||||||
|
continue
|
||||||
|
then
|
||||||
|
# default: append tok to cur
|
||||||
|
list-append
|
||||||
|
again
|
||||||
|
;
|
||||||
|
immediate
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
: extend-syntax
|
||||||
|
"call-syntax-rewrite" set-token-hook
|
||||||
|
;
|
||||||
|
immediate
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
|
||||||
|
: fn-op-prec
|
||||||
|
dup "+" string= if drop 1 exit then
|
||||||
|
dup "-" string= if drop 1 exit then
|
||||||
|
dup "*" string= if drop 2 exit then
|
||||||
|
dup "/" string= if drop 2 exit then
|
||||||
|
dup "%" string= if drop 2 exit then
|
||||||
|
drop 0
|
||||||
|
;
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
: fn-operator?
|
||||||
|
fn-op-prec 0 >
|
||||||
|
;
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
: fn-check-dup
|
||||||
|
>r # params (r: name)
|
||||||
|
0 # params idx
|
||||||
|
begin
|
||||||
|
over list-length swap >= if # params flag
|
||||||
|
r> exit
|
||||||
|
then
|
||||||
|
dup >r # params idx (r: idx name)
|
||||||
|
over swap list-get # params elem
|
||||||
|
1 rpick string= if "duplicate parameter names in fn definition" parse-error then
|
||||||
|
drop # drop comparison flag when no error
|
||||||
|
r> 1 + # params idx+1
|
||||||
|
again
|
||||||
|
;
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
: fn-params
|
||||||
|
list-new # lexer params
|
||||||
|
swap # params lexer
|
||||||
|
>r # params (r: lexer)
|
||||||
|
begin
|
||||||
|
0 rpick lexer-pop token-lexeme # params lex
|
||||||
|
swap drop # params lex (drop returned lexer)
|
||||||
|
dup ")" string= if drop r> exit then
|
||||||
|
dup "int" string= 0 == if "only 'int' parameters are supported in fn definitions" parse-error then
|
||||||
|
drop # params
|
||||||
|
0 rpick lexer-pop token-lexeme # params lexer pname
|
||||||
|
swap drop # params pname
|
||||||
|
dup identifier? 0 == if "invalid parameter name in fn definition" parse-error then
|
||||||
|
fn-check-dup # params pname
|
||||||
|
list-append # params
|
||||||
|
0 rpick lexer-pop token-lexeme # params lexer sep
|
||||||
|
swap drop # params sep
|
||||||
|
dup "," string= if drop continue then
|
||||||
|
dup ")" string= if drop r> exit then
|
||||||
|
"expected ',' or ')' in parameter list" parse-error
|
||||||
|
again
|
||||||
|
;
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
: fn-collect-body
|
||||||
|
"{" lexer-expect drop # consume opening brace, keep lexer
|
||||||
|
lexer-collect-brace # lexer bodyTokens
|
||||||
|
swap drop # bodyTokens
|
||||||
|
;
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
: fn-lexemes-from-tokens
|
||||||
|
list-new >r # tokens (r: acc)
|
||||||
|
0 # tokens idx
|
||||||
|
begin
|
||||||
|
over list-length over swap >= if # stop when idx >= len
|
||||||
|
drop drop # drop idx and tokens (flag consumed by if)
|
||||||
|
r> exit # return acc
|
||||||
|
then
|
||||||
|
over over list-get token-lexeme # tokens idx lex
|
||||||
|
r> swap list-append >r # tokens idx
|
||||||
|
1 + # tokens idx+1
|
||||||
|
again
|
||||||
|
;
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
: fn-validate-body
|
||||||
|
dup list-length 0 == if "empty function body" parse-error then
|
||||||
|
dup >r 0 r> swap list-get "return" string= 0 == if "function body must start with 'return'" parse-error then
|
||||||
|
dup list-last ";" string= 0 == if "function body must terminate with ';'" parse-error then
|
||||||
|
list-clone # work on a copy
|
||||||
|
list-pop drop # drop trailing ';'
|
||||||
|
list-pop-front drop # drop leading 'return'
|
||||||
|
dup list-length 0 == if "missing return expression" parse-error then
|
||||||
|
;
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
: fn-build-body
|
||||||
|
fn-translate-postfix # words
|
||||||
|
;
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
: fn
|
||||||
|
"(),{};+-*/%," lexer-new # lexer
|
||||||
|
dup lexer-pop # lexer nameTok
|
||||||
|
dup >r # save nameTok
|
||||||
|
token-lexeme # lexer name
|
||||||
|
dup identifier? 0 == if "invalid function name for 'fn'" parse-error then
|
||||||
|
>r # save name string
|
||||||
|
drop # leave lexer only for params
|
||||||
|
"(" lexer-expect drop # consume '(' keep lexer
|
||||||
|
fn-params # params lexer
|
||||||
|
fn-collect-body # params bodyTokens
|
||||||
|
swap >r # bodyTokens (r: params)
|
||||||
|
fn-lexemes-from-tokens # lexemes
|
||||||
|
fn-validate-body # expr
|
||||||
|
shunt # postfix
|
||||||
|
r> # postfix params
|
||||||
|
fn-build-body # body
|
||||||
|
r> drop # drop name string
|
||||||
|
r> # name token
|
||||||
|
swap emit-definition
|
||||||
|
;
|
||||||
|
immediate
|
||||||
|
compile-only
|
||||||
398
main.py
398
main.py
@@ -50,6 +50,22 @@ class Reader:
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.line = 1
|
self.line = 1
|
||||||
self.column = 0
|
self.column = 0
|
||||||
|
self.custom_tokens: Set[str] = {"(", ")", "{", "}", ";", ",", "[", "]"}
|
||||||
|
self._token_order: List[str] = sorted(self.custom_tokens, key=len, reverse=True)
|
||||||
|
|
||||||
|
def add_tokens(self, tokens: Iterable[str]) -> None:
|
||||||
|
updated = False
|
||||||
|
for tok in tokens:
|
||||||
|
if not tok:
|
||||||
|
continue
|
||||||
|
if tok not in self.custom_tokens:
|
||||||
|
self.custom_tokens.add(tok)
|
||||||
|
updated = True
|
||||||
|
if updated:
|
||||||
|
self._token_order = sorted(self.custom_tokens, key=len, reverse=True)
|
||||||
|
|
||||||
|
def add_token_chars(self, chars: str) -> None:
|
||||||
|
self.add_tokens(chars)
|
||||||
|
|
||||||
def tokenize(self, source: str) -> Iterable[Token]:
|
def tokenize(self, source: str) -> Iterable[Token]:
|
||||||
self.line = 1
|
self.line = 1
|
||||||
@@ -62,19 +78,78 @@ class Reader:
|
|||||||
source_len = len(source)
|
source_len = len(source)
|
||||||
while index < source_len:
|
while index < source_len:
|
||||||
char = source[index]
|
char = source[index]
|
||||||
|
if char == '"':
|
||||||
|
if lexeme:
|
||||||
|
yield Token("".join(lexeme), token_line, token_column, token_start, index)
|
||||||
|
lexeme.clear()
|
||||||
|
token_start = index
|
||||||
|
token_line = self.line
|
||||||
|
token_column = self.column
|
||||||
|
index += 1
|
||||||
|
self.column += 1
|
||||||
|
string_parts = ['"']
|
||||||
|
while True:
|
||||||
|
if index >= source_len:
|
||||||
|
raise ParseError("unterminated string literal")
|
||||||
|
ch = source[index]
|
||||||
|
string_parts.append(ch)
|
||||||
|
index += 1
|
||||||
|
if ch == "\n":
|
||||||
|
self.line += 1
|
||||||
|
self.column = 0
|
||||||
|
else:
|
||||||
|
self.column += 1
|
||||||
|
if ch == "\\":
|
||||||
|
if index >= source_len:
|
||||||
|
raise ParseError("unterminated string literal")
|
||||||
|
next_ch = source[index]
|
||||||
|
string_parts.append(next_ch)
|
||||||
|
index += 1
|
||||||
|
if next_ch == "\n":
|
||||||
|
self.line += 1
|
||||||
|
self.column = 0
|
||||||
|
else:
|
||||||
|
self.column += 1
|
||||||
|
continue
|
||||||
|
if ch == '"':
|
||||||
|
yield Token("".join(string_parts), token_line, token_column, token_start, index)
|
||||||
|
break
|
||||||
|
continue
|
||||||
if char == "#":
|
if char == "#":
|
||||||
while index < source_len and source[index] != "\n":
|
while index < source_len and source[index] != "\n":
|
||||||
index += 1
|
index += 1
|
||||||
continue
|
continue
|
||||||
|
if char == ";" and index + 1 < source_len and source[index + 1].isalpha():
|
||||||
|
if not lexeme:
|
||||||
|
token_start = index
|
||||||
|
token_line = self.line
|
||||||
|
token_column = self.column
|
||||||
|
lexeme.append(";")
|
||||||
|
index += 1
|
||||||
|
self.column += 1
|
||||||
|
continue
|
||||||
|
matched_token: Optional[str] = None
|
||||||
|
for tok in self._token_order:
|
||||||
|
if source.startswith(tok, index):
|
||||||
|
matched_token = tok
|
||||||
|
break
|
||||||
|
if matched_token is not None:
|
||||||
|
if lexeme:
|
||||||
|
yield Token("".join(lexeme), token_line, token_column, token_start, index)
|
||||||
|
lexeme.clear()
|
||||||
|
token_start = index
|
||||||
|
token_line = self.line
|
||||||
|
token_column = self.column
|
||||||
|
yield Token(matched_token, self.line, self.column, index, index + len(matched_token))
|
||||||
|
index += len(matched_token)
|
||||||
|
self.column += len(matched_token)
|
||||||
|
token_start = index
|
||||||
|
token_line = self.line
|
||||||
|
token_column = self.column
|
||||||
|
continue
|
||||||
if char.isspace():
|
if char.isspace():
|
||||||
if lexeme:
|
if lexeme:
|
||||||
yield Token(
|
yield Token("".join(lexeme), token_line, token_column, token_start, index)
|
||||||
"".join(lexeme),
|
|
||||||
token_line,
|
|
||||||
token_column,
|
|
||||||
token_start,
|
|
||||||
index,
|
|
||||||
)
|
|
||||||
lexeme.clear()
|
lexeme.clear()
|
||||||
if char == "\n":
|
if char == "\n":
|
||||||
self.line += 1
|
self.line += 1
|
||||||
@@ -82,6 +157,9 @@ class Reader:
|
|||||||
else:
|
else:
|
||||||
self.column += 1
|
self.column += 1
|
||||||
index += 1
|
index += 1
|
||||||
|
token_start = index
|
||||||
|
token_line = self.line
|
||||||
|
token_column = self.column
|
||||||
continue
|
continue
|
||||||
if not lexeme:
|
if not lexeme:
|
||||||
token_start = index
|
token_start = index
|
||||||
@@ -91,7 +169,7 @@ class Reader:
|
|||||||
self.column += 1
|
self.column += 1
|
||||||
index += 1
|
index += 1
|
||||||
if lexeme:
|
if lexeme:
|
||||||
yield Token("".join(lexeme), token_line, token_column, token_start, index)
|
yield Token("".join(lexeme), token_line, token_column, token_start, source_len)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -218,11 +296,8 @@ class MacroContext:
|
|||||||
def inject_token_objects(self, tokens: Sequence[Token]) -> None:
|
def inject_token_objects(self, tokens: Sequence[Token]) -> None:
|
||||||
self._parser.tokens[self._parser.pos:self._parser.pos] = list(tokens)
|
self._parser.tokens[self._parser.pos:self._parser.pos] = list(tokens)
|
||||||
|
|
||||||
def enable_call_syntax(self) -> None:
|
def set_token_hook(self, handler: Optional[str]) -> None:
|
||||||
self._parser.call_syntax_enabled = True
|
self._parser.token_hook = handler
|
||||||
|
|
||||||
def disable_call_syntax(self) -> None:
|
|
||||||
self._parser.call_syntax_enabled = False
|
|
||||||
|
|
||||||
def new_label(self, prefix: str) -> str:
|
def new_label(self, prefix: str) -> str:
|
||||||
return self._parser._new_label(prefix)
|
return self._parser._new_label(prefix)
|
||||||
@@ -247,6 +322,7 @@ class Word:
|
|||||||
macro_params: int = 0
|
macro_params: int = 0
|
||||||
compile_time_intrinsic: Optional[Callable[["CompileTimeVM"], None]] = None
|
compile_time_intrinsic: Optional[Callable[["CompileTimeVM"], None]] = None
|
||||||
compile_only: bool = False
|
compile_only: bool = False
|
||||||
|
compile_time_override: bool = False
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -271,9 +347,12 @@ Context = Union[Module, Definition]
|
|||||||
|
|
||||||
|
|
||||||
class Parser:
|
class Parser:
|
||||||
def __init__(self, dictionary: Dictionary) -> None:
|
def __init__(self, dictionary: Dictionary, reader: Optional[Reader] = None) -> None:
|
||||||
self.dictionary = dictionary
|
self.dictionary = dictionary
|
||||||
|
self.reader = reader or Reader()
|
||||||
self.tokens: List[Token] = []
|
self.tokens: List[Token] = []
|
||||||
|
self._token_iter: Optional[Iterable[Token]] = None
|
||||||
|
self._token_iter_exhausted = True
|
||||||
self.pos = 0
|
self.pos = 0
|
||||||
self.context_stack: List[Context] = []
|
self.context_stack: List[Context] = []
|
||||||
self.definition_stack: List[Word] = []
|
self.definition_stack: List[Word] = []
|
||||||
@@ -282,14 +361,20 @@ class Parser:
|
|||||||
self.macro_recording: Optional[MacroDefinition] = None
|
self.macro_recording: Optional[MacroDefinition] = None
|
||||||
self.control_stack: List[Dict[str, str]] = []
|
self.control_stack: List[Dict[str, str]] = []
|
||||||
self.label_counter = 0
|
self.label_counter = 0
|
||||||
self.call_syntax_enabled = False
|
self.token_hook: Optional[str] = None
|
||||||
|
self._last_token: Optional[Token] = None
|
||||||
self.compile_time_vm = CompileTimeVM(self)
|
self.compile_time_vm = CompileTimeVM(self)
|
||||||
|
|
||||||
|
def inject_token_objects(self, tokens: Sequence[Token]) -> None:
|
||||||
|
"""Insert tokens at the current parse position."""
|
||||||
|
self.tokens[self.pos:self.pos] = list(tokens)
|
||||||
|
|
||||||
# Public helpers for macros ------------------------------------------------
|
# Public helpers for macros ------------------------------------------------
|
||||||
def next_token(self) -> Token:
|
def next_token(self) -> Token:
|
||||||
return self._consume()
|
return self._consume()
|
||||||
|
|
||||||
def peek_token(self) -> Optional[Token]:
|
def peek_token(self) -> Optional[Token]:
|
||||||
|
self._ensure_tokens(self.pos)
|
||||||
return None if self._eof() else self.tokens[self.pos]
|
return None if self._eof() else self.tokens[self.pos]
|
||||||
|
|
||||||
def emit_node(self, node: ASTNode) -> None:
|
def emit_node(self, node: ASTNode) -> None:
|
||||||
@@ -300,7 +385,9 @@ class Parser:
|
|||||||
|
|
||||||
# Parsing ------------------------------------------------------------------
|
# Parsing ------------------------------------------------------------------
|
||||||
def parse(self, tokens: Iterable[Token], source: str) -> Module:
|
def parse(self, tokens: Iterable[Token], source: str) -> Module:
|
||||||
self.tokens = list(tokens)
|
self.tokens = []
|
||||||
|
self._token_iter = iter(tokens)
|
||||||
|
self._token_iter_exhausted = False
|
||||||
self.source = source
|
self.source = source
|
||||||
self.pos = 0
|
self.pos = 0
|
||||||
self.context_stack = [Module(forms=[])]
|
self.context_stack = [Module(forms=[])]
|
||||||
@@ -308,10 +395,14 @@ class Parser:
|
|||||||
self.last_defined = None
|
self.last_defined = None
|
||||||
self.control_stack = []
|
self.control_stack = []
|
||||||
self.label_counter = 0
|
self.label_counter = 0
|
||||||
self.call_syntax_enabled = False
|
self.token_hook = None
|
||||||
|
self._last_token = None
|
||||||
|
|
||||||
while not self._eof():
|
while not self._eof():
|
||||||
token = self._consume()
|
token = self._consume()
|
||||||
|
self._last_token = token
|
||||||
|
if self._run_token_hook(token):
|
||||||
|
continue
|
||||||
if self._handle_macro_recording(token):
|
if self._handle_macro_recording(token):
|
||||||
continue
|
continue
|
||||||
lexeme = token.lexeme
|
lexeme = token.lexeme
|
||||||
@@ -358,11 +449,6 @@ class Parser:
|
|||||||
|
|
||||||
# Internal helpers ---------------------------------------------------------
|
# Internal helpers ---------------------------------------------------------
|
||||||
def _handle_token(self, token: Token) -> None:
|
def _handle_token(self, token: Token) -> None:
|
||||||
if self.call_syntax_enabled:
|
|
||||||
call_target = self._maybe_call_form(token.lexeme)
|
|
||||||
if call_target is not None:
|
|
||||||
self._append_node(WordRef(name=call_target))
|
|
||||||
return
|
|
||||||
if self._try_literal(token):
|
if self._try_literal(token):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -444,6 +530,12 @@ class Parser:
|
|||||||
self.dictionary.register(word)
|
self.dictionary.register(word)
|
||||||
|
|
||||||
def _push_control(self, entry: Dict[str, str]) -> None:
|
def _push_control(self, entry: Dict[str, str]) -> None:
|
||||||
|
if "line" not in entry or "column" not in entry:
|
||||||
|
tok = self._last_token
|
||||||
|
if tok is not None:
|
||||||
|
entry = dict(entry)
|
||||||
|
entry["line"] = tok.line
|
||||||
|
entry["column"] = tok.column
|
||||||
self.control_stack.append(entry)
|
self.control_stack.append(entry)
|
||||||
|
|
||||||
def _pop_control(self, expected: Tuple[str, ...]) -> Dict[str, str]:
|
def _pop_control(self, expected: Tuple[str, ...]) -> Dict[str, str]:
|
||||||
@@ -451,7 +543,14 @@ class Parser:
|
|||||||
raise ParseError("control stack underflow")
|
raise ParseError("control stack underflow")
|
||||||
entry = self.control_stack.pop()
|
entry = self.control_stack.pop()
|
||||||
if entry.get("type") not in expected:
|
if entry.get("type") not in expected:
|
||||||
raise ParseError(f"mismatched control word '{entry.get('type')}'")
|
tok = self._last_token
|
||||||
|
location = ""
|
||||||
|
if tok is not None:
|
||||||
|
location = f" at {tok.line}:{tok.column} near '{tok.lexeme}'"
|
||||||
|
origin = ""
|
||||||
|
if "line" in entry and "column" in entry:
|
||||||
|
origin = f" (opened at {entry['line']}:{entry['column']})"
|
||||||
|
raise ParseError(f"mismatched control word '{entry.get('type')}'" + origin + location)
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
def _new_label(self, prefix: str) -> str:
|
def _new_label(self, prefix: str) -> str:
|
||||||
@@ -459,13 +558,16 @@ class Parser:
|
|||||||
self.label_counter += 1
|
self.label_counter += 1
|
||||||
return label
|
return label
|
||||||
|
|
||||||
def _maybe_call_form(self, lexeme: str) -> Optional[str]:
|
def _run_token_hook(self, token: Token) -> bool:
|
||||||
if len(lexeme) <= 2 or not lexeme.endswith("()"):
|
if not self.token_hook:
|
||||||
return None
|
return False
|
||||||
name = lexeme[:-2]
|
hook_word = self.dictionary.lookup(self.token_hook)
|
||||||
if not name or not _is_identifier(name):
|
if hook_word is None:
|
||||||
return None
|
raise ParseError(f"token hook '{self.token_hook}' not defined")
|
||||||
return name
|
self.compile_time_vm.invoke_with_args(hook_word, [token])
|
||||||
|
# Convention: hook leaves handled flag on stack (int truthy means consumed)
|
||||||
|
handled = self.compile_time_vm.pop()
|
||||||
|
return bool(handled)
|
||||||
|
|
||||||
def _handle_if_control(self) -> None:
|
def _handle_if_control(self) -> None:
|
||||||
false_label = self._new_label("if_false")
|
false_label = self._new_label("if_false")
|
||||||
@@ -518,6 +620,9 @@ class Parser:
|
|||||||
word = self.definition_stack.pop()
|
word = self.definition_stack.pop()
|
||||||
ctx.immediate = word.immediate
|
ctx.immediate = word.immediate
|
||||||
ctx.compile_only = word.compile_only
|
ctx.compile_only = word.compile_only
|
||||||
|
if word.compile_only or word.immediate:
|
||||||
|
word.compile_time_override = True
|
||||||
|
word.compile_time_intrinsic = None
|
||||||
module = self.context_stack[-1]
|
module = self.context_stack[-1]
|
||||||
if not isinstance(module, Module):
|
if not isinstance(module, Module):
|
||||||
raise ParseError("nested definitions are not supported yet")
|
raise ParseError("nested definitions are not supported yet")
|
||||||
@@ -626,6 +731,7 @@ class Parser:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _consume(self) -> Token:
|
def _consume(self) -> Token:
|
||||||
|
self._ensure_tokens(self.pos)
|
||||||
if self._eof():
|
if self._eof():
|
||||||
raise ParseError("unexpected EOF")
|
raise ParseError("unexpected EOF")
|
||||||
token = self.tokens[self.pos]
|
token = self.tokens[self.pos]
|
||||||
@@ -633,8 +739,23 @@ class Parser:
|
|||||||
return token
|
return token
|
||||||
|
|
||||||
def _eof(self) -> bool:
|
def _eof(self) -> bool:
|
||||||
|
self._ensure_tokens(self.pos)
|
||||||
return self.pos >= len(self.tokens)
|
return self.pos >= len(self.tokens)
|
||||||
|
|
||||||
|
def _ensure_tokens(self, upto: int) -> None:
|
||||||
|
if self._token_iter_exhausted:
|
||||||
|
return
|
||||||
|
if self._token_iter is None:
|
||||||
|
self._token_iter_exhausted = True
|
||||||
|
return
|
||||||
|
while len(self.tokens) <= upto and not self._token_iter_exhausted:
|
||||||
|
try:
|
||||||
|
next_tok = next(self._token_iter)
|
||||||
|
except StopIteration:
|
||||||
|
self._token_iter_exhausted = True
|
||||||
|
break
|
||||||
|
self.tokens.append(next_tok)
|
||||||
|
|
||||||
|
|
||||||
class CompileTimeVM:
|
class CompileTimeVM:
|
||||||
def __init__(self, parser: Parser) -> None:
|
def __init__(self, parser: Parser) -> None:
|
||||||
@@ -642,10 +763,12 @@ class CompileTimeVM:
|
|||||||
self.dictionary = parser.dictionary
|
self.dictionary = parser.dictionary
|
||||||
self.stack: List[Any] = []
|
self.stack: List[Any] = []
|
||||||
self.return_stack: List[Any] = []
|
self.return_stack: List[Any] = []
|
||||||
|
self.loop_stack: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
self.stack.clear()
|
self.stack.clear()
|
||||||
self.return_stack.clear()
|
self.return_stack.clear()
|
||||||
|
self.loop_stack.clear()
|
||||||
|
|
||||||
def push(self, value: Any) -> None:
|
def push(self, value: Any) -> None:
|
||||||
self.stack.append(value)
|
self.stack.append(value)
|
||||||
@@ -688,11 +811,18 @@ class CompileTimeVM:
|
|||||||
self.reset()
|
self.reset()
|
||||||
self._call_word(word)
|
self._call_word(word)
|
||||||
|
|
||||||
|
def invoke_with_args(self, word: Word, args: Sequence[Any]) -> None:
|
||||||
|
self.reset()
|
||||||
|
for value in args:
|
||||||
|
self.push(value)
|
||||||
|
self._call_word(word)
|
||||||
|
|
||||||
def _call_word(self, word: Word) -> None:
|
def _call_word(self, word: Word) -> None:
|
||||||
if word.compile_time_intrinsic is not None:
|
definition = word.definition
|
||||||
|
prefer_definition = word.compile_time_override or (isinstance(definition, Definition) and (word.immediate or word.compile_only))
|
||||||
|
if not prefer_definition and word.compile_time_intrinsic is not None:
|
||||||
word.compile_time_intrinsic(self)
|
word.compile_time_intrinsic(self)
|
||||||
return
|
return
|
||||||
definition = word.definition
|
|
||||||
if definition is None:
|
if definition is None:
|
||||||
raise ParseError(f"word '{word.name}' has no compile-time definition")
|
raise ParseError(f"word '{word.name}' has no compile-time definition")
|
||||||
if isinstance(definition, AsmDefinition):
|
if isinstance(definition, AsmDefinition):
|
||||||
@@ -708,7 +838,9 @@ class CompileTimeVM:
|
|||||||
def _execute_nodes(self, nodes: Sequence[ASTNode]) -> None:
|
def _execute_nodes(self, nodes: Sequence[ASTNode]) -> None:
|
||||||
label_positions = self._label_positions(nodes)
|
label_positions = self._label_positions(nodes)
|
||||||
loop_pairs = self._for_pairs(nodes)
|
loop_pairs = self._for_pairs(nodes)
|
||||||
loop_stack: List[Dict[str, Any]] = []
|
begin_pairs = self._begin_pairs(nodes)
|
||||||
|
self.loop_stack = []
|
||||||
|
begin_stack: List[Dict[str, int]] = []
|
||||||
ip = 0
|
ip = 0
|
||||||
while ip < len(nodes):
|
while ip < len(nodes):
|
||||||
node = nodes[ip]
|
node = nodes[ip]
|
||||||
@@ -717,7 +849,31 @@ class CompileTimeVM:
|
|||||||
ip += 1
|
ip += 1
|
||||||
continue
|
continue
|
||||||
if isinstance(node, WordRef):
|
if isinstance(node, WordRef):
|
||||||
self._call_word_by_name(node.name)
|
name = node.name
|
||||||
|
if name == "begin":
|
||||||
|
end_idx = begin_pairs.get(ip)
|
||||||
|
if end_idx is None:
|
||||||
|
raise ParseError("'begin' without matching 'again'")
|
||||||
|
begin_stack.append({"begin": ip, "end": end_idx})
|
||||||
|
ip += 1
|
||||||
|
continue
|
||||||
|
if name == "again":
|
||||||
|
if not begin_stack or begin_stack[-1]["end"] != ip:
|
||||||
|
raise ParseError("'again' without matching 'begin'")
|
||||||
|
ip = begin_stack[-1]["begin"] + 1
|
||||||
|
continue
|
||||||
|
if name == "continue":
|
||||||
|
if not begin_stack:
|
||||||
|
raise ParseError("'continue' outside begin/again loop")
|
||||||
|
ip = begin_stack[-1]["begin"] + 1
|
||||||
|
continue
|
||||||
|
if name == "exit":
|
||||||
|
if begin_stack:
|
||||||
|
frame = begin_stack.pop()
|
||||||
|
ip = frame["end"] + 1
|
||||||
|
continue
|
||||||
|
return
|
||||||
|
self._call_word_by_name(name)
|
||||||
ip += 1
|
ip += 1
|
||||||
continue
|
continue
|
||||||
if isinstance(node, BranchZero):
|
if isinstance(node, BranchZero):
|
||||||
@@ -748,18 +904,18 @@ class CompileTimeVM:
|
|||||||
raise ParseError("internal loop bookkeeping error")
|
raise ParseError("internal loop bookkeeping error")
|
||||||
ip = match + 1
|
ip = match + 1
|
||||||
continue
|
continue
|
||||||
loop_stack.append({"remaining": count, "begin": ip})
|
self.loop_stack.append({"remaining": count, "begin": ip, "initial": count})
|
||||||
ip += 1
|
ip += 1
|
||||||
continue
|
continue
|
||||||
if isinstance(node, ForNext):
|
if isinstance(node, ForNext):
|
||||||
if not loop_stack:
|
if not self.loop_stack:
|
||||||
raise ParseError("'next' without matching 'for'")
|
raise ParseError("'next' without matching 'for'")
|
||||||
frame = loop_stack[-1]
|
frame = self.loop_stack[-1]
|
||||||
frame["remaining"] -= 1
|
frame["remaining"] -= 1
|
||||||
if frame["remaining"] > 0:
|
if frame["remaining"] > 0:
|
||||||
ip = frame["begin"] + 1
|
ip = frame["begin"] + 1
|
||||||
continue
|
continue
|
||||||
loop_stack.pop()
|
self.loop_stack.pop()
|
||||||
ip += 1
|
ip += 1
|
||||||
continue
|
continue
|
||||||
raise ParseError(f"unsupported compile-time AST node {node!r}")
|
raise ParseError(f"unsupported compile-time AST node {node!r}")
|
||||||
@@ -787,6 +943,22 @@ class CompileTimeVM:
|
|||||||
raise ParseError("'for' without matching 'next'")
|
raise ParseError("'for' without matching 'next'")
|
||||||
return pairs
|
return pairs
|
||||||
|
|
||||||
|
def _begin_pairs(self, nodes: Sequence[ASTNode]) -> Dict[int, int]:
|
||||||
|
stack: List[int] = []
|
||||||
|
pairs: Dict[int, int] = {}
|
||||||
|
for idx, node in enumerate(nodes):
|
||||||
|
if isinstance(node, WordRef) and node.name == "begin":
|
||||||
|
stack.append(idx)
|
||||||
|
elif isinstance(node, WordRef) and node.name == "again":
|
||||||
|
if not stack:
|
||||||
|
raise ParseError("'again' without matching 'begin'")
|
||||||
|
begin_idx = stack.pop()
|
||||||
|
pairs[begin_idx] = idx
|
||||||
|
pairs[idx] = begin_idx
|
||||||
|
if stack:
|
||||||
|
raise ParseError("'begin' without matching 'again'")
|
||||||
|
return pairs
|
||||||
|
|
||||||
def _jump_to_label(self, labels: Dict[str, int], target: str) -> int:
|
def _jump_to_label(self, labels: Dict[str, int], target: str) -> int:
|
||||||
if target not in labels:
|
if target not in labels:
|
||||||
raise ParseError(f"unknown label '{target}' during compile-time execution")
|
raise ParseError(f"unknown label '{target}' during compile-time execution")
|
||||||
@@ -1457,6 +1629,13 @@ def _ct_rpick(vm: CompileTimeVM) -> None:
|
|||||||
vm.push(vm.return_stack[-1 - index])
|
vm.push(vm.return_stack[-1 - index])
|
||||||
|
|
||||||
|
|
||||||
|
def _ct_pick(vm: CompileTimeVM) -> None:
|
||||||
|
index = vm.pop_int()
|
||||||
|
if index < 0 or index >= len(vm.stack):
|
||||||
|
raise ParseError("pick index out of range")
|
||||||
|
vm.push(vm.stack[-1 - index])
|
||||||
|
|
||||||
|
|
||||||
def _ct_nil(vm: CompileTimeVM) -> None:
|
def _ct_nil(vm: CompileTimeVM) -> None:
|
||||||
vm.push(None)
|
vm.push(None)
|
||||||
|
|
||||||
@@ -1509,6 +1688,14 @@ def _ct_list_empty(vm: CompileTimeVM) -> None:
|
|||||||
vm.push(1 if not lst else 0)
|
vm.push(1 if not lst else 0)
|
||||||
|
|
||||||
|
|
||||||
|
def _ct_loop_index(vm: CompileTimeVM) -> None:
|
||||||
|
if not vm.loop_stack:
|
||||||
|
raise ParseError("'i' used outside of a for loop")
|
||||||
|
frame = vm.loop_stack[-1]
|
||||||
|
idx = frame["initial"] - frame["remaining"]
|
||||||
|
vm.push(idx)
|
||||||
|
|
||||||
|
|
||||||
def _ct_list_get(vm: CompileTimeVM) -> None:
|
def _ct_list_get(vm: CompileTimeVM) -> None:
|
||||||
index = vm.pop_int()
|
index = vm.pop_int()
|
||||||
lst = _ensure_list(vm.pop())
|
lst = _ensure_list(vm.pop())
|
||||||
@@ -1608,11 +1795,121 @@ def _ct_string_to_number(vm: CompileTimeVM) -> None:
|
|||||||
vm.push(0)
|
vm.push(0)
|
||||||
|
|
||||||
|
|
||||||
|
def _ct_set_token_hook(vm: CompileTimeVM) -> None:
|
||||||
|
hook_name = vm.pop_str()
|
||||||
|
vm.parser.token_hook = hook_name
|
||||||
|
|
||||||
|
|
||||||
|
def _ct_clear_token_hook(vm: CompileTimeVM) -> None:
|
||||||
|
vm.parser.token_hook = None
|
||||||
|
|
||||||
|
|
||||||
|
def _ct_use_l2_compile_time(vm: CompileTimeVM) -> None:
|
||||||
|
if vm.stack:
|
||||||
|
name = vm.pop_str()
|
||||||
|
word = vm.dictionary.lookup(name)
|
||||||
|
else:
|
||||||
|
word = vm.parser.most_recent_definition()
|
||||||
|
if word is None:
|
||||||
|
raise ParseError("use-l2-ct with empty stack and no recent definition")
|
||||||
|
name = word.name
|
||||||
|
if word is None:
|
||||||
|
raise ParseError(f"unknown word '{name}' for use-l2-ct")
|
||||||
|
word.compile_time_intrinsic = None
|
||||||
|
word.compile_time_override = True
|
||||||
|
|
||||||
|
|
||||||
|
def _ct_add_token(vm: CompileTimeVM) -> None:
|
||||||
|
tok = vm.pop_str()
|
||||||
|
vm.parser.reader.add_tokens([tok])
|
||||||
|
|
||||||
|
|
||||||
|
def _ct_add_token_chars(vm: CompileTimeVM) -> None:
|
||||||
|
chars = vm.pop_str()
|
||||||
|
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] = []
|
||||||
|
output: List[str] = []
|
||||||
|
prec = {"+": 1, "-": 1, "*": 2, "/": 2, "%": 2}
|
||||||
|
tokens = _ensure_list(vm.pop())
|
||||||
|
for tok in tokens:
|
||||||
|
if not isinstance(tok, str):
|
||||||
|
raise ParseError("shunt expects list of strings")
|
||||||
|
if tok == "(":
|
||||||
|
ops.append(tok)
|
||||||
|
continue
|
||||||
|
if tok == ")":
|
||||||
|
while ops and ops[-1] != "(":
|
||||||
|
output.append(ops.pop())
|
||||||
|
if not ops:
|
||||||
|
raise ParseError("mismatched parentheses in expression")
|
||||||
|
ops.pop()
|
||||||
|
continue
|
||||||
|
if tok in prec:
|
||||||
|
while ops and ops[-1] in prec and prec[ops[-1]] >= prec[tok]:
|
||||||
|
output.append(ops.pop())
|
||||||
|
ops.append(tok)
|
||||||
|
continue
|
||||||
|
output.append(tok)
|
||||||
|
while ops:
|
||||||
|
top = ops.pop()
|
||||||
|
if top == "(":
|
||||||
|
raise ParseError("mismatched parentheses in expression")
|
||||||
|
output.append(top)
|
||||||
|
vm.push(output)
|
||||||
|
|
||||||
|
|
||||||
def _ct_int_to_string(vm: CompileTimeVM) -> None:
|
def _ct_int_to_string(vm: CompileTimeVM) -> None:
|
||||||
value = vm.pop_int()
|
value = vm.pop_int()
|
||||||
vm.push(str(value))
|
vm.push(str(value))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _ct_identifier_p(vm: CompileTimeVM) -> None:
|
def _ct_identifier_p(vm: CompileTimeVM) -> None:
|
||||||
value = vm.pop_str()
|
value = vm.pop_str()
|
||||||
vm.push(1 if _is_identifier(value) else 0)
|
vm.push(1 if _is_identifier(value) else 0)
|
||||||
@@ -1677,12 +1974,6 @@ def _ct_parse_error(vm: CompileTimeVM) -> None:
|
|||||||
raise ParseError(message)
|
raise ParseError(message)
|
||||||
|
|
||||||
|
|
||||||
def _ct_enable_call_syntax(vm: CompileTimeVM) -> None:
|
|
||||||
vm.parser.call_syntax_enabled = True
|
|
||||||
|
|
||||||
|
|
||||||
def _ct_disable_call_syntax(vm: CompileTimeVM) -> None:
|
|
||||||
vm.parser.call_syntax_enabled = False
|
|
||||||
|
|
||||||
|
|
||||||
def _ct_lexer_new(vm: CompileTimeVM) -> None:
|
def _ct_lexer_new(vm: CompileTimeVM) -> None:
|
||||||
@@ -1763,6 +2054,7 @@ def _register_compile_time_primitives(dictionary: Dictionary) -> None:
|
|||||||
register("r>", _ct_r_from)
|
register("r>", _ct_r_from)
|
||||||
register("rdrop", _ct_rdrop)
|
register("rdrop", _ct_rdrop)
|
||||||
register("rpick", _ct_rpick)
|
register("rpick", _ct_rpick)
|
||||||
|
register("pick", _ct_pick)
|
||||||
|
|
||||||
register("nil", _ct_nil, compile_only=True)
|
register("nil", _ct_nil, compile_only=True)
|
||||||
register("nil?", _ct_nil_p, compile_only=True)
|
register("nil?", _ct_nil_p, compile_only=True)
|
||||||
@@ -1778,6 +2070,7 @@ def _register_compile_time_primitives(dictionary: Dictionary) -> None:
|
|||||||
register("list-clear", _ct_list_clear, compile_only=True)
|
register("list-clear", _ct_list_clear, compile_only=True)
|
||||||
register("list-extend", _ct_list_extend, compile_only=True)
|
register("list-extend", _ct_list_extend, compile_only=True)
|
||||||
register("list-last", _ct_list_last, compile_only=True)
|
register("list-last", _ct_list_last, compile_only=True)
|
||||||
|
register("i", _ct_loop_index, compile_only=True)
|
||||||
|
|
||||||
register("map-new", _ct_map_new, compile_only=True)
|
register("map-new", _ct_map_new, compile_only=True)
|
||||||
register("map-set", _ct_map_set, compile_only=True)
|
register("map-set", _ct_map_set, compile_only=True)
|
||||||
@@ -1788,18 +2081,27 @@ def _register_compile_time_primitives(dictionary: Dictionary) -> None:
|
|||||||
register("string-length", _ct_string_length, compile_only=True)
|
register("string-length", _ct_string_length, compile_only=True)
|
||||||
register("string-append", _ct_string_append, compile_only=True)
|
register("string-append", _ct_string_append, compile_only=True)
|
||||||
register("string>number", _ct_string_to_number, 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("int>string", _ct_int_to_string, compile_only=True)
|
||||||
register("identifier?", _ct_identifier_p, compile_only=True)
|
register("identifier?", _ct_identifier_p, compile_only=True)
|
||||||
|
register("shunt", _ct_shunt, compile_only=True)
|
||||||
|
|
||||||
register("token-lexeme", _ct_token_lexeme, compile_only=True)
|
register("token-lexeme", _ct_token_lexeme, compile_only=True)
|
||||||
register("token-from-lexeme", _ct_token_from_lexeme, compile_only=True)
|
register("token-from-lexeme", _ct_token_from_lexeme, compile_only=True)
|
||||||
register("next-token", _ct_next_token, compile_only=True)
|
register("next-token", _ct_next_token, compile_only=True)
|
||||||
register("peek-token", _ct_peek_token, compile_only=True)
|
register("peek-token", _ct_peek_token, compile_only=True)
|
||||||
register("inject-tokens", _ct_inject_tokens, compile_only=True)
|
register("inject-tokens", _ct_inject_tokens, compile_only=True)
|
||||||
|
register("add-token", _ct_add_token, compile_only=True)
|
||||||
|
register("add-token-chars", _ct_add_token_chars, compile_only=True)
|
||||||
|
register("set-token-hook", _ct_set_token_hook, compile_only=True)
|
||||||
|
register("clear-token-hook", _ct_clear_token_hook, compile_only=True)
|
||||||
|
register("use-l2-ct", _ct_use_l2_compile_time, compile_only=True)
|
||||||
|
word_use_l2 = dictionary.lookup("use-l2-ct")
|
||||||
|
if word_use_l2:
|
||||||
|
word_use_l2.immediate = True
|
||||||
register("emit-definition", _ct_emit_definition, compile_only=True)
|
register("emit-definition", _ct_emit_definition, compile_only=True)
|
||||||
register("parse-error", _ct_parse_error, compile_only=True)
|
register("parse-error", _ct_parse_error, compile_only=True)
|
||||||
register("enable-call-syntax", _ct_enable_call_syntax, compile_only=True)
|
|
||||||
register("disable-call-syntax", _ct_disable_call_syntax, compile_only=True)
|
|
||||||
|
|
||||||
register("lexer-new", _ct_lexer_new, compile_only=True)
|
register("lexer-new", _ct_lexer_new, compile_only=True)
|
||||||
register("lexer-pop", _ct_lexer_pop, compile_only=True)
|
register("lexer-pop", _ct_lexer_pop, compile_only=True)
|
||||||
@@ -1910,11 +2212,11 @@ class Compiler:
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.reader = Reader()
|
self.reader = Reader()
|
||||||
self.dictionary = bootstrap_dictionary()
|
self.dictionary = bootstrap_dictionary()
|
||||||
self.parser = Parser(self.dictionary)
|
self.parser = Parser(self.dictionary, self.reader)
|
||||||
self.assembler = Assembler(self.dictionary)
|
self.assembler = Assembler(self.dictionary)
|
||||||
|
|
||||||
def compile_source(self, source: str) -> Emission:
|
def compile_source(self, source: str) -> Emission:
|
||||||
tokens = list(self.reader.tokenize(source))
|
tokens = self.reader.tokenize(source)
|
||||||
module = self.parser.parse(tokens, source)
|
module = self.parser.parse(tokens, source)
|
||||||
return self.assembler.emit(module)
|
return self.assembler.emit(module)
|
||||||
|
|
||||||
|
|||||||
6
main.sl
6
main.sl
@@ -1,15 +1,15 @@
|
|||||||
import stdlib.sl
|
import stdlib.sl
|
||||||
|
import fn.sl
|
||||||
|
|
||||||
: main
|
: main
|
||||||
2 40 +
|
2 40 +
|
||||||
puts
|
puts
|
||||||
extend-syntax
|
extend-syntax
|
||||||
1
|
foo(1, 2)
|
||||||
2
|
|
||||||
foo()
|
|
||||||
puts
|
puts
|
||||||
0
|
0
|
||||||
;
|
;
|
||||||
|
|
||||||
fn foo(int a, int b){
|
fn foo(int a, int b){
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
221
stdlib.sl
221
stdlib.sl
@@ -47,211 +47,6 @@ puts_finish_digits:
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
: extend-syntax
|
|
||||||
enable-call-syntax
|
|
||||||
;
|
|
||||||
immediate
|
|
||||||
compile-only
|
|
||||||
|
|
||||||
:py fn {
|
|
||||||
FN_SPLIT_CHARS = set("(),{};+-*/%,")
|
|
||||||
|
|
||||||
def split_token(token):
|
|
||||||
lex = token.lexeme
|
|
||||||
parts = []
|
|
||||||
idx = 0
|
|
||||||
while idx < len(lex):
|
|
||||||
char = lex[idx]
|
|
||||||
if char in FN_SPLIT_CHARS:
|
|
||||||
parts.append(Token(
|
|
||||||
lexeme=char,
|
|
||||||
line=token.line,
|
|
||||||
column=token.column + idx,
|
|
||||||
start=token.start + idx,
|
|
||||||
end=token.start + idx + 1,
|
|
||||||
))
|
|
||||||
idx += 1
|
|
||||||
continue
|
|
||||||
start_idx = idx
|
|
||||||
while idx < len(lex) and lex[idx] not in FN_SPLIT_CHARS:
|
|
||||||
idx += 1
|
|
||||||
segment = lex[start_idx:idx]
|
|
||||||
if segment:
|
|
||||||
parts.append(Token(
|
|
||||||
lexeme=segment,
|
|
||||||
line=token.line,
|
|
||||||
column=token.column + start_idx,
|
|
||||||
start=token.start + start_idx,
|
|
||||||
end=token.start + idx,
|
|
||||||
))
|
|
||||||
return [part for part in parts if part.lexeme]
|
|
||||||
|
|
||||||
class FnLexer:
|
|
||||||
def __init__(self, parser):
|
|
||||||
self.parser = parser
|
|
||||||
self.buffer = []
|
|
||||||
|
|
||||||
def _fill(self):
|
|
||||||
while not self.buffer:
|
|
||||||
if self.parser._eof():
|
|
||||||
raise ParseError("unexpected EOF inside fn definition")
|
|
||||||
token = self.parser.next_token()
|
|
||||||
split = split_token(token)
|
|
||||||
if not split:
|
|
||||||
continue
|
|
||||||
self.buffer.extend(split)
|
|
||||||
|
|
||||||
def peek(self):
|
|
||||||
self._fill()
|
|
||||||
return self.buffer[0]
|
|
||||||
|
|
||||||
def pop(self):
|
|
||||||
token = self.peek()
|
|
||||||
self.buffer.pop(0)
|
|
||||||
return token
|
|
||||||
|
|
||||||
def expect(self, lexeme):
|
|
||||||
token = self.pop()
|
|
||||||
if token.lexeme != lexeme:
|
|
||||||
raise ParseError(f"expected '{lexeme}' but found '{token.lexeme}'")
|
|
||||||
return token
|
|
||||||
|
|
||||||
def push_back_remaining(self):
|
|
||||||
if not self.buffer:
|
|
||||||
return
|
|
||||||
self.parser.tokens[self.parser.pos:self.parser.pos] = self.buffer
|
|
||||||
self.buffer = []
|
|
||||||
|
|
||||||
def collect_block_tokens(self):
|
|
||||||
depth = 1
|
|
||||||
collected = []
|
|
||||||
while depth > 0:
|
|
||||||
token = self.pop()
|
|
||||||
if token.lexeme == "{":
|
|
||||||
depth += 1
|
|
||||||
collected.append(token)
|
|
||||||
continue
|
|
||||||
if token.lexeme == "}":
|
|
||||||
depth -= 1
|
|
||||||
if depth == 0:
|
|
||||||
break
|
|
||||||
collected.append(token)
|
|
||||||
continue
|
|
||||||
collected.append(token)
|
|
||||||
return collected
|
|
||||||
|
|
||||||
OP_PRECEDENCE = {}
|
|
||||||
OP_PRECEDENCE["+"] = 1
|
|
||||||
OP_PRECEDENCE["-"] = 1
|
|
||||||
OP_PRECEDENCE["*"] = 2
|
|
||||||
OP_PRECEDENCE["/"] = 2
|
|
||||||
OP_PRECEDENCE["%"] = 2
|
|
||||||
|
|
||||||
def parse_fn_body(tokens):
|
|
||||||
if not tokens:
|
|
||||||
raise ParseError("empty function body")
|
|
||||||
lexemes = [tok.lexeme for tok in tokens if tok.lexeme]
|
|
||||||
if not lexemes or lexemes[0] != "return":
|
|
||||||
raise ParseError("function body must start with 'return'")
|
|
||||||
if lexemes[-1] != ";":
|
|
||||||
raise ParseError("function body must terminate with ';'")
|
|
||||||
extra = lexemes[1:-1]
|
|
||||||
if not extra:
|
|
||||||
raise ParseError("missing return expression")
|
|
||||||
return extra
|
|
||||||
|
|
||||||
def shunting_yard(tokens):
|
|
||||||
output = []
|
|
||||||
stack = []
|
|
||||||
for token in tokens:
|
|
||||||
if token == "(":
|
|
||||||
stack.append(token)
|
|
||||||
continue
|
|
||||||
if token == ")":
|
|
||||||
while stack and stack[-1] != "(":
|
|
||||||
output.append(stack.pop())
|
|
||||||
if not stack:
|
|
||||||
raise ParseError("mismatched parentheses in return expression")
|
|
||||||
stack.pop()
|
|
||||||
continue
|
|
||||||
if token in OP_PRECEDENCE:
|
|
||||||
while stack and stack[-1] in OP_PRECEDENCE and OP_PRECEDENCE[stack[-1]] >= OP_PRECEDENCE[token]:
|
|
||||||
output.append(stack.pop())
|
|
||||||
stack.append(token)
|
|
||||||
continue
|
|
||||||
output.append(token)
|
|
||||||
while stack:
|
|
||||||
top = stack.pop()
|
|
||||||
if top == "(":
|
|
||||||
raise ParseError("mismatched parentheses in return expression")
|
|
||||||
output.append(top)
|
|
||||||
return output
|
|
||||||
|
|
||||||
def is_int_literal(text):
|
|
||||||
try:
|
|
||||||
int(text, 0)
|
|
||||||
return True
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def translate_postfix(postfix, params):
|
|
||||||
indices = {name: idx for idx, name in enumerate(params)}
|
|
||||||
translated = []
|
|
||||||
for token in postfix:
|
|
||||||
if token in indices:
|
|
||||||
translated.append(str(indices[token]))
|
|
||||||
translated.append("rpick")
|
|
||||||
continue
|
|
||||||
if is_int_literal(token):
|
|
||||||
translated.append(token)
|
|
||||||
continue
|
|
||||||
translated.append(token)
|
|
||||||
return translated
|
|
||||||
|
|
||||||
def macro(ctx):
|
|
||||||
parser = ctx.parser
|
|
||||||
if not isinstance(parser.context_stack[-1], Module):
|
|
||||||
raise ParseError("'fn' definitions must be top-level")
|
|
||||||
lexer = FnLexer(parser)
|
|
||||||
name_token = lexer.pop()
|
|
||||||
name = name_token.lexeme
|
|
||||||
if not is_identifier(name):
|
|
||||||
raise ParseError("invalid function name for 'fn'")
|
|
||||||
lexer.expect("(")
|
|
||||||
params = []
|
|
||||||
if lexer.peek().lexeme != ")":
|
|
||||||
while True:
|
|
||||||
type_token = lexer.pop()
|
|
||||||
if type_token.lexeme != "int":
|
|
||||||
raise ParseError("only 'int' parameters are supported in fn definitions")
|
|
||||||
param_token = lexer.pop()
|
|
||||||
if not is_identifier(param_token.lexeme):
|
|
||||||
raise ParseError("invalid parameter name in fn definition")
|
|
||||||
params.append(param_token.lexeme)
|
|
||||||
if lexer.peek().lexeme == ",":
|
|
||||||
lexer.pop()
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
lexer.expect(")")
|
|
||||||
lexer.expect("{")
|
|
||||||
body_tokens = lexer.collect_block_tokens()
|
|
||||||
lexer.push_back_remaining()
|
|
||||||
if len(params) != len(set(params)):
|
|
||||||
raise ParseError("duplicate parameter names in fn definition")
|
|
||||||
return_tokens = parse_fn_body(body_tokens)
|
|
||||||
postfix = shunting_yard(return_tokens)
|
|
||||||
body_words = []
|
|
||||||
for _ in reversed(params):
|
|
||||||
body_words.append(">r")
|
|
||||||
body_words.extend(translate_postfix(postfix, params))
|
|
||||||
for _ in params:
|
|
||||||
body_words.append("rdrop")
|
|
||||||
generated = []
|
|
||||||
emit_definition(generated, name_token, name, body_words)
|
|
||||||
ctx.inject_token_objects(generated)
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
:asm dup {
|
:asm dup {
|
||||||
mov rax, [r12]
|
mov rax, [r12]
|
||||||
sub r12, 8
|
sub r12, 8
|
||||||
@@ -264,6 +59,13 @@ compile-only
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
:asm over {
|
||||||
|
mov rax, [r12 + 8]
|
||||||
|
sub r12, 8
|
||||||
|
mov [r12], rax
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
:asm swap {
|
:asm swap {
|
||||||
mov rax, [r12]
|
mov rax, [r12]
|
||||||
mov rbx, [r12 + 8]
|
mov rbx, [r12 + 8]
|
||||||
@@ -453,6 +255,15 @@ compile-only
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
:asm pick {
|
||||||
|
mov rcx, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rax, [r12 + rcx * 8]
|
||||||
|
sub r12, 8
|
||||||
|
mov [r12], rax
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
:asm rpick {
|
:asm rpick {
|
||||||
mov rcx, [r12]
|
mov rcx, [r12]
|
||||||
add r12, 8
|
add r12, 8
|
||||||
|
|||||||
5
test.sl
5
test.sl
@@ -1,4 +1,5 @@
|
|||||||
import stdlib.sl
|
import stdlib.sl
|
||||||
|
import fn.sl
|
||||||
|
|
||||||
:asm mem-slot {
|
:asm mem-slot {
|
||||||
lea rax, [rel print_buf]
|
lea rax, [rel print_buf]
|
||||||
@@ -33,8 +34,8 @@ struct: Point
|
|||||||
|
|
||||||
extend-syntax
|
extend-syntax
|
||||||
|
|
||||||
fn fancy_add(int left, int right){
|
fn fancy_add(int a, int b){
|
||||||
return (left + right) * right;
|
return (a + b) * b;
|
||||||
}
|
}
|
||||||
|
|
||||||
: test-add
|
: test-add
|
||||||
|
|||||||
153
tests/run_tests.py
Normal file
153
tests/run_tests.py
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Simple end-to-end test runner for L2.
|
||||||
|
|
||||||
|
Each test case provides an L2 program source and an expected stdout. The runner
|
||||||
|
invokes the bootstrap compiler on the fly and executes the produced binary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
COMPILER = ROOT / "main.py"
|
||||||
|
PYTHON = Path(sys.executable)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TestCase:
|
||||||
|
name: str
|
||||||
|
source: str
|
||||||
|
expected_stdout: str
|
||||||
|
|
||||||
|
|
||||||
|
CASES: List[TestCase] = [
|
||||||
|
TestCase(
|
||||||
|
name="call_syntax_parens",
|
||||||
|
source=f"""
|
||||||
|
import {ROOT / 'stdlib.sl'}
|
||||||
|
import {ROOT / 'fn.sl'}
|
||||||
|
|
||||||
|
: main
|
||||||
|
2 40 +
|
||||||
|
puts
|
||||||
|
extend-syntax
|
||||||
|
foo(1, 2)
|
||||||
|
puts
|
||||||
|
0
|
||||||
|
;
|
||||||
|
|
||||||
|
fn foo(int a, int b){{
|
||||||
|
return a + b;
|
||||||
|
}}
|
||||||
|
""",
|
||||||
|
expected_stdout="42\n3\n",
|
||||||
|
),
|
||||||
|
TestCase(
|
||||||
|
name="loops_and_cmp",
|
||||||
|
source=f"""
|
||||||
|
import {ROOT / 'stdlib.sl'}
|
||||||
|
|
||||||
|
: main
|
||||||
|
0
|
||||||
|
5 for
|
||||||
|
1 +
|
||||||
|
next
|
||||||
|
puts
|
||||||
|
5 5 == puts
|
||||||
|
5 4 == puts
|
||||||
|
0
|
||||||
|
;
|
||||||
|
""",
|
||||||
|
expected_stdout="5\n1\n0\n",
|
||||||
|
),
|
||||||
|
TestCase(
|
||||||
|
name="override_dup_compile_time",
|
||||||
|
source=f"""
|
||||||
|
import {ROOT / 'stdlib.sl'}
|
||||||
|
|
||||||
|
: dup
|
||||||
|
6
|
||||||
|
;
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
: emit-overridden
|
||||||
|
"dup" use-l2-ct
|
||||||
|
42
|
||||||
|
dup
|
||||||
|
int>string
|
||||||
|
nil
|
||||||
|
token-from-lexeme
|
||||||
|
list-new
|
||||||
|
swap
|
||||||
|
list-append
|
||||||
|
inject-tokens
|
||||||
|
;
|
||||||
|
immediate
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
: main
|
||||||
|
emit-overridden
|
||||||
|
puts
|
||||||
|
0
|
||||||
|
;
|
||||||
|
""",
|
||||||
|
expected_stdout="6\n",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def run_case(case: TestCase) -> None:
|
||||||
|
print(f"[run] {case.name}")
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
src_path = Path(tmp) / f"{case.name}.sl"
|
||||||
|
exe_path = Path(tmp) / f"{case.name}.out"
|
||||||
|
src_path.write_text(case.source.strip() + "\n", encoding="utf-8")
|
||||||
|
|
||||||
|
compile_cmd = [str(PYTHON), str(COMPILER), str(src_path), "-o", str(exe_path)]
|
||||||
|
compile_result = subprocess.run(
|
||||||
|
compile_cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
cwd=ROOT,
|
||||||
|
)
|
||||||
|
if compile_result.returncode != 0:
|
||||||
|
sys.stderr.write("[fail] compile error\n")
|
||||||
|
sys.stderr.write(compile_result.stdout)
|
||||||
|
sys.stderr.write(compile_result.stderr)
|
||||||
|
raise SystemExit(compile_result.returncode)
|
||||||
|
|
||||||
|
run_result = subprocess.run(
|
||||||
|
[str(exe_path)],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
cwd=ROOT,
|
||||||
|
)
|
||||||
|
if run_result.returncode != 0:
|
||||||
|
sys.stderr.write("[fail] execution error\n")
|
||||||
|
sys.stderr.write(run_result.stdout)
|
||||||
|
sys.stderr.write(run_result.stderr)
|
||||||
|
raise SystemExit(run_result.returncode)
|
||||||
|
|
||||||
|
if run_result.stdout != case.expected_stdout:
|
||||||
|
sys.stderr.write(f"[fail] output mismatch for {case.name}\n")
|
||||||
|
sys.stderr.write("expected:\n" + case.expected_stdout)
|
||||||
|
sys.stderr.write("got:\n" + run_result.stdout)
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
print(f"[ok] {case.name}")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
for case in CASES:
|
||||||
|
run_case(case)
|
||||||
|
print("[all tests passed]")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user