This commit is contained in:
IgorCielniak
2025-12-14 00:38:19 +01:00
parent 6910e05be6
commit 6574222280
23 changed files with 1473 additions and 277 deletions

Binary file not shown.

BIN
a.out

Binary file not shown.

3
a.sl
View File

@@ -2,4 +2,5 @@ import stdlib.sl
: main : main
"hello world" puts "hello world" puts
; ;
compile-time main

View File

@@ -19,7 +19,7 @@ _start:
mov rax, 60 mov rax, 60
syscall syscall
word_puts: 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 rax, [r12] ; len or int value
mov rbx, [r12 + 8] ; possible address mov rbx, [r12 + 8] ; possible address
cmp rax, 0 cmp rax, 0
@@ -114,6 +114,64 @@ word_swap:
mov [r12], rbx mov [r12], rbx
mov [r12 + 8], rax mov [r12 + 8], rax
ret 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: word__2b:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8
@@ -242,6 +300,39 @@ word_exit:
mov rax, 60 mov rax, 60
syscall syscall
ret 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: word__3er:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8

BIN
build/a.o

Binary file not shown.

View File

@@ -19,7 +19,7 @@ _start:
mov rax, 60 mov rax, 60
syscall syscall
word_puts: 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 rax, [r12] ; len or int value
mov rbx, [r12 + 8] ; possible address mov rbx, [r12 + 8] ; possible address
cmp rax, 0 cmp rax, 0
@@ -114,6 +114,64 @@ word_swap:
mov [r12], rbx mov [r12], rbx
mov [r12 + 8], rax mov [r12 + 8], rax
ret 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: word__2b:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8
@@ -242,6 +300,39 @@ word_exit:
mov rax, 60 mov rax, 60
syscall syscall
ret 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: word__3er:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8

Binary file not shown.

388
build/hello.asm Normal file
View File

@@ -0,0 +1,388 @@
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 str_0
sub r12, 8
mov qword [r12], str_0
; push 11
sub r12, 8
mov qword [r12], 11
call word_puts
ret
section .data
data_start:
str_0: db 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0
str_0_len equ 11
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:

BIN
build/hello.o Normal file

Binary file not shown.

View File

@@ -19,7 +19,7 @@ _start:
mov rax, 60 mov rax, 60
syscall syscall
word_puts: 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 rax, [r12] ; len or int value
mov rbx, [r12 + 8] ; possible address mov rbx, [r12 + 8] ; possible address
cmp rax, 0 cmp rax, 0
@@ -114,6 +114,64 @@ word_swap:
mov [r12], rbx mov [r12], rbx
mov [r12 + 8], rax mov [r12 + 8], rax
ret 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: word__2b:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8
@@ -242,6 +300,39 @@ word_exit:
mov rax, 60 mov rax, 60
syscall syscall
ret 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: word__3er:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8

Binary file not shown.

View File

@@ -19,7 +19,7 @@ _start:
mov rax, 60 mov rax, 60
syscall syscall
word_puts: 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 rax, [r12] ; len or int value
mov rbx, [r12 + 8] ; possible address mov rbx, [r12 + 8] ; possible address
cmp rax, 0 cmp rax, 0
@@ -114,6 +114,64 @@ word_swap:
mov [r12], rbx mov [r12], rbx
mov [r12 + 8], rax mov [r12 + 8], rax
ret 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: word__2b:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8
@@ -242,6 +300,39 @@ word_exit:
mov rax, 60 mov rax, 60
syscall syscall
ret 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: word__3er:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8

Binary file not shown.

View File

@@ -19,7 +19,7 @@ _start:
mov rax, 60 mov rax, 60
syscall syscall
word_puts: 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 rax, [r12] ; len or int value
mov rbx, [r12 + 8] ; possible address mov rbx, [r12 + 8] ; possible address
cmp rax, 0 cmp rax, 0
@@ -114,6 +114,64 @@ word_swap:
mov [r12], rbx mov [r12], rbx
mov [r12 + 8], rax mov [r12 + 8], rax
ret 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: word__2b:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8
@@ -242,6 +300,39 @@ word_exit:
mov rax, 60 mov rax, 60
syscall syscall
ret 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: word__3er:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8

