2 ;; nasm -felf64 step7_quote.asm && ld step7_quote.o && ./a.out
4 ;; Calling convention: Address of input is in RSI
5 ;; Address of return value is in RAX
10 %include "types.asm" ; Data types, memory
11 %include "env.asm" ; Environment type
12 %include "system.asm" ; System calls
13 %include "reader.asm" ; String -> Data structures
14 %include "core.asm" ; Core functions
15 %include "printer.asm" ; Data structures -> String
16 %include "exceptions.asm" ; Error handling
20 ;; Top-level (REPL) environment
25 ;; ------------------------------------------
26 ;; Fixed strings for printing
28 static prompt_string
, db 10,"user> " ; The string to print at the prompt
30 static error_string
, db 27,'[31m',"Error",27,'[0m',": "
32 static not_found_string
, db " not found"
34 static def_missing_arg_string
, db "missing argument to def!",10
36 static def_expecting_symbol_string
, db "expecting symbol as first argument to def!",10
38 static let_missing_bindings_string
, db "let* missing bindings",10
40 static let_bindings_list_string
, db "let* expected a list or vector of bindings",10
42 static let_bind_symbol_string
, db "let* expected a symbol in bindings list",10
44 static let_bind_value_string
, db "let* missing value in bindings list",10
46 static let_missing_body_string
, db "let* missing body",10
47 static eval_list_not_function
, db "list does not begin with a function",10
49 static if_missing_condition_string
, db "missing condition in if expression",10
51 ;; Symbols used for comparison
53 static_symbol def_symbol
, 'def!'
54 static_symbol let_symbol
, 'let*'
55 static_symbol do_symbol
, 'do'
56 static_symbol if_symbol
, 'if'
57 static_symbol fn_symbol
, 'fn*'
59 static_symbol argv_symbol
, '*ARGV*'
61 static_symbol quote_symbol
, 'quote'
62 static_symbol quasiquote_symbol
, 'quasiquote'
63 static_symbol unquote_symbol
, 'unquote'
64 static_symbol splice_unquote_symbol
, 'splice-unquote'
65 static_symbol concat_symbol
, 'concat'
66 static_symbol cons_symbol
, 'cons'
68 ;; Startup string. This is evaluated on startup
69 static mal_startup_string
, db "(do (def! not (fn* (a) (if a false true))) (def! load-file (fn* (f) (eval (read-string (str ",34,"(do",34," (slurp f) ",34,")",34," ))))) )"
71 ;; Command to run, appending the name of the script to run
72 static run_script_string
, db "(load-file ",34
75 ;; ----------------------------------------------
78 ;; Inputs: RSI Form to evaluate
82 mov r15
, rdi
; Save Env in r15
87 ; Check if this is a list
89 and ah, container_mask
90 cmp ah, container_list
96 cmp ah, container_vector
99 ; Not a list, map or vector
100 cmp ah, container_symbol
103 ; Not a symbol, list, map or vector
104 call incref_object
; Increment reference count
110 ; Check if first character of symbol is ':'
111 mov al, BYTE [rsi
+ Array.data
]
115 ; look in environment
118 ; symbol is the key in rdi
122 je .done
; result in RAX
124 ; Not found, throw an error
126 print_str_mac error_string
; print 'Error: '
130 mov edx, [rsi
+ Array.
length]
132 call print_rawstring
; print symbol
134 print_str_mac not_found_string
; print ' not found'
139 ; ------------------------------
142 ; Just return keywords unaltered
147 ; ------------------------------
149 ; Evaluate each element of the list
151 xor r8
, r8
; The list to return
152 ; r9 contains head of list
155 mov al, BYTE [rsi
] ; Check type
158 cmp ah, content_pointer
161 ; A value in RSI, so copy
166 add bl, (block_cons
+ container_list
)
167 mov [rax
], BYTE bl ; set type
168 mov rbx
, [rsi
+ Cons.car
]
169 mov [rax
+ Cons.car
], rbx
; copy value
175 ; List element is a pointer to something
180 mov rdi
, [rsi
+ Cons.car
] ; Get the address
183 call incref_object
; Environment increment refs
184 xchg rsi
, rdi
; Env in RDI, AST in RSI
186 call incref_object
; AST increment refs
188 call eval
; Evaluate it, result in rax
194 ; Check the type it's evaluated to
197 and bh, (block_mask
+ container_mask
)
198 cmp bh, (block_cons
+ container_value
)
201 ; Not a value, so need a pointer to it
204 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
205 pop rbx
; Address to point to
206 mov [rax
+ Cons.car
], rbx
210 ; Got value in RAX, so copy
212 call alloc_cons
; Copy in RAX
213 pop rbx
; Value to copy in RBX
216 or cl, (block_cons
+ container_list
)
217 mov [rax
], BYTE cl ; set type
218 mov rcx
, [rbx
+ Cons.car
]
219 mov [rax
+ Cons.car
], rcx
; copy value
221 ; Release the value in RBX
229 ; Fall through to .list_append
233 cmp r8
, 0 ; Check if this is the first
237 mov [r9
+ Cons.cdr
], rax
238 mov [r9
+ Cons.typecdr
], BYTE content_pointer
245 ; fall through to .list_next
248 ; Check if there's another
249 mov al, BYTE [rsi
+ Cons.typecdr
]
250 cmp al, content_pointer
251 jne .list_done
; finished list
252 mov rsi
, [rsi
+ Cons.cdr
] ; next in list
256 mov rax
, r8
; Return the list
259 ; ---------------------
261 ; Create a new map, evaluating all the values
263 ; Check if the map is empty
264 cmp al, maltype_empty_map
267 ; map empty. Just return it
274 mov r10
, rsi
; input in R10
275 xor r12
, r12
; New map in r12
277 ; Now loop through each key-value pair
278 ; NOTE: This method relies on the implementation
283 call alloc_cons
; New Cons in RAX
285 mov bl, [r10
+ Cons.typecar
] ; Type in BL
286 mov [rax
+ Cons.typecar
], bl
287 mov rcx
, [r10
+ Cons.car
] ; Value in RCX
288 mov [rax
+ Cons.car
], rcx
290 ; Check the type of the key
292 cmp bl, content_pointer
293 jne .map_got_key
; a value
295 ; a pointer, so increment reference count
296 mov bx, WORD [rcx
+ Cons.refcount
]
298 mov [rcx
+ Cons.refcount
], WORD bx
310 ; Appending to previous value in r13
311 mov [r13
+ Cons.typecdr
], BYTE content_pointer
312 mov [r13
+ Cons.cdr
], rax
316 ; Check that we have a value
317 mov al, BYTE [r10
+ Cons.typecdr
]
318 cmp al, content_pointer
319 jne .map_error_missing_value
320 mov r10
, [r10
+ Cons.cdr
]
322 ; Now got value in r10
324 ; Check the type of the value
325 mov bl, [r10
+ Cons.typecar
] ; Type in BL
327 cmp bl, content_pointer
328 je .map_value_pointer
330 ; Not a pointer, so make a copy
332 mov bl, [r10
+ Cons.typecar
]
333 mov [rax
+ Cons.typecar
], bl
334 mov rcx
, [r10
+ Cons.car
]
335 mov [rax
+ Cons.car
], rcx
339 ; A pointer, so need to evaluate
341 push r12
; start of result
342 push r13
; Current head of result
344 mov rsi
, [r10
+ Cons.car
] ; Get the address
348 call incref_object
; Environment increment refs
353 call eval
; Evaluate it, result in rax
359 ; Check the type it's evaluated to
362 and bh, (block_mask
+ container_mask
)
363 cmp bh, (block_cons
+ container_value
)
365 jne .map_eval_pointer
367 ; A value, so just change the type to a map
369 add bl, (block_cons
+ container_map
)
374 ; Not a value, so need a pointer to it
377 mov [rax
], BYTE (block_cons
+ container_map
+ content_pointer
)
378 pop rbx
; Address to point to
379 mov [rax
+ Cons.car
], rbx
382 ; Append RAX to list in R13
383 mov [r13
+ Cons.typecdr
], BYTE content_pointer
384 mov [r13
+ Cons.cdr
], rax
387 ; Check if there's another key
388 mov al, BYTE [r10
+ Cons.typecdr
]
389 cmp al, content_pointer
390 jne .map_done
; finished map
391 mov r10
, [r10
+ Cons.cdr
] ; next in map
398 .
map_error_missing_value:
402 ; ------------------------------
404 ; Evaluate each element of the vector
406 xor r8
, r8
; The vector to return
407 ; r9 contains head of vector
410 mov al, BYTE [rsi
] ; Check type
413 cmp ah, content_pointer
420 add bl, (block_cons
+ container_vector
)
421 mov [rax
], BYTE bl ; set type
422 mov rbx
, [rsi
+ Cons.car
]
423 mov [rax
+ Cons.car
], rbx
; copy value
429 ; Vector element is a pointer to something
434 mov rsi
, [rsi
+ Cons.car
] ; Get the address
438 call incref_object
; Environment increment refs
443 call eval
; Evaluate it, result in rax
449 ; Check the type it's evaluated to
452 and bh, (block_mask
+ container_mask
)
453 cmp bh, (block_cons
+ container_value
)
454 je .vector_eval_value
456 ; Not a value, so need a pointer to it
459 mov [rax
], BYTE (block_cons
+ container_vector
+ content_pointer
)
460 pop rbx
; Address to point to
461 mov [rax
+ Cons.car
], rbx
465 ; Got value in RAX, so copy
467 call alloc_cons
; Copy in RAX
468 pop rbx
; Value to copy in RBX
471 or cl, (block_cons
+ container_vector
)
472 mov [rax
], BYTE cl ; set type
473 mov rcx
, [rbx
+ Cons.car
]
474 mov [rax
+ Cons.car
], rcx
; copy value
476 ; Release the value in RBX
487 cmp r8
, 0 ; Check if this is the first
491 mov [r9
+ Cons.cdr
], rax
492 mov [r9
+ Cons.typecdr
], BYTE content_pointer
499 ; fall through to .vector_next
502 ; Check if there's another
503 mov al, BYTE [rsi
+ Cons.typecdr
]
504 cmp al, content_pointer
505 jne .vector_done
; finished vector
506 mov rsi
, [rsi
+ Cons.cdr
] ; next in vector
510 mov rax
, r8
; Return the vector
513 ; ---------------------
519 ;; Comparison of symbols for eval function
520 ;; Compares the symbol in RSI with specified symbol
521 ;; Preserves RSI and RBX
523 %macro eval_cmp_symbol
1
528 call compare_char_array
531 test rax
, rax
; ZF set if rax = 0 (equal)
534 ;; ----------------------------------------------------
537 ;; Input: RSI AST to evaluate [ Released ]
538 ;; RDI Environment [ Released ]
540 ;; Returns: Result in RAX
542 ;; Note: Both the form and environment will have their reference count
543 ;; reduced by one (released). This is for tail call optimisation (Env),
544 ;; quasiquote and macroexpand (AST)
549 push rsi
; AST pushed, must be popped before return
553 cmp al, maltype_empty_list
554 je .empty_list
; empty list, return unchanged
556 and al, container_mask
557 cmp al, container_list
560 ; Not a list. Evaluate and return
562 jmp .return
; Releases Env
564 ; --------------------
568 ; Check if the first element is a symbol
572 cmp al, content_pointer
575 mov rbx
, [rsi
+ Cons.car
]
577 cmp al, maltype_symbol
580 ; Is a symbol, address in RBX
582 ; Compare against special form symbols
584 eval_cmp_symbol def_symbol
; def!
587 eval_cmp_symbol let_symbol
; let*
590 eval_cmp_symbol do_symbol
; do
593 eval_cmp_symbol if_symbol
; if
596 eval_cmp_symbol fn_symbol
; fn
599 eval_cmp_symbol quote_symbol
; quote
602 eval_cmp_symbol quasiquote_symbol
; quasiquote
603 je .quasiquote_symbol
609 ; -----------------------------
612 ; Define a new symbol in current environment
614 ; Next item should be a symbol
615 mov al, BYTE [rsi
+ Cons.typecdr
]
616 cmp al, content_pointer
617 jne .def_error_missing_arg
618 mov rsi
, [rsi
+ Cons.cdr
]
620 ; Now should have a symbol
622 mov al, BYTE [rsi
+ Cons.typecar
]
624 cmp al, content_pointer
625 jne .def_error_expecting_symbol
626 mov r8
, [rsi
+ Cons.car
] ; Symbol (?)
629 cmp al, maltype_symbol
630 jne .def_error_expecting_symbol
632 ; R8 now contains a symbol
634 ; expecting a value or pointer next
635 mov al, BYTE [rsi
+ Cons.typecdr
]
636 cmp al, content_pointer
637 jne .def_error_missing_arg
638 mov rsi
, [rsi
+ Cons.cdr
]
640 ; Check if this is a pointer
644 cmp ah, content_pointer
650 pop rbx
; BL now contains type
652 add bl, (block_cons
+ container_value
)
654 mov rcx
, [rsi
+ Cons.car
]
655 mov [rax
+ Cons.car
], rcx
661 ; A pointer, so evaluate
663 ; This may throw an error, so define a handler
667 mov rsi
, [rsi
+ Cons.car
] ; Pointer
671 call incref_object
; Environment increment refs
672 xchg rsi
, rdi
; since it will be decremented by eval
674 call incref_object
; AST increment refs
683 ; Symbol in R8, value in RSI
684 mov rdi
, r8
; key (symbol)
686 mov rsi
, r15
; Environment
692 .
def_error_missing_arg:
693 mov rsi
, def_missing_arg_string
694 mov rdx
, def_missing_arg_string.len
695 jmp .def_handle_error
697 .
def_error_expecting_symbol:
698 mov rsi
, def_expecting_symbol_string
699 mov rdx
, def_expecting_symbol_string.len
700 jmp .def_handle_error
705 print_str_mac error_string
; print 'Error: '
709 call print_rawstring
; print message
711 xor rsi
, rsi
; no object to throw
712 jmp error_throw
; No return
714 ; -----------------------------
716 ; Create a new environment
718 mov r11
, rsi
; Let form in R11
720 mov rsi
, r15
; Outer env
721 call env_new
; Increments R15's ref count
722 mov r14
, rax
; New environment in R14
725 call release_object
; Decrement R15 ref count
727 ; Second element should be the bindings
729 mov al, BYTE [r11
+ Cons.typecdr
]
730 cmp al, content_pointer
731 jne .let_error_missing_bindings
732 mov r11
, [r11
+ Cons.cdr
]
736 cmp al, content_pointer
737 jne .let_error_bindings_list
739 mov r12
, [r11
+ Cons.car
] ; should be bindings list
741 and al, (block_mask
+ container_mask
)
742 ; Can be either a list or vector
743 cmp al, block_cons
+ container_list
745 cmp al, block_cons
+ container_vector
748 ; Not a list or vector
749 jmp .let_error_bindings_list
752 ; R12 now contains a list with an even number of items
753 ; The first should be a symbol, then a value to evaluate
758 cmp al, content_pointer
759 jne .let_error_bind_symbol
761 mov r13
, [r12
+ Cons.car
] ; Symbol (?)
763 cmp al, maltype_symbol
764 jne .let_error_bind_symbol
766 ; R13 now contains a symbol to bind
767 ; The next item in the bindings list (R12)
768 ; should be a value or expression to evaluate
770 mov al, BYTE [r12
+ Cons.typecdr
]
772 cmp al, content_pointer
773 jne .let_error_bind_value
774 mov r12
, [r12
+ Cons.cdr
]
778 ; Check the type of the value
779 mov bl, [r12
+ Cons.typecar
] ; Type in BL
781 cmp bl, content_pointer
782 je .let_value_pointer
784 ; Not a pointer, so make a copy
786 mov bl, [r12
+ Cons.typecar
]
788 ;or bl, (block_cons + container_value) ; 0
789 mov [rax
+ Cons.typecar
], bl
790 mov rcx
, [r12
+ Cons.car
]
791 mov [rax
+ Cons.car
], rcx
796 ; A pointer, so need to evaluate
797 push r11
; let* form list
798 push r12
; Position in bindings list
799 push r13
; symbol to bind
800 push r14
; new environment
806 mov rsi
, [r12
+ Cons.car
] ; Get the address
808 call incref_object
; Increment ref count of AST
810 call eval
; Evaluate it, result in rax
824 mov rsi
, rcx
; The value
827 ; Check if there are more bindings
828 mov al, BYTE [r12
+ Cons.typecdr
]
829 cmp al, content_pointer
830 jne .let_done_binding
831 mov r12
, [r12
+ Cons.cdr
] ; Next
836 ; Evaluate next item in let* form in new environment
838 mov al, BYTE [r11
+ Cons.typecdr
]
839 cmp al, content_pointer
840 jne .let_error_missing_body
841 mov r11
, [r11
+ Cons.cdr
] ; Now contains value to evaluate
842 ; Check type of the value
844 and al, block_mask
+ content_mask
845 cmp al, content_pointer
848 ; Just a value, so copy
852 mov [rax
], BYTE bl ; set type
853 mov rbx
, [r11
+ Cons.car
]
854 mov [rax
+ Cons.car
], rbx
; copy value
858 ; Evaluate using new environment
860 mov rsi
, [r11
+ Cons.car
] ; Object pointed to
861 call incref_object
; will be released by eval
863 mov r11
, rsi
; save new AST
866 mov rsi
, r11
; New AST
868 mov rdi
, r14
; New environment
871 ; Note: eval will release the new environment on return
874 ; Release the new environment
885 ret ; already released env
887 .
let_error_missing_bindings:
888 mov rsi
, let_missing_bindings_string
889 mov rdx
, let_missing_bindings_string.len
890 jmp .let_handle_error
892 .
let_error_bindings_list: ; expected a list or vector, got something else
893 mov rsi
, let_bindings_list_string
894 mov rdx
, let_bindings_list_string.len
895 jmp .let_handle_error
897 .
let_error_bind_symbol: ; expected a symbol, got something else
898 mov rsi
, let_bind_symbol_string
899 mov rdx
, let_bind_symbol_string.len
900 jmp .let_handle_error
902 .
let_error_bind_value: ; Missing value in binding list
903 mov rsi
, let_bind_value_string
904 mov rdx
, let_bind_value_string.len
905 jmp .let_handle_error
907 .
let_error_missing_body: ; Missing body to evaluate
908 mov rsi
, let_missing_body_string
909 mov rdx
, let_missing_body_string.len
910 jmp .let_handle_error
913 push r11
; For printing later
918 print_str_mac error_string
; print 'Error: '
922 call print_rawstring
; print message
925 jmp error_throw
; No return
927 ; -----------------------------
930 mov r11
, rsi
; do form in RSI
933 ; Check if there is a body
934 mov al, BYTE [r11
+ Cons.typecdr
]
935 cmp al, content_pointer
936 jne .do_no_body
; error
938 mov r11
, [r11
+ Cons.cdr
] ; Body in R11
942 ; Need to test if this is the last form
943 ; so we can handle tail call
945 mov bl, BYTE [r11
+ Cons.typecdr
]
946 cmp bl, content_pointer
947 jne .do_body_last
; Last expression
949 ; not the last expression
951 ; Check if this is a value or pointer
953 and al, block_mask
+ content_mask
954 cmp al, content_pointer
955 jne .do_next
; A value, so skip
957 ; A pointer, so evaluate
963 call incref_object
; Increase Env reference
964 ; since eval will release Env
966 mov rsi
, [r11
+ Cons.car
] ; Form
967 call incref_object
; Increment ref count since eval will release
970 call eval
; Result in RAX
972 ; Another form after this.
973 ; Discard the result of the last eval
981 mov r11
, [r11
+ Cons.cdr
] ; Next in list
986 ; The last form is in R11, which will be returned
988 ; Check if this is a value or pointer
990 and al, block_mask
+ content_mask
991 cmp al, content_pointer
992 jne .do_body_value_return
993 jmp .do_body_expr_return
995 .
do_body_value_return:
996 ; Got a value as last form (in R11).
999 push rax
; Type of value to return
1005 ; Allocate a Cons object to hold value
1007 pop rbx
; type in BL
1009 mov rbx
, [r11
+ Cons.car
]
1010 mov [rax
+ Cons.car
], rbx
1014 mov r15
, rax
; not modified by release
1020 .
do_body_expr_return:
1021 ; An expression to evaluate as the last form
1022 ; Tail call optimise, jumping to eval
1023 ; Don't increment Env reference count
1025 mov rsi
, [r11
+ Cons.car
] ; new AST form
1026 call incref_object
; This will be released by eval
1028 mov r11
, rsi
; Save new AST
1029 pop rsi
; Remove old AST from stack
1034 jmp eval
; Tail call
1037 ; No expressions to evaluate. Return nil
1040 call release_object
; Release Env
1047 mov [rax
], BYTE maltype_nil
1048 mov [rax
+ Cons.typecdr
], BYTE content_nil
1051 ; -----------------------------
1054 mov r11
, rsi
; if form in R11
1055 ; Environment in R15
1057 mov al, BYTE [r11
+ Cons.typecdr
]
1058 cmp al, content_pointer
1059 jne .if_no_condition
1061 mov r11
, [r11
+ Cons.cdr
] ; Should be a condition
1063 ; Check if value or pointer
1065 and al, content_mask
1066 cmp al, content_pointer
1069 ; A pointer, so evaluate
1075 call incref_object
; Increase Env reference
1077 mov rsi
, [r11
+ Cons.car
] ; Form
1078 call incref_object
; Increase Form/AST ref count
1081 call eval
; Result in RAX
1085 ; Get type of result
1097 cmp bl, maltype_false
1107 cmp al, content_false
1113 ; Skip the next item
1114 mov al, BYTE [r11
+ Cons.typecdr
]
1115 cmp al, content_pointer
1118 mov r11
, [r11
+ Cons.cdr
]
1121 ; Get the next item in the list and evaluate it
1122 mov al, BYTE [r11
+ Cons.typecdr
]
1123 cmp al, content_pointer
1124 jne .return_nil
; Nothing to return
1126 mov r11
, [r11
+ Cons.cdr
]
1128 ; Check if value or pointer
1130 and al, content_mask
1131 cmp al, content_pointer
1138 and bl, content_mask
1140 mov rbx
, [r11
+ Cons.car
]
1141 mov [rax
+ Cons.car
], rbx
1146 mov rsi
, [r11
+ Cons.car
] ; Form
1147 call incref_object
; Will be released by eval
1151 call release_object
; Release old AST
1152 mov rsi
, r11
; New AST
1155 jmp eval
; Tail call
1157 .
if_no_condition: ; just (if) without a condition
1159 print_str_mac error_string
1160 print_str_mac if_missing_condition_string
1162 ; Release environment
1165 xor rsi
, rsi
; No object to throw
1170 mov [rax
], BYTE maltype_nil
1171 mov [rax
+ Cons.typecdr
], BYTE content_nil
1174 ; Release environment
1176 mov r15
, rax
; Save RAX (return value)
1180 pop rsi
; Pushed at start of eval
1183 mov rax
, r15
; return value
1186 ; -----------------------------
1189 mov r11
, rsi
; fn form in R11
1190 ; Environment in R15
1192 ; Get the binds and body of the function
1193 mov al, BYTE [r11
+ Cons.typecdr
]
1194 cmp al, content_pointer
1197 mov r11
, [r11
+ Cons.cdr
]
1199 and al, content_mask
1200 cmp al, content_pointer
1201 jne .fn_binds_not_list
1203 mov r12
, [r11
+ Cons.car
] ; Should be binds list
1205 and al, (block_mask
+ container_mask
)
1206 cmp al, (block_cons
+ container_list
)
1207 je .fn_got_binds
; Can be list
1208 cmp al, (block_cons
+ container_vector
)
1209 je .fn_got_binds
; or vector
1210 jmp .fn_binds_not_list
1214 ; Next get the body of the function
1215 mov al, BYTE [r11
+ Cons.typecdr
]
1216 cmp al, content_pointer
1219 mov r11
, [r11
+ Cons.cdr
]
1220 ; Check value or pointer
1222 and al, content_mask
1223 cmp al, content_pointer
1224 jne .fn_is_value
; Body in r11
1225 mov r11
, [r11
+ Cons.car
]
1229 ; Body is just a value, no expression
1230 mov [r11
], BYTE al ; Mark as value, not list
1234 ; Now put into function type
1235 ; Addr is "apply_fn", the address to call
1241 mov [rax
], BYTE (block_cons
+ container_function
+ content_function
)
1243 mov [rax
+ Cons.car
], rbx
; Address of apply function
1244 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1246 mov r13
, rax
; Return list in R13
1249 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1250 mov [rax
+ Cons.car
], r15
; Environment
1251 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1253 mov [r13
+ Cons.cdr
], rax
; Append to list
1264 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1265 mov [rax
+ Cons.car
], r12
; Binds list
1266 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1268 mov [r14
+ Cons.cdr
], rax
; Append to list
1277 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1278 mov [rax
+ Cons.car
], r11
; Body of function
1280 mov [r14
+ Cons.cdr
], rax
1293 mov [rax
], BYTE maltype_nil
1294 mov [rax
+ Cons.typecdr
], BYTE content_nil
1297 ; -----------------------------
1300 ; Just return the arguments in rsi cdr
1302 mov al, BYTE [rsi
+ Cons.typecdr
]
1303 cmp al, content_pointer
1304 jne .return_nil
; quote empty, so return nil
1306 mov rsi
, [rsi
+ Cons.cdr
]
1308 ; Check if this is a value or pointer
1309 mov al, BYTE [rsi
+ Cons.typecar
]
1310 and al, content_mask
1311 cmp al, content_pointer
1314 ; RSI contains a value. Remove the list container
1315 mov [rsi
+ Cons.typecar
], BYTE al
1321 ; RSI contains a pointer, so get the object pointed to
1322 mov rsi
, [rsi
+ Cons.car
]
1327 ; -----------------------------
1330 ; call quasiquote function with first argument
1332 mov al, BYTE [rsi
+ Cons.typecdr
]
1333 cmp al, content_pointer
1334 jne .return_nil
; quasiquote empty, so return nil
1336 mov r11
, rsi
; Save original AST in R11
1338 mov rsi
, [rsi
+ Cons.cdr
]
1340 ; Check if this is a value or pointer
1341 mov al, BYTE [rsi
+ Cons.typecar
]
1342 and al, content_mask
1343 cmp al, content_pointer
1344 je .quasiquote_pointer
1346 ; RSI contains a value. Remove the list container
1347 mov [rsi
+ Cons.typecar
], BYTE al
1352 .
quasiquote_pointer:
1353 ; RSI contains a pointer, so get the object pointed to
1354 mov rsi
, [rsi
+ Cons.car
]
1356 push r15
; Environment
1357 ; Original AST already on stack
1361 pop rdi
; Environment
1364 mov r11
, rax
; New AST
1365 call release_object
; Release old AST
1366 mov rsi
, r11
; New AST in RSI
1368 jmp eval
; Tail call
1370 ; -----------------------------
1374 mov rdi
, r15
; Environment
1376 call eval_ast
; List of evaluated forms in RAX
1381 ; This point can be called to run a function
1384 ; Inputs: RAX - List with function as first element
1385 ; NOTE: This list is released
1387 ; Check that the first element of the return is a function
1389 and bl, content_mask
1390 cmp bl, content_pointer
1391 jne .list_not_function
1393 mov rbx
, [rax
+ Cons.car
] ; Get the address
1395 cmp cl, maltype_function
1396 jne .list_not_function
1398 ; Check the rest of the args
1399 mov cl, BYTE [rax
+ Cons.typecdr
]
1400 cmp cl, content_pointer
1405 push rbx
; Function object
1407 mov rsi
, rax
; List with function first
1408 call release_object
; Can be freed now
1410 ; Create an empty list for the arguments
1412 mov [rax
], BYTE maltype_empty_list
1416 jmp .list_function_call
1418 mov rsi
, [rax
+ Cons.cdr
] ; Rest of list
1419 .
list_function_call:
1420 ; Call the function with the rest of the list in RSI
1422 mov rdx
, rax
; List to release
1423 mov rdi
, rbx
; Function object in RDI
1425 mov rbx
, [rbx
+ Cons.car
] ; Call function
1427 je apply_fn_jmp
; Jump to user function apply
1429 ; A built-in function, so call (no recursion)
1437 pop rsi
; eval'ed list
1442 jmp .return
; Releases Env
1445 ; Not a function. Probably an error
1451 print_str_mac error_string
1452 print_str_mac eval_list_not_function
1460 ;; Applies a user-defined function
1462 ;; Input: RSI - Arguments to bind
1463 ;; RDI - Function object
1464 ;; RDX - list to release after binding
1465 ;; R15 - Env (will be released)
1466 ;; R13 - AST released before return
1469 ;; Output: Result in RAX
1471 ;; This is jumped to from eval, so if it returns
1472 ;; then it will return to the caller of eval, not to eval
1474 ; This is jumped to from eval with AST on the stack
1478 ; Extract values from the list in RDI
1479 mov rax
, [rdi
+ Cons.cdr
]
1480 mov rsi
, [rax
+ Cons.car
] ; Env
1481 mov rax
, [rax
+ Cons.cdr
]
1482 mov rdi
, [rax
+ Cons.car
] ; Binds
1483 mov rax
, [rax
+ Cons.cdr
]
1484 mov rax
, [rax
+ Cons.car
] ; Body
1487 ; Check the type of the body
1489 and bl, block_mask
+ container_mask
1491 ; Just a value (in RAX). No eval needed
1493 mov r14
, rax
; Save return value in R14
1498 ; Release the list passed in RDX
1502 ; Release the environment
1513 ; Create a new environment, binding arguments
1516 mov r14
, r13
; Old AST. R13 used by env_new_bind
1522 mov rdi
, rax
; New environment in RDI
1524 ; Note: Need to increment the reference count
1525 ; of the function body before releasing anything,
1526 ; since if the function was defined in-place (lambda)
1527 ; then the body may be released early
1530 call incref_object
; Will be released by eval
1531 mov r8
, rsi
; Body in R8
1533 ; Release the list passed in RDX
1537 ; Release the environment
1541 ; Release the old AST
1547 jmp eval
; Tail call
1548 ; The new environment (in RDI) will be released by eval
1551 ;; Set ZF if RSI is a non-empty list or vector
1552 ;; Modifies RAX, does not modify RSI
1556 jnz .false
; Not a Cons
1557 cmp al, maltype_empty_list
1558 je .false
; Empty list
1559 cmp al, maltype_empty_vector
1560 je .false
; Empty vector
1562 ; Something non empty
1563 and al, container_mask
1564 cmp al, container_list
1566 cmp al, container_vector
1568 ; Not a list or vector -> false
1572 and ah, 255-64 ; clear zero flag
1577 or ah, 64 ; set zero flag
1581 ;; Called by eval with AST in RSI [ modified ]
1582 ;; Returns new AST in RAX
1584 ; i. Check if AST is an empty list
1588 ; ii. Check if the first element of RSI is the symbol
1592 and al, content_mask
1593 cmp al, content_pointer
1594 jne .not_unquote
; Not a pointer
1596 mov rdi
, [rsi
+ Cons.car
] ; Get the pointer
1598 cmp cl, maltype_symbol
1601 ; Compare against 'unquote'
1605 mov rsi
, unquote_symbol
1606 call compare_char_array
1615 ; iii. Handle splice-unquote
1616 ; RSI -> ( ( splice-unquote ? ) ? )
1618 ; Test if RSI contains a pointer
1620 cmp al, content_pointer
1623 mov rbx
, [rsi
+ Cons.car
] ; Get the object pointer
1625 ; RBX -> ( splice-unquote ? )
1630 jne .not_splice
; First element not a pair
1632 ; Check if this list in RBX starts with 'splice-unquote' symbol
1634 and al, content_mask
1635 cmp al, content_pointer
1639 mov rdi
, [rbx
+ Cons.car
] ; Get the pointer
1641 cmp al, maltype_symbol
1647 ; Compare against 'splice-unquote'
1648 mov rsi
, splice_unquote_symbol
1649 call compare_char_array
1659 ; iv. Cons first and rest of AST in RSI
1661 ; check if pointer or value
1663 and cl, content_mask
1664 cmp cl, content_pointer
1669 or cl, container_list
1670 mov [rax
], BYTE cl ; List + Content
1671 mov rbx
, [rsi
+ Cons.car
]
1672 mov [rax
+ Cons.car
], rbx
1677 ; Get the pointer and call quasiquote
1679 mov rsi
, [rsi
+ Cons.car
]
1685 mov [rax
], BYTE (container_list
+ content_pointer
)
1686 mov [rax
+ Cons.car
], rcx
1690 ; Have Cons with first object in RCX
1692 ; Call quasiquote on the rest of the AST
1693 ; Check if this is the end of the list
1694 mov al, BYTE [rsi
+ Cons.typecdr
]
1695 cmp al, content_pointer
1698 mov rsi
, [rsi
+ Cons.cdr
] ; Rest of the list
1700 call incref_object
; Will release after quasiquote call
1702 jmp .cons_quasiquote_ast
1705 ; End of the AST, so make an empty list
1707 mov [rax
], BYTE maltype_empty_list
1710 .
cons_quasiquote_ast:
1714 mov rdx
, rax
; List in RDX
1717 call release_object
; Release input
1719 pop rcx
; Value in RCX
1722 ; Work from the end of the list to the front
1725 mov [rax
], BYTE (container_list
+ content_pointer
)
1726 mov [rax
+ Cons.car
], rdx
; The rest of AST
1728 ; Link to the RCX Cons
1729 mov [rcx
+ Cons.typecdr
], BYTE content_pointer
1730 mov [rcx
+ Cons.cdr
], rax
1733 call alloc_cons
; Cons for cons symbol
1734 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1735 mov [rax
+ Cons.cdr
], rdx
1738 ; Get the cons symbol
1739 mov rsi
, cons_symbol
1742 mov [rdx
], BYTE (container_list
+ content_pointer
)
1743 mov [rdx
+ Cons.car
], rsi
1749 ; Return (quote RSI)
1751 call incref_object
; RSI reference count
1755 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
1756 mov [rax
+ Cons.car
], rsi
1759 ; Cons for quote symbol
1762 mov [rbx
+ Cons.typecdr
], BYTE content_pointer
1763 mov [rbx
+ Cons.cdr
], rsi
1765 ; Get a quote symbol, incrementing references
1766 mov rsi
, quote_symbol
1769 ; Put into the Cons in RBX
1770 mov [rbx
+ Cons.car
], rsi
1771 mov [rbx
], BYTE (block_cons
+ container_list
+ content_pointer
)
1774 ; -----------------------
1778 ; Got unquote symbol. Return second element of RSI
1779 mov al, BYTE [rsi
+ Cons.typecdr
]
1780 cmp al, content_pointer
1781 jne .empty_list
; No second element
1783 mov rsi
, [rsi
+ Cons.cdr
]
1785 ; Check if it's a value or pointer
1787 and cl, content_mask
1788 cmp cl, content_pointer
1791 ; A value, so need a new Cons
1793 mov [rax
], BYTE cl ; content
1794 mov rbx
, [rsi
+ Cons.car
]
1795 mov [rax
+ Cons.car
], rbx
; Copy content
1799 mov rsi
, [rsi
+ Cons.car
]
1804 ; -----------------------
1806 ; RSI -> ( RBX->( splice-unquote A ) B )
1808 ; RBX Car points to splice-unquote symbol
1810 ; Check if there is anything after the symbol
1811 mov al, BYTE [rbx
+ Cons.typecdr
]
1812 cmp al, content_pointer
1813 jne .splice_unquote_empty
1815 ; Point to the second element of the splice-unquote list
1816 mov rcx
, [rbx
+ Cons.cdr
]
1818 ; Check whether it's a value or pointer
1820 and al, content_mask
1821 cmp al, content_pointer
1822 je .splice_unquote_pointer
1824 ; A value, so change the container to a value
1826 ; Remove pointer from RBX
1827 mov [rbx
+ Cons.typecdr
], BYTE 0
1828 jmp .splice_unquote_first
; Got the value in RCX
1830 .
splice_unquote_pointer:
1831 mov rcx
, [rcx
+ Cons.car
] ; Get the object pointed to
1834 xchg rcx
, rsi
; Object in RCX
1836 .
splice_unquote_first: ; Got the first object in RCX
1838 ; Check if RSI contains anything else
1839 mov al, BYTE [rsi
+ Cons.typecdr
]
1840 cmp al, content_pointer
1841 jne .splice_unquote_notail
1843 mov rsi
, [rsi
+ Cons.cdr
]
1846 ; ( ( splice-unquote A ) B )
1848 ; Need to call quasiquote on the rest of the list
1853 ; Need to concat rcx and rdx
1854 ; Work from the end of the list to the front
1857 mov [rax
], BYTE (container_list
+ content_pointer
)
1858 mov [rax
+ Cons.car
], rdx
; The rest of AST
1859 mov rdx
, rax
; Push list into RDX
1862 mov [rax
], BYTE (container_list
+ content_pointer
)
1863 mov [rax
+ Cons.car
], rcx
; The splice-unquote object
1864 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1865 mov [rax
+ Cons.cdr
], rdx
1868 call alloc_cons
; Cons for concat symbol
1869 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1870 mov [rax
+ Cons.cdr
], rdx
1873 ; Get the concat symbol
1874 mov rsi
, concat_symbol
1877 mov [rdx
], BYTE (container_list
+ content_pointer
)
1878 mov [rdx
+ Cons.car
], rsi
1883 .
splice_unquote_notail:
1884 ; Just return the object in RCX
1885 ; since nothing to concatenate with
1889 .
splice_unquote_empty:
1890 ; Nothing in the (splice-unquote) list, so ignore
1891 ; Just call quasiquote on the rest of RSI
1893 mov al, BYTE [rsi
+ Cons.typecdr
]
1894 cmp al, content_pointer
1895 jne .empty_list
; Nothing else
1897 mov rsi
, [rsi
+ Cons.cdr
]
1898 jmp quasiquote
; Tail call
1901 ; Return an empty list
1903 mov [rax
], BYTE maltype_empty_list
1916 mov rsi
, rax
; Form to evaluate
1917 mov rdi
, [repl_env
] ; Environment
1920 call incref_object
; Environment increment refs
1921 xchg rsi
, rdi
; since it will be decremented by eval
1923 jmp eval
; This releases Env and Form/AST
1926 ;; Read-Eval-Print in sequence
1928 ;; Input string in RSI
1936 mov rsi
, rax
; Form to evaluate
1937 mov rdi
, [repl_env
] ; Environment
1940 call incref_object
; Environment increment refs
1941 xchg rsi
, rdi
; since it will be decremented by eval
1943 call eval
; This releases Env and Form/AST
1944 push rax
; Save result of eval
1949 mov rsi
, rax
; Output of eval into input of print
1950 mov rdi
, 1 ; print readably
1951 call pr_str
; String in RAX
1953 mov r8
, rax
; Save output
1955 pop rsi
; Result from eval
1963 ; Create and print the core environment
1964 call core_environment
; Environment in RAX
1966 mov [repl_env
], rax
; store in memory
1968 ; Set the error handler
1969 mov rsi
, rsp
; Stack pointer
1970 mov rdi
, .catch
; Address to jump to
1971 xor rcx
, rcx
; No data
1972 call error_handler_push
1974 ; Evaluate the startup string
1976 mov rsi
, mal_startup_string
1977 mov edx, mal_startup_string.len
1978 call raw_to_string
; String in RAX
1982 call read_str
; AST in RAX
1986 call release_array
; string
1987 pop rdi
; AST in RDI
1989 mov rsi
, [repl_env
] ; Environment in RSI
1991 call incref_object
; Environment increment refs
1992 xchg rsi
, rdi
; Env in RDI, AST in RSI
1997 call release_object
; Return from eval
1999 ; -----------------------------
2000 ; Check command-line arguments
2002 pop rax
; Number of arguments
2003 cmp rax
, 1 ; Always have at least one, the path to executable
2006 ; No extra arguments, so just set *ARGV* to an empty list
2007 call alloc_cons
; in RAX
2008 mov [rax
], BYTE maltype_empty_list
2009 mov rcx
, rax
; value (empty list)
2010 mov rdi
, argv_symbol
; symbol (*ARGV*)
2011 mov rsi
, [repl_env
] ; environment
2014 ; -----------------------------
2019 print_str_mac prompt_string
2023 ; Check if we have a zero-length string
2024 cmp DWORD [rax
+Array.
length], 0
2027 push rax
; Save address of the string
2030 call rep_seq
; Read-Eval-Print
2032 push rax
; Save returned string
2034 mov rsi
, rax
; Put into input of print_string
2037 ; Release string from rep_seq
2041 ; Release the input string
2051 ; Jumps here on error
2053 ; Check if an object was thrown
2055 je .catch_done_print
; nothing to print
2061 jmp .mainLoop
; Go back to the prompt
2066 ; Called with number of command-line arguments in RAX
2068 pop rbx
; executable
2071 pop rsi
; Address of first arg
2072 call cstring_to_string
; string in RAX
2075 ; get the rest of the args
2080 ; Got some arguments
2082 ; Got an argument left.
2083 pop rsi
; Address of C string
2084 call cstring_to_string
; String in RAX
2087 ;Make a Cons to point to the string
2088 call alloc_cons
; in RAX
2089 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
2090 mov [rax
+ Cons.car
], r12
2095 ; R10 zero, so first arg
2096 mov r10
, rax
; Head of list
2097 mov r11
, rax
; Tail of list
2100 ; R10 not zero, so append to list tail
2101 mov [r11
+ Cons.cdr
], rax
2102 mov [r11
+ Cons.typecdr
], BYTE content_pointer
2110 ; No arguments. Create an emoty list
2111 call alloc_cons
; in RAX
2112 mov [rax
], BYTE maltype_empty_list
2116 push r9
; File name string
2118 mov rcx
, r10
; value (list)
2119 mov rdi
, argv_symbol
; symbol (*ARGV*)
2120 mov rsi
, [repl_env
] ; environment
2123 mov rsi
, run_script_string
; load-file function
2124 mov edx, run_script_string.len
2125 call raw_to_string
; String in RAX
2128 pop rdx
; File name string
2129 call string_append_string
2132 call string_append_char
2134 call string_append_char
; closing brace
2136 ; Read-Eval "(load-file <file>)"