can do math

This commit is contained in:
janis 2026-06-10 14:15:36 +02:00
parent 07435f5839
commit 6df1d2ac9b
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8
2 changed files with 541 additions and 41 deletions

View file

@ -109,6 +109,15 @@ extern dealloc
ATOM_QUOTE equ 0
ATOM_TRUE equ 1
ATOM_PLUS equ 2
ATOM_MINUS equ 3
ATOM_MUL equ 4
ATOM_DIV equ 5
ATOM_REM equ 6
ATOM_LT equ 7
ATOM_EQ equ 8
ATOM_CAR equ 9
ATOM_CDR equ 10
ATOM_NOT equ 11
QUOTE_STR db "quote"
QUOTE_STR_LEN equ $ - QUOTE_STR
@ -116,45 +125,391 @@ extern dealloc
TRUE_STR_LEN equ $ - TRUE_STR
PLUS_STR db "+"
PLUS_STR_LEN equ $ - PLUS_STR
MINUS_STR db "-"
MINUS_STR_LEN equ $ - MINUS_STR
MUL_STR db "*"
MUL_STR_LEN equ $ - MUL_STR
DIV_STR db "/"
DIV_STR_LEN equ $ - DIV_STR
REM_STR db "%"
REM_STR_LEN equ $ - REM_STR
LT_STR db "<"
LT_STR_LEN equ $ - LT_STR
EQ_STR db "="
EQ_STR_LEN equ $ - EQ_STR
CAR_STR db "car"
CAR_STR_LEN equ $ - CAR_STR
CDR_STR db "cdr"
CDR_STR_LEN equ $ - CDR_STR
NOT_STR db "not"
NOT_STR_LEN equ $ - NOT_STR
;; sum list $rdi in env $rsi
p_add:
sub rsp, 24
call eval_list
mov qword [rsp], rax ; evaled list
;; folds all elements in the list $rdi using the binary function $rdx in the environment $rsi with an accumulator value of $rcx, returning the result in $rax
;; fn fold_list(List<T>, Env, (U, T) -> U, U) -> U
fold_list:
sub rsp, 32
mov qword [rsp], rdi ; rest
mov qword [rsp + 8], rsi ; env
mov qword [rsp + 16], 0 ; acc = 0
.add_loop:
mov rdi, qword [rsp] ; $rdi = evaled list
mov qword [rsp + 16], rcx ; acc
mov qword [rsp + 24], rdx ; func
.loop:
mov rdi, qword [rsp]
call get_cdr
mov qword [rsp], rax ; update list to cdr
mov qword [rsp], rax ; update rest to cdr
call get_car
mov rdi, rax ; $rdi = car
mov rsi, rax
mov rdi, qword [rsp + 16] ; $rdi = acc
mov rdx, qword [rsp + 8] ; $rdx = env
mov rax, qword [rsp + 24] ; $rax = func
call rax
mov qword [rsp + 16], rax ; update acc to result of func(acc, car)
mov rdi, qword [rsp] ; $rdi = rest
lea rsi, [rel nil]
cmp rdi, rsi
jne .loop
mov rax, qword [rsp + 16] ; return acc
add rsp, 32
ret
;; like fold_list, but requires that the list is non-empty
;; fn reduce_list(List<T>, Env, (T, T) -> T) -> T
reduce_list:
call get_car
mov rcx, rax ; acc = car(list)
call get_cdr ; list = cdr(list)
lea rdi, [rel nil]
cmp rax, rdi
je .done
mov rdi, rax
call fold_list
ret
.done:
mov rax, rcx
ret
;; like reduce_list, calls a predicate instead of a binary function, and returns "t" if the predicate returns non-zero for any pair of adjacent elements, and nil otherwise
any2_list:
sub rsp, 24
mov qword [rsp], rdi ; rest
mov qword [rsp + 8], rsi ; env
mov qword [rsp + 16], rdx ; pred
.loop:
mov rdi, qword [rsp]
call get_cdr
lea rsi, [rel nil]
cmp rax, rsi
je .false
mov qword [rsp], rax ; update rest to cdr
call get_car
mov rsi, rax
mov rdi, qword [rsp]
call get_car
mov rdi, rax
xchg rsi, rdi
mov rdx, qword [rsp + 8] ; $rdx = env
mov rax, qword [rsp + 16] ; $rax = func
call rax
test al, al
jnz .true
jmp .loop
.true:
mov al, 1
add rsp, 24
ret
.false:
xor al, al
add rsp, 24
ret
;; |a, b| {a += b; a}
p_add_inner:
xchg rdi, rsi
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rax, qword [rax + 8] ; num
add qword [rsp + 16], rax ; acc += num
mov rdi, qword [rsp] ; $rdi = list
lea rsi, [rel nil]
cmp rdi, rsi
jne .add_loop
.done:
mov rcx, rax ; b.addr()
mov rdi, rsi
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rcx, qword [rcx + 8] ; b.num
add qword [rax + 8], rcx ; a.num += b.num
mov rax, rdi
ret
.invalid:
xor rdi, rdi
call panic_abort
;; |a, b| {a -= b; a}
p_sub_inner:
xchg rdi, rsi
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rcx, rax ; b.addr()
mov rdi, rsi
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rcx, qword [rcx + 8] ; b.num
sub qword [rax + 8], rcx ; a.num -= b.num
mov rax, rdi
ret
.invalid:
xor rdi, rdi
call panic_abort
;; |a, b| {a *= b; a}
p_imul_inner:
xchg rdi, rsi
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rcx, rax ; b.addr()
mov rdi, rsi
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rcx, qword [rcx + 8] ; b.num
mov rsi, rax ; a.addr()
mov rax, qword [rsi + 8] ; a.num
imul rcx ; a.num * b.num
mov qword [rsi + 8], rax ; a.num = a.num * b.num
mov rax, rdi
ret
.invalid:
xor rdi, rdi
call panic_abort
;; |a, b| {a /= b; a}
p_idiv_inner:
xchg rdi, rsi
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rcx, rax ; b.addr()
mov rdi, rsi
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rcx, qword [rcx + 8] ; b.num
mov rsi, rax ; a.addr()
mov rax, qword [rsi + 8] ; a.num
xor rdx, rdx
idiv rcx ; a.num / b.num
mov qword [rsi + 8], rax ; a.num = a.num / b.num
mov rax, rdi
ret
.invalid:
xor rdi, rdi
call panic_abort
;; |a, b| {a %= b; a}
p_irem_inner:
xchg rdi, rsi
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rcx, rax ; b.addr()
mov rdi, rsi
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rcx, qword [rcx + 8] ; b.num
mov rsi, rax ; a.addr()
mov rax, qword [rsi + 8] ; a.num
xor rdx, rdx
idiv rcx ; a.num % b.num
mov qword [rsi + 8], rdx ; a.num = a.num % b.num
mov rax, rdi
ret
.invalid:
xor rdi, rdi
call panic_abort
;; |a, b| {a < b}
p_lt_inner:
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rcx, rax ; a.addr()
mov rdi, rsi
call obj_tag_part
cmp al, OBJ_NUM
jne .invalid
call obj_addr_part
mov rdx, qword [rax + 8] ; b.num
mov rax, qword [rcx + 8] ; a.num
cmp rax, rdx
lea rax, [rel nil]
lea rcx, [rel atoms]
mov rdx, [rcx + ATOM_TRUE * 8] ; get the "t" atom
cmovl rax, rdx ; if a < b, return "t", else return nil
ret
.invalid:
xor rdi, rdi
call panic_abort
p_neq_inner:
call obj_eq
ret
p_lt:
call eval_list
mov rdi, rax
call get_cdr
mov rsi, rax
call get_car
mov rcx, rax
mov rdi, rsi
call get_car
mov rsi, rax
mov rdi, rcx
call p_lt_inner
ret
p_eq:
push rsi
call eval_list
mov rdi, rax
pop rsi
lea rdx, [rel p_neq_inner]
call any2_list ; list.any(|a, b| a != b)
test al, al ; 1 if any pair is unequual
lea rax, [rel nil]
lea rsi, [rel atoms]
cmovz rax, [rsi + ATOM_TRUE * 8] ; if no pair is unequal, return "t", else return nil
ret
;; sum list $rdi in env $rsi
p_add:
sub rsp, 24
mov qword [rsp + 8], rsi ; env
call eval_list
mov qword [rsp], rax ; evaled list
mov rdi, 16
mov rsi, 8
call alloc
mov rdi, rax
mov dword [rdi], 1 ; refcount = 1
mov rax, qword [rsp + 16] ; acc
mov qword [rdi + 8], rax ; value = acc
mov qword [rdi + 8], 0 ; value = 0
mov rsi, OBJ_NUM
call obj_set_tag
mov rcx, rax ; acc
mov rdi, qword [rsp] ; $rdi = evaled list
mov rsi, qword [rsp + 8] ; $rsi = env
lea rdx, [rel p_add_inner]
call fold_list
add rsp, 24
ret
.invalid:
xor rdi, rdi
call panic_abort
p_sub:
sub rsp, 24
mov qword [rsp + 8], rsi ; env
call eval_list
mov qword [rsp], rax ; evaled list
mov rdi, 16
mov rsi, 8
call alloc
mov rdi, rax
mov dword [rdi], 1 ; refcount = 1
mov qword [rdi + 8], 0 ; value = 0
mov rsi, OBJ_NUM
call obj_set_tag
mov rcx, rax ; acc
mov rdi, qword [rsp] ; $rdi = evaled list
mov rsi, qword [rsp + 8] ; $rsi = env
lea rdx, [rel p_sub_inner]
call fold_list
add rsp, 24
ret
p_mul:
sub rsp, 24
mov qword [rsp + 8], rsi ; env
call eval_list
mov qword [rsp], rax ; evaled list
mov rdi, 16
mov rsi, 8
call alloc
mov rdi, rax
mov dword [rdi], 1 ; refcount = 1
mov qword [rdi + 8], 1 ; value = 1
mov rsi, OBJ_NUM
call obj_set_tag
mov rcx, rax ; acc
mov rdi, qword [rsp] ; $rdi = evaled list
mov rsi, qword [rsp + 8] ; $rsi = env
lea rdx, [rel p_imul_inner]
call fold_list
add rsp, 24
ret
p_div:
sub rsp, 24
mov qword [rsp + 8], rsi ; env
call eval_list
mov qword [rsp], rax ; evaled list
mov rdi, qword [rsp] ; $rdi = evaled list
mov rsi, qword [rsp + 8] ; $rsi = env
lea rdx, [rel p_idiv_inner]
call reduce_list
add rsp, 24
ret
p_rem:
sub rsp, 24
mov qword [rsp + 8], rsi ; env
call eval_list
mov qword [rsp], rax ; evaled list
mov rdi, qword [rsp] ; $rdi = evaled list
mov rsi, qword [rsp + 8] ; $rsi = env
lea rdx, [rel p_irem_inner]
call reduce_list
add rsp, 24
ret
p_car:
call eval_list
mov rdi, rax
call get_car
mov rdi, rax
call get_car
ret
p_cdr:
call eval_list
mov rdi, rax
call get_car
mov rdi, rax
call get_cdr
ret
p_not:
call eval_list
mov rdi, rax
call get_car
lea rsi, [rel nil]
cmp rsi, rax
lea rax, qword [rel atoms]
mov rax, [rax + ATOM_TRUE * 8] ; get the "t" atom
cmovne rax, rsi
ret
p_quote:
call get_car
ret
global init_env
@ -170,6 +525,15 @@ init_env:
call cons ; ((t . t) . nil)
push rax
lea rdi, [rel atoms]
mov rdi, [rdi + ATOM_QUOTE * 8] ; get the "quote" atom
lea rsi, [rel p_quote]
call make_prim_pair ; ("quote" . p_add)
mov rdi, rax
mov rsi, qword [rsp]
call cons
mov qword [rsp], rax
lea rdi, [rel atoms]
mov rdi, [rdi + ATOM_PLUS * 8] ; get the "+" atom
lea rsi, [rel p_add]
@ -178,7 +542,87 @@ init_env:
mov rsi, qword [rsp]
call cons
mov qword [rsp], rax
lea rdi, [rel atoms]
mov rdi, [rdi + ATOM_MINUS * 8] ; get the "+" atom
lea rsi, [rel p_sub]
call make_prim_pair ; ("+" . p_add)
mov rdi, rax
mov rsi, qword [rsp]
call cons
mov qword [rsp], rax
lea rdi, [rel atoms]
mov rdi, [rdi + ATOM_MUL * 8] ; get the "*" atom
lea rsi, [rel p_mul]
call make_prim_pair ; ("*" . p_mul)
mov rdi, rax
mov rsi, qword [rsp]
call cons
mov qword [rsp], rax
lea rdi, [rel atoms]
mov rdi, [rdi + ATOM_DIV * 8] ; get the "/" atom
lea rsi, [rel p_div]
call make_prim_pair ; ("/" . p_div)
mov rdi, rax
mov rsi, qword [rsp]
call cons
mov qword [rsp], rax
lea rdi, [rel atoms]
mov rdi, [rdi + ATOM_REM * 8] ; get the "%" atom
lea rsi, [rel p_rem]
call make_prim_pair ; ("%" . p_rem)
mov rdi, rax
mov rsi, qword [rsp]
call cons
mov qword [rsp], rax
lea rdi, [rel atoms]
mov rdi, [rdi + ATOM_LT * 8] ; get the "<" atom
lea rsi, [rel p_lt]
call make_prim_pair ; ("<" . p_lt)
mov rdi, rax
mov rsi, qword [rsp]
call cons
mov qword [rsp], rax
lea rdi, [rel atoms]
mov rdi, [rdi + ATOM_EQ * 8] ; get the "=" atom
lea rsi, [rel p_eq]
call make_prim_pair ; ("=" . p_eq)
mov rdi, rax
mov rsi, qword [rsp]
call cons
mov qword [rsp], rax
lea rdi, [rel atoms]
mov rdi, [rdi + ATOM_CAR * 8] ; get the "car" atom
lea rsi, [rel p_car]
call make_prim_pair ; ("car" . p_car)
mov rdi, rax
mov rsi, qword [rsp]
call cons
mov qword [rsp], rax
lea rdi, [rel atoms]
mov rdi, [rdi + ATOM_CDR * 8] ; get the "cdr" atom
lea rsi, [rel p_cdr]
call make_prim_pair ; ("cdr" . p_cdr)
mov rdi, rax
mov rsi, qword [rsp]
call cons
mov qword [rsp], rax
lea rdi, [rel atoms]
mov rdi, [rdi + ATOM_NOT * 8] ; get the "not" atom
lea rsi, [rel p_not]
call make_prim_pair ; ("not" . p_cdr)
mov rdi, rax
mov rsi, qword [rsp]
call cons
mov qword [rsp], rax
pop rax
mov qword [rel env], rax ; env = ((t . t) . nil)
@ -225,19 +669,74 @@ init_atoms:
mov rsi, QUOTE_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx], rax ; atoms[0] = "quote"
mov qword [rcx], rax ; atoms[0] = "quote"
mov rdi, TRUE_STR
mov rsi, TRUE_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + 8], rax ; atoms[1] = "t"
mov qword [rcx + 8], rax ; atoms[1] = "t"
mov rdi, PLUS_STR
mov rsi, PLUS_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + 16], rax ; atoms[2] = "+"
mov qword [rcx + ATOM_PLUS * 8], rax ; atoms[2] = "+"
mov rdi, MINUS_STR
mov rsi, MINUS_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_MINUS * 8], rax ; atoms[3] = "-"
mov rdi, MUL_STR
mov rsi, MUL_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_MUL * 8], rax ; atoms[4] = "*"
mov rdi, DIV_STR
mov rsi, DIV_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_DIV * 8], rax ; atoms[5] = "/"
mov rdi, REM_STR
mov rsi, REM_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_REM * 8], rax ; atoms[6] = "%"
mov rdi, LT_STR
mov rsi, LT_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_LT * 8], rax ; atoms[7] = "<"
mov rdi, EQ_STR
mov rsi, EQ_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_EQ * 8], rax ; atoms[8] = "="
mov rdi, CAR_STR
mov rsi, CAR_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_CAR * 8], rax ; atoms[9] = "car"
mov rdi, CDR_STR
mov rsi, CDR_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_CDR * 8], rax ; atoms[10] = "cdr"
mov rdi, NOT_STR
mov rsi, NOT_STR_LEN
call make_atom
lea rcx, [rel atoms]
mov qword [rcx + ATOM_NOT * 8], rax ; atoms[11] = "not"
ret
@ -568,6 +1067,8 @@ parse_cur_token:
je .list
cmp byte [rel buf], "'"
je .quote
cmp byte [rel buf], `"`
je .string
pop rdi
call parse_atom
ret
@ -584,7 +1085,7 @@ parse_num:
push r12
sub rsp, 16
mov qword [rsp], 0 ; acc = 0
mov dword [rsp + 4], 10 ; radix = 10
mov dword [rsp + 8], 10 ; radix = 10
lea r12, [rel buf]
cmp byte [r12], '-' ; check for optional leading '-'
jne .skip_sign
@ -604,24 +1105,24 @@ parse_num:
test dil, dil
jz .done
movzx rsi, dword [rsp + 4] ; radix
movzx rsi, dword [rsp + 8] ; radix
call to_digit
test al, al
jz .done
mov rax, [rsp] ; acc
mov rcx, rdx ; digit
imul rax, rsi ; acc *= radix
add rax, rcx ; acc += digit
mov [rsp], rcx
inc r12 ; index++
mov rax, qword [rsp] ; acc
mov rcx, rdx ; digit
imul rax, rsi ; acc *= radix
add rax, rcx ; acc += digit
mov qword [rsp], rax
inc r12 ; index++
jmp .skip_radix
.done:
cmp byte [r12 + 1], 0
cmp byte [r12], 0
setz al
lea rcx, [rel buf]
sub r12, rcx ; r12 = count
mul rax, r12
mov rdx, [rsp] ; acc
mov rdx, qword [rsp] ; acc
add rsp, 16
pop r12
ret
@ -701,7 +1202,6 @@ parse_list:
mov rsi, rax ; $rsi = b
mov rdi, qword [rsp + 8] ; $rdi = (a . nil)
call set_cdr ; set_cdr((a . nil), b) => (a . b)
mov qword [rsp + 8], rax ; update the tail of the list to (a . b)
mov rdi, qword [rsp]
call next_token
cmp al, ')'
@ -949,7 +1449,7 @@ prepend:
;; returns 1 if the object in $rdi is nil, 0 otherwise
is_nil:
call obj_tag_part
cmp al, OBJ_NIL
setz al
lea rax, [rel nil]
cmp rdi, rax
sete al
ret

View file

@ -132,7 +132,7 @@ fn test_parse_list() {
let env = unsafe { *ENV_INIT };
println!("env: {:?}", env);
let input = b"(+ (+ 1 2) 3)";
let input = b"(not (< 3 2))";
let mut src = Source::from(&input[..]);
let sexp = unsafe { parse_next_token(&raw mut src) };
println!("{:?}", sexp);