Binary file not shown.

View File

@@ -19,7 +19,7 @@ _start:
mov rax, 60 mov rax, 60
syscall syscall
word_puts: 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 rax, [r12] ; len or int value
mov rbx, [r12 + 8] ; possible address mov rbx, [r12 + 8] ; possible address
cmp rax, 0 cmp rax, 0
@@ -114,6 +114,64 @@ word_swap:
mov [r12], rbx mov [r12], rbx
mov [r12 + 8], rax mov [r12 + 8], rax
ret 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: word__2b:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8
@@ -242,6 +300,39 @@ word_exit:
mov rax, 60 mov rax, 60
syscall syscall
ret 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: word__3er:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8

Binary file not shown.

View File

@@ -19,7 +19,7 @@ _start:
mov rax, 60 mov rax, 60
syscall syscall
word_puts: 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 rax, [r12] ; len or int value
mov rbx, [r12 + 8] ; possible address mov rbx, [r12 + 8] ; possible address
cmp rax, 0 cmp rax, 0
@@ -114,6 +114,64 @@ word_swap:
mov [r12], rbx mov [r12], rbx
mov [r12 + 8], rax mov [r12 + 8], rax
ret 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: word__2b:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8
@@ -242,6 +300,39 @@ word_exit:
mov rax, 60 mov rax, 60
syscall syscall
ret 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: word__3er:
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8

Binary file not shown.

26
fn.sl
View File

@@ -100,27 +100,29 @@ compile-only
compile-only compile-only
: fn-lexemes-from-tokens : fn-lexemes-from-tokens
list-new >r # tokens (r: acc) >r # (r: tokens)
0 # tokens idx list-new # acc
begin begin
over list-length over swap >= if # stop when idx >= len 0 rpick list-empty? if
drop drop # drop idx and tokens (flag consumed by if) rdrop exit
r> exit # return acc
then then
over over list-get token-lexeme # tokens idx lex 0 rpick list-pop-front # acc tokens' first
r> swap list-append >r # tokens idx rdrop # acc tokens'
1 + # tokens idx+1 swap # acc first tokens'
>r # acc first (r: tokens')
token-lexeme # acc lex
list-append # acc'
again again
; ;
compile-only compile-only
: fn-validate-body : fn-validate-body
dup list-length 0 == if "empty function body" parse-error then 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 0 list-get token-lexeme "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 dup list-last ";" string= 0 == if "function body must terminate with ';'" parse-error then
list-clone # work on a copy list-clone # body body'
list-pop drop # drop trailing ';' list-pop drop # body expr' (trim trailing ';')
list-pop-front drop # drop leading 'return' list-pop-front drop # body expr (trim leading 'return')
dup list-length 0 == if "missing return expression" parse-error then dup list-length 0 == if "missing return expression" parse-error then
; ;
compile-only compile-only

567
main.py
View File

@@ -11,6 +11,8 @@ This file now contains working scaffolding for:
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import ctypes
import mmap
import subprocess import subprocess
import sys import sys
import textwrap import textwrap
@@ -18,6 +20,13 @@ from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Union, Tuple from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Union, Tuple
try: # lazy optional import; required for compile-time :asm execution
from keystone import Ks, KsError, KS_ARCH_X86, KS_MODE_64
except Exception: # pragma: no cover - optional dependency
Ks = None
KsError = Exception
KS_ARCH_X86 = KS_MODE_64 = None
class ParseError(Exception): class ParseError(Exception):
"""Raised when the source stream cannot be parsed.""" """Raised when the source stream cannot be parsed."""
@@ -764,11 +773,13 @@ class CompileTimeVM:
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]] = [] self.loop_stack: List[Dict[str, Any]] = []
self._handles = _CTHandleTable()
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() self.loop_stack.clear()
self._handles.clear()
def push(self, value: Any) -> None: def push(self, value: Any) -> None:
self.stack.append(value) self.stack.append(value)
@@ -778,6 +789,22 @@ class CompileTimeVM:
raise ParseError("compile-time stack underflow") raise ParseError("compile-time stack underflow")
return self.stack.pop() return self.stack.pop()
def _resolve_handle(self, value: Any) -> Any:
if isinstance(value, int):
for delta in (0, -1, 1):
candidate = value + delta
if candidate in self._handles.objects:
obj = self._handles.objects[candidate]
self._handles.objects[value] = obj
return obj
# Occasionally a raw object id can appear on the stack; recover it if we still
# hold the object reference.
for obj in self._handles.objects.values():
if id(obj) == value:
self._handles.objects[value] = obj
return obj
return value
def peek(self) -> Any: def peek(self) -> Any:
if not self.stack: if not self.stack:
raise ParseError("compile-time stack underflow") raise ParseError("compile-time stack underflow")
@@ -790,19 +817,24 @@ class CompileTimeVM:
return value return value
def pop_str(self) -> str: def pop_str(self) -> str:
value = self.pop() value = self._resolve_handle(self.pop())
if not isinstance(value, str): if not isinstance(value, str):
raise ParseError("expected string on compile-time stack") raise ParseError("expected string on compile-time stack")
return value return value
def pop_list(self) -> List[Any]: def pop_list(self) -> List[Any]:
value = self.pop() value = self._resolve_handle(self.pop())
if not isinstance(value, list): if not isinstance(value, list):
raise ParseError("expected list on compile-time stack") known = value in self._handles.objects if isinstance(value, int) else False
handles_size = len(self._handles.objects)
handle_keys = list(self._handles.objects.keys())
raise ParseError(
f"expected list on compile-time stack, got {type(value).__name__} value={value!r} known_handle={known} handles={handles_size}:{handle_keys!r} stack={self.stack!r}"
)
return value return value
def pop_token(self) -> Token: def pop_token(self) -> Token:
value = self.pop() value = self._resolve_handle(self.pop())
if not isinstance(value, Token): if not isinstance(value, Token):
raise ParseError("expected token on compile-time stack") raise ParseError("expected token on compile-time stack")
return value return value
@@ -826,9 +858,210 @@ class CompileTimeVM:
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):
raise ParseError(f"word '{word.name}' cannot run at compile time") self._run_asm_definition(word)
return
self._execute_nodes(definition.body) self._execute_nodes(definition.body)
def _run_asm_definition(self, word: Word) -> None:
definition = word.definition
if Ks is None:
raise ParseError("keystone is required for compile-time :asm execution; install keystone-engine")
if not isinstance(definition, AsmDefinition): # pragma: no cover - defensive
raise ParseError(f"word '{word.name}' has no asm body")
asm_body = definition.body.strip("\n")
string_mode = word.name == "puts"
handles = self._handles
non_int_data = any(not isinstance(v, int) for v in self.stack)
non_int_return = any(not isinstance(v, int) for v in self.return_stack)
# Collect all strings present on data and return stacks so we can point
# puts() at a real buffer and pass its range check (data_start..data_end).
strings: List[str] = []
if string_mode:
for v in self.stack + self.return_stack:
if isinstance(v, str):
strings.append(v)
data_blob = b""
string_addrs: Dict[str, Tuple[int, int]] = {}
if strings:
offset = 0
parts: List[bytes] = []
seen: Dict[str, Tuple[int, int]] = {}
for s in strings:
if s in seen:
string_addrs[s] = seen[s]
continue
encoded = s.encode("utf-8") + b"\x00"
parts.append(encoded)
addr = offset
length = len(encoded) - 1
seen[s] = (addr, length)
string_addrs[s] = (addr, length)
offset += len(encoded)
data_blob = b"".join(parts)
string_buffer: Optional[ctypes.Array[Any]] = None
data_start = 0
data_end = 0
if data_blob:
string_buffer = ctypes.create_string_buffer(data_blob)
data_start = ctypes.addressof(string_buffer)
data_end = data_start + len(data_blob)
handles.refs.append(string_buffer)
for s, (off, _len) in string_addrs.items():
handles.objects[data_start + off] = s
PRINT_BUF_BYTES = 128
print_buffer = ctypes.create_string_buffer(PRINT_BUF_BYTES)
handles.refs.append(print_buffer)
print_buf = ctypes.addressof(print_buffer)
wrapper_lines = []
wrapper_lines.extend([
"_ct_entry:",
" push rbx",
" push r12",
" push r13",
" push r14",
" push r15",
" mov r12, rdi", # data stack pointer
" mov r13, rsi", # return stack pointer
" mov r14, rdx", # out ptr for r12
" mov r15, rcx", # out ptr for r13
])
if asm_body:
patched_body = []
for line in asm_body.splitlines():
line = line.strip()
if line == "ret":
line = "jmp _ct_save"
if "lea r8, [rel data_start]" in line:
line = line.replace("lea r8, [rel data_start]", f"mov r8, {data_start}")
if "lea r9, [rel data_end]" in line:
line = line.replace("lea r9, [rel data_end]", f"mov r9, {data_end}")
if "mov byte [rel print_buf]" in line or "mov byte ptr [rel print_buf]" in line:
patched_body.append(f"mov rax, {print_buf}")
patched_body.append("mov byte ptr [rax], 10")
continue
if "lea rsi, [rel print_buf_end]" in line:
line = f"mov rsi, {print_buf + PRINT_BUF_BYTES}"
if "lea rsi, [rel print_buf]" in line:
line = f"mov rsi, {print_buf}"
patched_body.append(line)
wrapper_lines.extend(patched_body)
wrapper_lines.extend([
"_ct_save:",
" mov [r14], r12",
" mov [r15], r13",
" pop r15",
" pop r14",
" pop r13",
" pop r12",
" pop rbx",
" ret",
])
def _normalize_sizes(line: str) -> str:
for size in ("qword", "dword", "word", "byte"):
line = line.replace(f"{size} [", f"{size} ptr [")
return line
def _strip_comment(line: str) -> str:
return line.split(";", 1)[0].rstrip()
normalized_lines = []
for raw in wrapper_lines:
stripped = _strip_comment(raw)
if not stripped.strip():
continue
normalized_lines.append(_normalize_sizes(stripped))
ks = Ks(KS_ARCH_X86, KS_MODE_64)
try:
encoding, _ = ks.asm("\n".join(normalized_lines))
except KsError as exc:
debug_lines = "\n".join(normalized_lines)
raise ParseError(
f"keystone failed for word '{word.name}': {exc}\n--- asm ---\n{debug_lines}\n--- end asm ---"
) from exc
if encoding is None:
raise ParseError(
f"keystone produced no code for word '{word.name}' (lines: {len(wrapper_lines)})"
)
code = bytes(encoding)
code_buf = mmap.mmap(-1, len(code), prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC)
code_buf.write(code)
code_ptr = ctypes.addressof(ctypes.c_char.from_buffer(code_buf))
func_type = ctypes.CFUNCTYPE(None, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64)
func = func_type(code_ptr)
handles = self._handles
def _marshal_stack(py_stack: List[Any]) -> Tuple[int, int, int, Any]:
capacity = len(py_stack) + 16
buffer = (ctypes.c_int64 * capacity)()
base = ctypes.addressof(buffer)
top = base + capacity * 8
sp = top
for value in py_stack:
sp -= 8
if isinstance(value, int):
ctypes.c_int64.from_address(sp).value = value
elif isinstance(value, str):
if string_mode:
offset, strlen = string_addrs.get(value, (0, 0))
addr = data_start + offset if data_start else handles.store(value)
# puts expects (len, addr) with len on top
ctypes.c_int64.from_address(sp).value = addr
sp -= 8
ctypes.c_int64.from_address(sp).value = strlen
else:
ctypes.c_int64.from_address(sp).value = handles.store(value)
else:
ctypes.c_int64.from_address(sp).value = handles.store(value)
return sp, top, base, buffer
# r12/r13 must point at the top element (or top of buffer if empty)
buffers: List[Any] = []
d_sp, d_top, d_base, d_buf = _marshal_stack(self.stack)
buffers.append(d_buf)
r_sp, r_top, r_base, r_buf = _marshal_stack(self.return_stack)
buffers.append(r_buf)
out_d = ctypes.c_uint64(0)
out_r = ctypes.c_uint64(0)
func(d_sp, r_sp, ctypes.addressof(out_d), ctypes.addressof(out_r))
new_d = out_d.value
new_r = out_r.value
if not (d_base <= new_d <= d_top):
raise ParseError(f"compile-time asm '{word.name}' corrupted data stack pointer")
if not (r_base <= new_r <= r_top):
raise ParseError(f"compile-time asm '{word.name}' corrupted return stack pointer")
def _unmarshal_stack(sp: int, top: int, table: _CTHandleTable) -> List[Any]:
if sp == top:
return []
values: List[Any] = []
addr = top - 8
while addr >= sp:
raw = ctypes.c_int64.from_address(addr).value
if raw in table.objects:
obj = table.objects[raw]
if isinstance(obj, str) and values and isinstance(values[-1], int):
# collapse (len, addr) pairs back into the original string
values.pop()
values.append(obj)
else:
values.append(obj)
else:
values.append(raw)
addr -= 8
return values
self.stack = _unmarshal_stack(new_d, d_top, handles)
self.return_stack = _unmarshal_stack(new_r, r_top, handles)
def _call_word_by_name(self, name: str) -> None: def _call_word_by_name(self, name: str) -> None:
word = self.dictionary.lookup(name) word = self.dictionary.lookup(name)
if word is None: if word is None:
@@ -1085,6 +1318,27 @@ def _parse_string_literal(token: Token) -> Optional[str]:
return "".join(result) return "".join(result)
class _CTHandleTable:
"""Keeps Python object references stable across compile-time asm calls."""
def __init__(self) -> None:
self.objects: Dict[int, Any] = {}
self.refs: List[Any] = []
self.string_buffers: List[ctypes.Array[Any]] = []
def clear(self) -> None:
self.objects.clear()
self.refs.clear()
self.string_buffers.clear()
def store(self, value: Any) -> int:
addr = id(value)
self.refs.append(value)
self.objects[addr] = value
return addr
class Assembler: class Assembler:
def __init__(self, dictionary: Dictionary) -> None: def __init__(self, dictionary: Dictionary) -> None:
self.dictionary = dictionary self.dictionary = dictionary
@@ -1298,6 +1552,24 @@ def macro_compile_only(ctx: MacroContext) -> Optional[List[ASTNode]]:
return None return None
def macro_compile_time(ctx: MacroContext) -> Optional[List[ASTNode]]:
"""Run the next word at compile time and still emit it for runtime."""
parser = ctx.parser
if parser._eof():
raise ParseError("word name missing after 'compile-time'")
tok = parser.next_token()
name = tok.lexeme
word = parser.dictionary.lookup(name)
if word is None:
raise ParseError(f"unknown word '{name}' for compile-time")
if word.compile_only:
raise ParseError(f"word '{name}' is compile-time only")
parser.compile_time_vm.invoke(word)
if isinstance(parser.context_stack[-1], Definition):
parser.emit_node(WordRef(name=name))
return None
def macro_begin_text_macro(ctx: MacroContext) -> Optional[List[ASTNode]]: def macro_begin_text_macro(ctx: MacroContext) -> Optional[List[ASTNode]]:
parser = ctx.parser parser = ctx.parser
if parser._eof(): if parser._eof():
@@ -1447,14 +1719,6 @@ def _ensure_lexer(value: Any) -> SplitLexer:
return value return value
def _truthy(value: Any) -> bool:
if isinstance(value, bool):
return value
if isinstance(value, int):
return value != 0
return value is not None
def _coerce_str(value: Any) -> str: def _coerce_str(value: Any) -> str:
if isinstance(value, str): if isinstance(value, str):
return value return value
@@ -1473,217 +1737,21 @@ def _default_template(template: Optional[Token]) -> Token:
return template return template
def _trunc_divmod(a: int, b: int) -> Tuple[int, int]:
if b == 0:
raise ParseError("division by zero")
quot = abs(a) // abs(b)
if (a < 0) ^ (b < 0):
quot = -quot
rem = a - quot * b
return quot, rem
def _ct_dup(vm: CompileTimeVM) -> None:
vm.push(vm.peek())
def _ct_drop(vm: CompileTimeVM) -> None:
vm.pop()
def _ct_swap(vm: CompileTimeVM) -> None:
a = vm.pop()
b = vm.pop()
vm.push(a)
vm.push(b)
def _ct_over(vm: CompileTimeVM) -> None:
if len(vm.stack) < 2:
raise ParseError("over requires two stack values")
vm.push(vm.stack[-2])
def _ct_rot(vm: CompileTimeVM) -> None:
if len(vm.stack) < 3:
raise ParseError("rot requires three stack values")
vm.stack[-3], vm.stack[-2], vm.stack[-1] = vm.stack[-2], vm.stack[-1], vm.stack[-3]
def _ct_nip(vm: CompileTimeVM) -> None:
if len(vm.stack) < 2:
raise ParseError("nip requires two stack values")
top = vm.pop()
vm.pop()
vm.push(top)
def _ct_tuck(vm: CompileTimeVM) -> None:
if len(vm.stack) < 2:
raise ParseError("tuck requires two stack values")
first = vm.pop()
second = vm.pop()
vm.push(first)
vm.push(second)
vm.push(first)
def _ct_2dup(vm: CompileTimeVM) -> None:
if len(vm.stack) < 2:
raise ParseError("2dup requires two stack values")
second = vm.pop()
first = vm.pop()
vm.push(first)
vm.push(second)
vm.push(first)
vm.push(second)
def _ct_2drop(vm: CompileTimeVM) -> None:
if len(vm.stack) < 2:
raise ParseError("2drop requires two stack values")
vm.pop()
vm.pop()
def _ct_2swap(vm: CompileTimeVM) -> None:
if len(vm.stack) < 4:
raise ParseError("2swap requires four stack values")
a = vm.pop()
b = vm.pop()
c = vm.pop()
d = vm.pop()
vm.push(a)
vm.push(b)
vm.push(c)
vm.push(d)
def _ct_2over(vm: CompileTimeVM) -> None:
if len(vm.stack) < 4:
raise ParseError("2over requires four stack values")
vm.push(vm.stack[-4])
vm.push(vm.stack[-3])
def _ct_minus_rot(vm: CompileTimeVM) -> None:
if len(vm.stack) < 3:
raise ParseError("-rot requires three stack values")
vm.stack[-3], vm.stack[-2], vm.stack[-1] = vm.stack[-1], vm.stack[-3], vm.stack[-2]
def _ct_binary_int(vm: CompileTimeVM, func: Callable[[int, int], int]) -> None:
b = vm.pop_int()
a = vm.pop_int()
vm.push(func(a, b))
def _ct_add(vm: CompileTimeVM) -> None:
_ct_binary_int(vm, lambda a, b: a + b)
def _ct_sub(vm: CompileTimeVM) -> None:
_ct_binary_int(vm, lambda a, b: a - b)
def _ct_mul(vm: CompileTimeVM) -> None:
_ct_binary_int(vm, lambda a, b: a * b)
def _ct_div(vm: CompileTimeVM) -> None:
divisor = vm.pop_int()
dividend = vm.pop_int()
quot, _ = _trunc_divmod(dividend, divisor)
vm.push(quot)
def _ct_mod(vm: CompileTimeVM) -> None:
divisor = vm.pop_int()
dividend = vm.pop_int()
_, rem = _trunc_divmod(dividend, divisor)
vm.push(rem)
def _ct_compare(vm: CompileTimeVM, predicate: Callable[[Any, Any], bool]) -> None:
b = vm.pop()
a = vm.pop()
vm.push(1 if predicate(a, b) else 0)
def _ct_eq(vm: CompileTimeVM) -> None:
_ct_compare(vm, lambda a, b: a == b)
def _ct_ne(vm: CompileTimeVM) -> None:
_ct_compare(vm, lambda a, b: a != b)
def _ct_lt(vm: CompileTimeVM) -> None:
_ct_compare(vm, lambda a, b: a < b)
def _ct_le(vm: CompileTimeVM) -> None:
_ct_compare(vm, lambda a, b: a <= b)
def _ct_gt(vm: CompileTimeVM) -> None:
_ct_compare(vm, lambda a, b: a > b)
def _ct_ge(vm: CompileTimeVM) -> None:
_ct_compare(vm, lambda a, b: a >= b)
def _ct_and(vm: CompileTimeVM) -> None:
b = _truthy(vm.pop())
a = _truthy(vm.pop())
vm.push(1 if (a and b) else 0)
def _ct_or(vm: CompileTimeVM) -> None:
b = _truthy(vm.pop())
a = _truthy(vm.pop())
vm.push(1 if (a or b) else 0)
def _ct_not(vm: CompileTimeVM) -> None:
vm.push(1 if not _truthy(vm.pop()) else 0)
def _ct_to_r(vm: CompileTimeVM) -> None:
vm.return_stack.append(vm.pop())
def _ct_r_from(vm: CompileTimeVM) -> None:
if not vm.return_stack:
raise ParseError("return stack underflow")
vm.push(vm.return_stack.pop())
def _ct_rdrop(vm: CompileTimeVM) -> None:
if not vm.return_stack:
raise ParseError("return stack underflow")
vm.return_stack.pop()
def _ct_rpick(vm: CompileTimeVM) -> None:
index = vm.pop_int()
if index < 0 or index >= len(vm.return_stack):
raise ParseError("rpick index out of range")
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)
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: def _ct_nil_p(vm: CompileTimeVM) -> None:
vm.push(1 if vm.pop() is None else 0) vm.push(1 if vm.pop() is None else 0)
@@ -1704,6 +1772,12 @@ def _ct_list_append(vm: CompileTimeVM) -> None:
vm.push(lst) vm.push(lst)
def _ct_drop(vm: CompileTimeVM) -> None:
if not vm.stack:
return
vm.pop()
def _ct_list_pop(vm: CompileTimeVM) -> None: def _ct_list_pop(vm: CompileTimeVM) -> None:
lst = _ensure_list(vm.pop()) lst = _ensure_list(vm.pop())
if not lst: if not lst:
@@ -1723,7 +1797,7 @@ def _ct_list_pop_front(vm: CompileTimeVM) -> None:
def _ct_list_length(vm: CompileTimeVM) -> None: def _ct_list_length(vm: CompileTimeVM) -> None:
lst = _ensure_list(vm.pop()) lst = vm.pop_list()
vm.push(len(lst)) vm.push(len(lst))
@@ -1955,13 +2029,24 @@ def _ct_int_to_string(vm: CompileTimeVM) -> None:
def _ct_identifier_p(vm: CompileTimeVM) -> None: def _ct_identifier_p(vm: CompileTimeVM) -> None:
value = vm.pop_str() value = vm._resolve_handle(vm.pop())
if isinstance(value, Token):
value = value.lexeme
if not isinstance(value, str):
vm.push(0)
return
vm.push(1 if _is_identifier(value) else 0) vm.push(1 if _is_identifier(value) else 0)
def _ct_token_lexeme(vm: CompileTimeVM) -> None: def _ct_token_lexeme(vm: CompileTimeVM) -> None:
token = vm.pop_token() value = vm._resolve_handle(vm.pop())
vm.push(token.lexeme) if isinstance(value, Token):
vm.push(value.lexeme)
return
if isinstance(value, str):
vm.push(value)
return
raise ParseError("expected token or string on compile-time stack")
def _ct_token_from_lexeme(vm: CompileTimeVM) -> None: def _ct_token_from_lexeme(vm: CompileTimeVM) -> None:
@@ -2068,43 +2153,12 @@ def _register_compile_time_primitives(dictionary: Dictionary) -> None:
if compile_only: if compile_only:
word.compile_only = True word.compile_only = True
register("dup", _ct_dup)
register("drop", _ct_drop)
register("swap", _ct_swap)
register("over", _ct_over)
register("rot", _ct_rot)
register("nip", _ct_nip)
register("tuck", _ct_tuck)
register("2dup", _ct_2dup)
register("2drop", _ct_2drop)
register("2swap", _ct_2swap)
register("2over", _ct_2over)
register("-rot", _ct_minus_rot)
register("+", _ct_add)
register("-", _ct_sub)
register("*", _ct_mul)
register("/", _ct_div)
register("%", _ct_mod)
register("==", _ct_eq)
register("!=", _ct_ne)
register("<", _ct_lt)
register("<=", _ct_le)
register(">", _ct_gt)
register(">=", _ct_ge)
register("and", _ct_and)
register("or", _ct_or)
register("not", _ct_not)
register(">r", _ct_to_r)
register("r>", _ct_r_from)
register("rdrop", _ct_rdrop)
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)
register("list-new", _ct_list_new, compile_only=True) register("list-new", _ct_list_new, compile_only=True)
register("list-clone", _ct_list_clone, compile_only=True) register("list-clone", _ct_list_clone, compile_only=True)
register("list-append", _ct_list_append, compile_only=True) register("list-append", _ct_list_append, compile_only=True)
register("drop", _ct_drop)
register("list-pop", _ct_list_pop, compile_only=True) register("list-pop", _ct_list_pop, compile_only=True)
register("list-pop-front", _ct_list_pop_front, compile_only=True) register("list-pop-front", _ct_list_pop_front, compile_only=True)
register("list-length", _ct_list_length, compile_only=True) register("list-length", _ct_list_length, compile_only=True)
@@ -2239,6 +2293,7 @@ def bootstrap_dictionary() -> Dictionary:
dictionary = Dictionary() dictionary = Dictionary()
dictionary.register(Word(name="immediate", immediate=True, macro=macro_immediate)) dictionary.register(Word(name="immediate", immediate=True, macro=macro_immediate))
dictionary.register(Word(name="compile-only", immediate=True, macro=macro_compile_only)) dictionary.register(Word(name="compile-only", immediate=True, macro=macro_compile_only))
dictionary.register(Word(name="compile-time", immediate=True, macro=macro_compile_time))
dictionary.register(Word(name="macro:", immediate=True, macro=macro_begin_text_macro)) dictionary.register(Word(name="macro:", immediate=True, macro=macro_begin_text_macro))
dictionary.register(Word(name=";macro", immediate=True, macro=macro_end_text_macro)) dictionary.register(Word(name=";macro", immediate=True, macro=macro_end_text_macro))
dictionary.register(Word(name="struct:", immediate=True, macro=macro_struct_begin)) dictionary.register(Word(name="struct:", immediate=True, macro=macro_struct_begin))

115
stdlib.sl
View File

@@ -1,5 +1,5 @@
:asm puts { :asm 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 rax, [r12] ; len or int value
mov rbx, [r12 + 8] ; possible address mov rbx, [r12 + 8] ; possible address
cmp rax, 0 cmp rax, 0
@@ -104,6 +104,80 @@ puts_finish_digits:
} }
; ;
:asm 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
}
;
:asm -rot {
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
}
;
:asm nip {
mov rax, [r12]
add r12, 8 ; drop lower element
mov [r12], rax ; keep original top
}
;
:asm 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
}
;
:asm 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
}
;
:asm 2drop {
add r12, 16
}
;
:asm 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
}
;
:asm 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
}
;
:asm + { :asm + {
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8
@@ -264,6 +338,45 @@ puts_finish_digits:
} }
; ;
:asm 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
}
;
:asm 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
}
;
:asm not {
mov rax, [r12]
test rax, rax
setz al
movzx rax, al
mov [r12], rax
}
;
:asm >r { :asm >r {
mov rax, [r12] mov rax, [r12]
add r12, 8 add r12, 8

Binary file not shown.