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
19 ;; Top-level (REPL) environment
27 ;; ------------------------------------------
28 ;; Fixed strings for printing
30 static prompt_string
, db 10,"user> " ; The string to print at the prompt
32 static error_string
, db 27,'[31m',"Error",27,'[0m',": "
34 static not_found_string
, db " not found.",10
36 static def_missing_arg_string
, db "missing argument to def!",10
38 static def_expecting_symbol_string
, db "expecting symbol as first argument to def!",10
40 static let_missing_bindings_string
, db "let* missing bindings",10
42 static let_bindings_list_string
, db "let* expected a list or vector of bindings",10
44 static let_bind_symbol_string
, db "let* expected a symbol in bindings list",10
46 static let_bind_value_string
, db "let* missing value in bindings list",10
48 static let_missing_body_string
, db "let* missing body",10
49 static eval_list_not_function
, db "list does not begin with a function",10
51 static if_missing_condition_string
, db "missing condition in if expression",10
53 ;; Symbols used for comparison
55 static_symbol def_symbol
, 'def!'
56 static_symbol let_symbol
, 'let*'
57 static_symbol do_symbol
, 'do'
58 static_symbol if_symbol
, 'if'
59 static_symbol fn_symbol
, 'fn*'
61 static_symbol argv_symbol
, '*ARGV*'
63 static_symbol quote_symbol
, 'quote'
64 static_symbol quasiquote_symbol
, 'quasiquote'
65 static_symbol unquote_symbol
, 'unquote'
66 static_symbol splice_unquote_symbol
, 'splice-unquote'
67 static_symbol concat_symbol
, 'concat'
68 static_symbol cons_symbol
, 'cons'
70 ;; Startup string. This is evaluated on startup
71 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," ))))) )"
73 ;static mal_startup_string, db "(do (def! not (fn* (a) (if a false true))) )"
75 ;; Command to run, appending the name of the script to run
76 static run_script_string
, db "(load-file ",34
79 ;; ----------------------------------------------
83 ;; A handler consists of:
84 ;; - A stack pointer address to reset to
85 ;; - An address to jump to
86 ;; - An optional data structure to pass
88 ;; When jumped to, an error handler will be given:
89 ;; - the object thrown in RSI
90 ;; - the optional data structure in RDI
94 ;; Add an error handler to the front of the list
96 ;; Input: RSI - Stack pointer
97 ;; RDI - Address to jump to
98 ;; RCX - Data structure. Set to zero for none.
99 ;; If not zero, reference count incremented
101 ;; Modifies registers:
106 ; car will point to a list (stack, addr, data)
107 ; cdr will point to the previous handler
108 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
109 mov rbx
, [error_handler
]
110 cmp rbx
, 0 ; Check if previous handler was zero
111 je .create_handler
; Zero, so leave null
112 ; Not zero, so create pointer to it
113 mov [rax
+ Cons.typecdr
], BYTE content_pointer
114 mov [rax
+ Cons.cdr
], rbx
116 ; note: not incrementing reference count, since
117 ; we're replacing one reference with another
119 mov [error_handler
], rax
; new error handler
123 mov [rdx
+ Cons.car
], rax
124 ; Store stack pointer
125 mov [rax
], BYTE (block_cons
+ container_list
+ content_function
)
126 mov [rax
+ Cons.car
], rsi
; stack pointer
130 mov [rdx
+ Cons.typecdr
], BYTE content_pointer
131 mov [rdx
+ Cons.cdr
], rax
132 ; Store function pointer to jump to
133 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
134 mov [rax
+ Cons.car
], rdi
136 ; Check if there is an object to pass to handler
140 ; Set the final CDR to point to the object
141 mov [rax
+ Cons.typecdr
], BYTE content_pointer
142 mov [rax
+ Cons.cdr
], rcx
151 ;; Removes an error handler from the list
153 ;; Modifies registers:
159 mov rsi
, [error_handler
]
161 je .done
; Nothing to remove
164 mov rsi
, [rsi
+ Cons.cdr
] ; next handler
165 mov [error_handler
], rsi
166 call incref_object
; needed because releasing soon
168 pop rsi
; handler being removed
176 ;; Object to pass to handler should be in RSI
178 ; Get the next error handler
179 mov rax
, [error_handler
]
184 mov rax
, [rax
+ Cons.car
] ; handler
185 mov rbx
, [rax
+ Cons.car
] ; stack pointer
186 mov rax
, [rax
+ Cons.cdr
]
187 mov rcx
, [rax
+ Cons.car
] ; function
188 mov rdi
, [rax
+ Cons.cdr
] ; data structure
193 ; Jump to the handler
197 ; Print the object in RSI then quit
199 je .done
; nothing to print
200 mov rdi
, 1 ; print_readably
207 ;; ----------------------------------------------
210 ;; Inputs: RSI Form to evaluate
214 mov r15
, rdi
; Save Env in r15
219 ; Check if this is a list
221 and ah, container_mask
222 cmp ah, container_list
225 cmp ah, container_map
228 cmp ah, container_vector
231 ; Not a list, map or vector
232 cmp ah, container_symbol
235 ; Not a symbol, list, map or vector
236 call incref_object
; Increment reference count
242 ; Check if first character of symbol is ':'
243 mov al, BYTE [rsi
+ Array.data
]
247 ; look in environment
250 ; symbol is the key in rdi
254 je .done
; result in RAX
256 ; Not found, throw an error
258 print_str_mac error_string
; print 'Error: '
262 mov edx, [rsi
+ Array.
length]
264 call print_rawstring
; print symbol
266 print_str_mac not_found_string
; print ' not found'
271 ; ------------------------------
274 ; Just return keywords unaltered
279 ; ------------------------------
281 ; Evaluate each element of the list
283 xor r8
, r8
; The list to return
284 ; r9 contains head of list
287 mov al, BYTE [rsi
] ; Check type
290 cmp ah, content_pointer
293 ; A value in RSI, so copy
298 add bl, (block_cons
+ container_list
)
299 mov [rax
], BYTE bl ; set type
300 mov rbx
, [rsi
+ Cons.car
]
301 mov [rax
+ Cons.car
], rbx
; copy value
307 ; List element is a pointer to something
312 mov rdi
, [rsi
+ Cons.car
] ; Get the address
315 call incref_object
; Environment increment refs
316 xchg rsi
, rdi
; Env in RDI, AST in RSI
318 call incref_object
; AST increment refs
320 call eval
; Evaluate it, result in rax
326 ; Check the type it's evaluated to
329 and bh, (block_mask
+ container_mask
)
330 cmp bh, (block_cons
+ container_value
)
333 ; Not a value, so need a pointer to it
336 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
337 pop rbx
; Address to point to
338 mov [rax
+ Cons.car
], rbx
342 ; Got value in RAX, so copy
344 call alloc_cons
; Copy in RAX
345 pop rbx
; Value to copy in RBX
348 or cl, (block_cons
+ container_list
)
349 mov [rax
], BYTE cl ; set type
350 mov rcx
, [rbx
+ Cons.car
]
351 mov [rax
+ Cons.car
], rcx
; copy value
353 ; Release the value in RBX
361 ; Fall through to .list_append
365 cmp r8
, 0 ; Check if this is the first
369 mov [r9
+ Cons.cdr
], rax
370 mov [r9
+ Cons.typecdr
], BYTE content_pointer
377 ; fall through to .list_next
380 ; Check if there's another
381 mov al, BYTE [rsi
+ Cons.typecdr
]
382 cmp al, content_pointer
383 jne .list_done
; finished list
384 mov rsi
, [rsi
+ Cons.cdr
] ; next in list
388 mov rax
, r8
; Return the list
391 ; ---------------------
393 ; Create a new map, evaluating all the values
395 ; Check if the map is empty
396 cmp al, maltype_empty_map
399 ; map empty. Just return it
406 mov r10
, rsi
; input in R10
407 xor r12
, r12
; New map in r12
409 ; Now loop through each key-value pair
410 ; NOTE: This method relies on the implementation
415 call alloc_cons
; New Cons in RAX
417 mov bl, [r10
+ Cons.typecar
] ; Type in BL
418 mov [rax
+ Cons.typecar
], bl
419 mov rcx
, [r10
+ Cons.car
] ; Value in RCX
420 mov [rax
+ Cons.car
], rcx
422 ; Check the type of the key
424 cmp bl, content_pointer
425 jne .map_got_key
; a value
427 ; a pointer, so increment reference count
428 mov bx, WORD [rcx
+ Cons.refcount
]
430 mov [rcx
+ Cons.refcount
], WORD bx
442 ; Appending to previous value in r13
443 mov [r13
+ Cons.typecdr
], BYTE content_pointer
444 mov [r13
+ Cons.cdr
], rax
448 ; Check that we have a value
449 mov al, BYTE [r10
+ Cons.typecdr
]
450 cmp al, content_pointer
451 jne .map_error_missing_value
452 mov r10
, [r10
+ Cons.cdr
]
454 ; Now got value in r10
456 ; Check the type of the value
457 mov bl, [r10
+ Cons.typecar
] ; Type in BL
459 cmp bl, content_pointer
460 je .map_value_pointer
462 ; Not a pointer, so make a copy
464 mov bl, [r10
+ Cons.typecar
]
465 mov [rax
+ Cons.typecar
], bl
466 mov rcx
, [r10
+ Cons.car
]
467 mov [rax
+ Cons.car
], rcx
471 ; A pointer, so need to evaluate
473 push r12
; start of result
474 push r13
; Current head of result
476 mov rsi
, [r10
+ Cons.car
] ; Get the address
480 call incref_object
; Environment increment refs
485 call eval
; Evaluate it, result in rax
491 ; Check the type it's evaluated to
494 and bh, (block_mask
+ container_mask
)
495 cmp bh, (block_cons
+ container_value
)
497 jne .map_eval_pointer
499 ; A value, so just change the type to a map
501 add bl, (block_cons
+ container_map
)
506 ; Not a value, so need a pointer to it
509 mov [rax
], BYTE (block_cons
+ container_map
+ content_pointer
)
510 pop rbx
; Address to point to
511 mov [rax
+ Cons.car
], rbx
514 ; Append RAX to list in R13
515 mov [r13
+ Cons.typecdr
], BYTE content_pointer
516 mov [r13
+ Cons.cdr
], rax
519 ; Check if there's another key
520 mov al, BYTE [r10
+ Cons.typecdr
]
521 cmp al, content_pointer
522 jne .map_done
; finished map
523 mov r10
, [r10
+ Cons.cdr
] ; next in map
530 .
map_error_missing_value:
534 ; ------------------------------
536 ; Evaluate each element of the vector
538 xor r8
, r8
; The vector to return
539 ; r9 contains head of vector
542 mov al, BYTE [rsi
] ; Check type
545 cmp ah, content_pointer
552 add bl, (block_cons
+ container_vector
)
553 mov [rax
], BYTE bl ; set type
554 mov rbx
, [rsi
+ Cons.car
]
555 mov [rax
+ Cons.car
], rbx
; copy value
561 ; Vector element is a pointer to something
566 mov rsi
, [rsi
+ Cons.car
] ; Get the address
570 call incref_object
; Environment increment refs
575 call eval
; Evaluate it, result in rax
581 ; Check the type it's evaluated to
584 and bh, (block_mask
+ container_mask
)
585 cmp bh, (block_cons
+ container_value
)
586 je .vector_eval_value
588 ; Not a value, so need a pointer to it
591 mov [rax
], BYTE (block_cons
+ container_vector
+ content_pointer
)
592 pop rbx
; Address to point to
593 mov [rax
+ Cons.car
], rbx
597 ; Got value in RAX, so copy
599 call alloc_cons
; Copy in RAX
600 pop rbx
; Value to copy in RBX
603 or cl, (block_cons
+ container_vector
)
604 mov [rax
], BYTE cl ; set type
605 mov rcx
, [rbx
+ Cons.car
]
606 mov [rax
+ Cons.car
], rcx
; copy value
608 ; Release the value in RBX
619 cmp r8
, 0 ; Check if this is the first
623 mov [r9
+ Cons.cdr
], rax
624 mov [r9
+ Cons.typecdr
], BYTE content_pointer
631 ; fall through to .vector_next
634 ; Check if there's another
635 mov al, BYTE [rsi
+ Cons.typecdr
]
636 cmp al, content_pointer
637 jne .vector_done
; finished vector
638 mov rsi
, [rsi
+ Cons.cdr
] ; next in vector
642 mov rax
, r8
; Return the vector
645 ; ---------------------
651 ;; Comparison of symbols for eval function
652 ;; Compares the symbol in RSI with specified symbol
653 ;; Preserves RSI and RBX
655 %macro eval_cmp_symbol
1
660 call compare_char_array
663 test rax
, rax
; ZF set if rax = 0 (equal)
666 ;; ----------------------------------------------------
669 ;; Input: RSI AST to evaluate [ Released ]
670 ;; RDI Environment [ Released ]
672 ;; Returns: Result in RAX
674 ;; Note: Both the form and environment will have their reference count
675 ;; reduced by one (released). This is for tail call optimisation (Env),
676 ;; quasiquote and macroexpand (AST)
681 push rsi
; AST pushed, must be popped before return
685 cmp al, maltype_empty_list
688 and al, container_mask
689 cmp al, container_list
692 ; Not a list. Evaluate and return
694 jmp .return
; Releases Env
696 ; --------------------
700 ; Check if the first element is a symbol
704 cmp al, content_pointer
707 mov rbx
, [rsi
+ Cons.car
]
709 cmp al, maltype_symbol
712 ; Is a symbol, address in RBX
714 ; Compare against special form symbols
716 eval_cmp_symbol def_symbol
; def!
719 eval_cmp_symbol let_symbol
; let*
722 eval_cmp_symbol do_symbol
; do
725 eval_cmp_symbol if_symbol
; if
728 eval_cmp_symbol fn_symbol
; fn
731 eval_cmp_symbol quote_symbol
; quote
734 eval_cmp_symbol quasiquote_symbol
; quasiquote
735 je .quasiquote_symbol
741 ; Define a new symbol in current environment
743 ; Next item should be a symbol
744 mov al, BYTE [rsi
+ Cons.typecdr
]
745 cmp al, content_pointer
746 jne .def_error_missing_arg
747 mov rsi
, [rsi
+ Cons.cdr
]
749 ; Now should have a symbol
751 mov al, BYTE [rsi
+ Cons.typecar
]
753 cmp al, content_pointer
754 jne .def_error_expecting_symbol
755 mov r8
, [rsi
+ Cons.car
] ; Symbol (?)
758 cmp al, maltype_symbol
759 jne .def_error_expecting_symbol
761 ; R8 now contains a symbol
763 ; expecting a value or pointer next
764 mov al, BYTE [rsi
+ Cons.typecdr
]
765 cmp al, content_pointer
766 jne .def_error_missing_arg
767 mov rsi
, [rsi
+ Cons.cdr
]
769 ; Check if this is a pointer
773 cmp ah, content_pointer
779 pop rbx
; BL now contains type
781 add bl, (block_cons
+ container_value
)
783 mov rcx
, [rsi
+ Cons.car
]
784 mov [rax
+ Cons.car
], rcx
790 ; A pointer, so evaluate
792 ; This may throw an error, so define a handler
797 mov rsi
, [rsi
+ Cons.car
] ; Pointer
801 call incref_object
; Environment increment refs
802 xchg rsi
, rdi
; since it will be decremented by eval
804 call incref_object
; AST increment refs
812 ; Symbol in R8, value in RSI
813 mov rdi
, r8
; key (symbol)
815 mov rsi
, r15
; Environment
821 .
def_error_missing_arg:
822 mov rsi
, def_missing_arg_string
823 mov rdx
, def_missing_arg_string.len
824 jmp .def_handle_error
826 .
def_error_expecting_symbol:
827 mov rsi
, def_expecting_symbol_string
828 mov rdx
, def_expecting_symbol_string.len
829 jmp .def_handle_error
834 print_str_mac error_string
; print 'Error: '
838 call print_rawstring
; print message
840 xor rsi
, rsi
; no object to throw
841 jmp error_throw
; No return
843 ; -----------------------------
845 ; Create a new environment
847 mov r11
, rsi
; Let form in R11
849 mov rsi
, r15
; Outer env
850 call env_new
; Increments R15's ref count
851 mov r14
, rax
; New environment in R14
854 call release_object
; Decrement R15 ref count
856 ; Second element should be the bindings
858 mov al, BYTE [r11
+ Cons.typecdr
]
859 cmp al, content_pointer
860 jne .let_error_missing_bindings
861 mov r11
, [r11
+ Cons.cdr
]
865 cmp al, content_pointer
866 jne .let_error_bindings_list
868 mov r12
, [r11
+ Cons.car
] ; should be bindings list
870 and al, (block_mask
+ container_mask
)
871 ; Can be either a list or vector
872 cmp al, block_cons
+ container_list
874 cmp al, block_cons
+ container_vector
877 ; Not a list or vector
878 jmp .let_error_bindings_list
881 ; R12 now contains a list with an even number of items
882 ; The first should be a symbol, then a value to evaluate
887 cmp al, content_pointer
888 jne .let_error_bind_symbol
890 mov r13
, [r12
+ Cons.car
] ; Symbol (?)
892 cmp al, maltype_symbol
893 jne .let_error_bind_symbol
895 ; R13 now contains a symbol to bind
896 ; The next item in the bindings list (R12)
897 ; should be a value or expression to evaluate
899 mov al, BYTE [r12
+ Cons.typecdr
]
901 cmp al, content_pointer
902 jne .let_error_bind_value
903 mov r12
, [r12
+ Cons.cdr
]
907 ; Check the type of the value
908 mov bl, [r12
+ Cons.typecar
] ; Type in BL
910 cmp bl, content_pointer
911 je .let_value_pointer
913 ; Not a pointer, so make a copy
915 mov bl, [r12
+ Cons.typecar
]
917 ;or bl, (block_cons + container_value) ; 0
918 mov [rax
+ Cons.typecar
], bl
919 mov rcx
, [r12
+ Cons.car
]
920 mov [rax
+ Cons.car
], rcx
925 ; A pointer, so need to evaluate
926 push r11
; let* form list
927 push r12
; Position in bindings list
928 push r13
; symbol to bind
929 push r14
; new environment
930 mov rsi
, [r12
+ Cons.car
] ; Get the address
932 call incref_object
; Increment ref count of AST
935 call eval
; Evaluate it, result in rax
949 mov rsi
, rcx
; The value
952 ; Check if there are more bindings
953 mov al, BYTE [r12
+ Cons.typecdr
]
954 cmp al, content_pointer
955 jne .let_done_binding
956 mov r12
, [r12
+ Cons.cdr
] ; Next
961 ; Evaluate next item in let* form in new environment
963 mov al, BYTE [r11
+ Cons.typecdr
]
964 cmp al, content_pointer
965 jne .let_error_missing_body
966 mov r11
, [r11
+ Cons.cdr
] ; Now contains value to evaluate
967 ; Check type of the value
969 and al, block_mask
+ content_mask
970 cmp al, content_pointer
973 ; Just a value, so copy
977 mov [rax
], BYTE bl ; set type
978 mov rbx
, [r11
+ Cons.car
]
979 mov [rax
+ Cons.car
], rbx
; copy value
983 ; Evaluate using new environment
985 mov rsi
, [r11
+ Cons.car
] ; Object pointed to
986 call incref_object
; will be released by eval
988 mov r11
, rsi
; save new AST
991 mov rsi
, r11
; New AST
993 mov rdi
, r14
; New environment
996 ; Note: eval will release the new environment on return
999 ; Release the new environment
1010 ret ; already released env
1012 .
let_error_missing_bindings:
1013 mov rsi
, let_missing_bindings_string
1014 mov rdx
, let_missing_bindings_string.len
1015 jmp .let_handle_error
1017 .
let_error_bindings_list: ; expected a list or vector, got something else
1018 mov rsi
, let_bindings_list_string
1019 mov rdx
, let_bindings_list_string.len
1020 jmp .let_handle_error
1022 .
let_error_bind_symbol: ; expected a symbol, got something else
1023 mov rsi
, let_bind_symbol_string
1024 mov rdx
, let_bind_symbol_string.len
1025 jmp .let_handle_error
1027 .
let_error_bind_value: ; Missing value in binding list
1028 mov rsi
, let_bind_value_string
1029 mov rdx
, let_bind_value_string.len
1030 jmp .let_handle_error
1032 .
let_error_missing_body: ; Missing body to evaluate
1033 mov rsi
, let_missing_body_string
1034 mov rdx
, let_missing_body_string.len
1035 jmp .let_handle_error
1038 push r11
; For printing later
1043 print_str_mac error_string
; print 'Error: '
1047 call print_rawstring
; print message
1050 jmp error_throw
; No return
1052 ; -----------------------------
1055 mov r11
, rsi
; do form in RSI
1056 ; Environment in R15
1058 ; Check if there is a body
1059 mov al, BYTE [r11
+ Cons.typecdr
]
1060 cmp al, content_pointer
1061 jne .do_no_body
; error
1063 mov r11
, [r11
+ Cons.cdr
] ; Body in R11
1067 ; Need to test if this is the last form
1068 ; so we can handle tail call
1070 mov bl, BYTE [r11
+ Cons.typecdr
]
1071 cmp bl, content_pointer
1072 jne .do_body_last
; Last expression
1074 ; not the last expression
1076 ; Check if this is a value or pointer
1078 and al, block_mask
+ content_mask
1079 cmp al, content_pointer
1080 jne .do_next
; A value, so skip
1082 ; A pointer, so evaluate
1088 call incref_object
; Increase Env reference
1089 ; since eval will release Env
1091 mov rsi
, [r11
+ Cons.car
] ; Form
1092 call incref_object
; Increment ref count since eval will release
1095 call eval
; Result in RAX
1097 ; Another form after this.
1098 ; Discard the result of the last eval
1106 mov r11
, [r11
+ Cons.cdr
] ; Next in list
1111 ; The last form is in R11, which will be returned
1113 ; Check if this is a value or pointer
1115 and al, block_mask
+ content_mask
1116 cmp al, content_pointer
1117 jne .do_body_value_return
1118 jmp .do_body_expr_return
1120 .
do_body_value_return:
1121 ; Got a value as last form (in R11).
1124 push rax
; Type of value to return
1130 ; Allocate a Cons object to hold value
1132 pop rbx
; type in BL
1134 mov rbx
, [r11
+ Cons.car
]
1135 mov [rax
+ Cons.car
], rbx
1139 mov r15
, rax
; not modified by release
1145 .
do_body_expr_return:
1146 ; An expression to evaluate as the last form
1147 ; Tail call optimise, jumping to eval
1148 ; Don't increment Env reference count
1151 mov rsi
, [r11
+ Cons.car
] ; new AST form
1152 call incref_object
; This will be released by eval
1154 mov r11
, rsi
; Save new AST
1155 pop rsi
; Remove old AST from stack
1160 jmp eval
; Tail call
1163 ; No expressions to evaluate. Return nil
1166 call release_object
; Release Env
1173 mov [rax
], BYTE maltype_nil
1174 mov [rax
+ Cons.typecdr
], BYTE content_nil
1177 ; -----------------------------
1180 mov r11
, rsi
; if form in R11
1181 ; Environment in R15
1183 mov al, BYTE [r11
+ Cons.typecdr
]
1184 cmp al, content_pointer
1185 jne .if_no_condition
1187 mov r11
, [r11
+ Cons.cdr
] ; Should be a condition
1189 ; Check if value or pointer
1191 and al, content_mask
1192 cmp al, content_pointer
1195 ; A pointer, so evaluate
1201 call incref_object
; Increase Env reference
1203 mov rsi
, [r11
+ Cons.car
] ; Form
1204 call incref_object
; Increase Form/AST ref count
1207 call eval
; Result in RAX
1211 ; Get type of result
1223 cmp bl, maltype_false
1233 cmp al, content_false
1239 ; Skip the next item
1240 mov al, BYTE [r11
+ Cons.typecdr
]
1241 cmp al, content_pointer
1244 mov r11
, [r11
+ Cons.cdr
]
1247 ; Get the next item in the list and evaluate it
1248 mov al, BYTE [r11
+ Cons.typecdr
]
1249 cmp al, content_pointer
1250 jne .return_nil
; Nothing to return
1252 mov r11
, [r11
+ Cons.cdr
]
1254 ; Check if value or pointer
1256 and al, content_mask
1257 cmp al, content_pointer
1264 and bl, content_mask
1266 mov rbx
, [r11
+ Cons.car
]
1267 mov [rax
+ Cons.car
], rbx
1272 mov rsi
, [r11
+ Cons.car
] ; Form
1273 call incref_object
; Will be released by eval
1277 call release_object
; Release old AST
1278 mov rsi
, r11
; New AST
1281 jmp eval
; Tail call
1283 .
if_no_condition: ; just (if) without a condition
1285 print_str_mac error_string
1286 print_str_mac if_missing_condition_string
1288 ; Release environment
1291 xor rsi
, rsi
; No object to throw
1296 mov [rax
], BYTE maltype_nil
1297 mov [rax
+ Cons.typecdr
], BYTE content_nil
1300 ; Release environment
1302 mov r15
, rax
; Save RAX (return value)
1306 pop rsi
; Pushed at start of eval
1309 mov rax
, r15
; return value
1312 ; -----------------------------
1315 mov r11
, rsi
; fn form in R11
1316 ; Environment in R15
1318 ; Get the binds and body of the function
1319 mov al, BYTE [r11
+ Cons.typecdr
]
1320 cmp al, content_pointer
1323 mov r11
, [r11
+ Cons.cdr
]
1325 and al, content_mask
1326 cmp al, content_pointer
1327 jne .fn_binds_not_list
1329 mov r12
, [r11
+ Cons.car
] ; Should be binds list
1331 and al, (block_mask
+ container_mask
)
1332 cmp al, (block_cons
+ container_list
)
1333 je .fn_got_binds
; Can be list
1334 cmp al, (block_cons
+ container_vector
)
1335 je .fn_got_binds
; or vector
1336 jmp .fn_binds_not_list
1340 ; Next get the body of the function
1341 mov al, BYTE [r11
+ Cons.typecdr
]
1342 cmp al, content_pointer
1345 mov r11
, [r11
+ Cons.cdr
]
1346 ; Check value or pointer
1348 and al, content_mask
1349 cmp al, content_pointer
1350 jne .fn_is_value
; Body in r11
1351 mov r11
, [r11
+ Cons.car
]
1355 ; Body is just a value, no expression
1356 mov [r11
], BYTE al ; Mark as value, not list
1360 ; Now put into function type
1361 ; Addr is "apply_fn", the address to call
1367 mov [rax
], BYTE (block_cons
+ container_function
+ content_function
)
1369 mov [rax
+ Cons.car
], rbx
; Address of apply function
1370 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1372 mov r13
, rax
; Return list in R13
1375 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1376 mov [rax
+ Cons.car
], r15
; Environment
1377 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1379 mov [r13
+ Cons.cdr
], rax
; Append to list
1388 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1389 mov [rax
+ Cons.car
], r12
; Binds list
1390 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1392 mov [r14
+ Cons.cdr
], rax
; Append to list
1401 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1402 mov [rax
+ Cons.car
], r11
; Body of function
1404 mov [r14
+ Cons.cdr
], rax
1417 mov [rax
], BYTE maltype_nil
1418 mov [rax
+ Cons.typecdr
], BYTE content_nil
1421 ; -----------------------------
1424 ; Just return the arguments in rsi cdr
1426 mov al, BYTE [rsi
+ Cons.typecdr
]
1427 cmp al, content_pointer
1428 jne .return_nil
; quote empty, so return nil
1430 mov rsi
, [rsi
+ Cons.cdr
]
1432 ; Check if this is a value or pointer
1433 mov al, BYTE [rsi
+ Cons.typecar
]
1434 and al, content_mask
1435 cmp al, content_pointer
1438 ; RSI contains a value. Remove the list container
1439 mov [rsi
+ Cons.typecar
], BYTE al
1445 ; RSI contains a pointer, so get the object pointed to
1446 mov rsi
, [rsi
+ Cons.car
]
1451 ; -----------------------------
1454 ; call quasiquote function with first argument
1456 mov al, BYTE [rsi
+ Cons.typecdr
]
1457 cmp al, content_pointer
1458 jne .return_nil
; quasiquote empty, so return nil
1460 mov r11
, rsi
; Save original AST in R11
1462 mov rsi
, [rsi
+ Cons.cdr
]
1464 ; Check if this is a value or pointer
1465 mov al, BYTE [rsi
+ Cons.typecar
]
1466 and al, content_mask
1467 cmp al, content_pointer
1468 je .quasiquote_pointer
1470 ; RSI contains a value. Remove the list container
1471 mov [rsi
+ Cons.typecar
], BYTE al
1476 .
quasiquote_pointer:
1477 ; RSI contains a pointer, so get the object pointed to
1478 mov rsi
, [rsi
+ Cons.car
]
1483 push r15
; Environment
1484 ; Original AST already on stack
1488 pop rdi
; Environment
1491 mov r11
, rax
; New AST
1492 call release_object
; Release old AST
1493 mov rsi
, r11
; New AST in RSI
1495 jmp eval
; Tail call
1497 ; -----------------------------
1501 mov rdi
, r15
; Environment
1503 call eval_ast
; List of evaluated forms in RAX
1509 ; This point can be called to run a function
1512 ; Inputs: RAX - List with function as first element
1513 ; NOTE: This list is released
1515 ; Check that the first element of the return is a function
1517 and bl, content_mask
1518 cmp bl, content_pointer
1519 jne .list_not_function
1521 mov rbx
, [rax
+ Cons.car
] ; Get the address
1523 cmp cl, maltype_function
1524 jne .list_not_function
1526 ; Check the rest of the args
1527 mov cl, BYTE [rax
+ Cons.typecdr
]
1528 cmp cl, content_pointer
1534 mov [rax
], BYTE maltype_empty_list
1537 jmp .list_function_call
1539 mov rsi
, [rax
+ Cons.cdr
] ; Rest of list
1540 .
list_function_call:
1541 ; Call the function with the rest of the list in RSI
1543 mov rdx
, rax
; List to release
1544 mov rdi
, rbx
; Function object in RDI
1546 mov rbx
, [rbx
+ Cons.car
] ; Call function
1548 je apply_fn
; Jump to user function apply
1550 ; A built-in function, so call (no recursion)
1558 pop rsi
; eval'ed list
1564 jmp .return
; Releases Env
1567 ; Not a function. Probably an error
1573 print_str_mac error_string
1574 print_str_mac eval_list_not_function
1579 ;; Applies a user-defined function
1581 ;; Input: RSI - Arguments to bind
1582 ;; RDI - Function object
1583 ;; RDX - list to release after binding
1584 ;; R15 - Env (will be released)
1586 ;; Output: Result in RAX
1588 ;; This is jumped to from eval, so if it returns
1589 ;; then it will return to the caller of eval, not to eval
1592 ; Extract values from the list in RDI
1593 mov rax
, [rdi
+ Cons.cdr
]
1594 mov rsi
, [rax
+ Cons.car
] ; Env
1595 mov rax
, [rax
+ Cons.cdr
]
1596 mov rdi
, [rax
+ Cons.car
] ; Binds
1597 mov rax
, [rax
+ Cons.cdr
]
1598 mov rax
, [rax
+ Cons.car
] ; Body
1601 ; Check the type of the body
1603 and bl, block_mask
+ container_mask
1605 ; Just a value (in RAX). No eval needed
1607 mov r14
, rax
; Save return value in R14
1612 ; Release the list passed in RDX
1616 ; Release the environment
1620 ; Release the AST, pushed at start of eval
1627 ; Create a new environment, binding arguments
1634 mov rdi
, rax
; New environment in RDI
1636 ; Release the list passed in RDX
1641 ; Release the environment
1646 call incref_object
; Will be released by eval
1648 jmp eval
; Tail call
1649 ; The new environment (in RDI) will be released by eval
1652 ;; Set ZF if RSI is a non-empty list or vector
1653 ;; Modifies RAX, does not modify RSI
1657 jnz .false
; Not a Cons
1658 cmp al, maltype_empty_list
1659 je .false
; Empty list
1660 cmp al, maltype_empty_vector
1661 je .false
; Empty vector
1663 ; Something non empty
1664 and al, container_mask
1665 cmp al, container_list
1667 cmp al, container_vector
1669 ; Not a list or vector -> false
1673 and ah, 255-64 ; clear zero flag
1678 or ah, 64 ; set zero flag
1682 ;; Called by eval with AST in RSI [ modified ]
1683 ;; Returns new AST in RAX
1686 ; i. Check if AST is an empty list
1690 ; ii. Check if the first element of RSI is the symbol
1694 and al, content_mask
1695 cmp al, content_pointer
1696 jne .not_unquote
; Not a pointer
1698 mov rdi
, [rsi
+ Cons.car
] ; Get the pointer
1700 cmp cl, maltype_symbol
1703 ; Compare against 'unquote'
1707 mov rsi
, unquote_symbol
1708 call compare_char_array
1717 ; iii. Handle splice-unquote
1718 ; RSI -> ( ( splice-unquote ? ) ? )
1720 ; Test if RSI contains a pointer
1722 cmp al, content_pointer
1725 mov rbx
, [rsi
+ Cons.car
] ; Get the object pointer
1727 ; RBX -> ( splice-unquote ? )
1732 jne .not_splice
; First element not a pair
1734 ; Check if this list in RBX starts with 'splice-unquote' symbol
1736 and al, content_mask
1737 cmp al, content_pointer
1741 mov rdi
, [rbx
+ Cons.car
] ; Get the pointer
1743 cmp al, maltype_symbol
1749 ; Compare against 'splice-unquote'
1750 mov rsi
, splice_unquote_symbol
1751 call compare_char_array
1761 ; iv. Cons first and rest of AST in RSI
1763 ; check if pointer or value
1765 and cl, content_mask
1766 cmp cl, content_pointer
1771 or cl, container_list
1772 mov [rax
], BYTE cl ; List + Content
1773 mov rbx
, [rsi
+ Cons.car
]
1774 mov [rax
+ Cons.car
], rbx
1779 ; Get the pointer and call quasiquote
1781 mov rsi
, [rsi
+ Cons.car
]
1787 mov [rax
], BYTE (container_list
+ content_pointer
)
1788 mov [rax
+ Cons.car
], rcx
1792 ; Have Cons with first object in RCX
1794 ; Call quasiquote on the rest of the AST
1795 ; Check if this is the end of the list
1796 mov al, BYTE [rsi
+ Cons.typecdr
]
1797 cmp al, content_pointer
1800 mov rsi
, [rsi
+ Cons.cdr
] ; Rest of the list
1802 call incref_object
; Will release after quasiquote call
1804 jmp .cons_quasiquote_ast
1807 ; End of the AST, so make an empty list
1809 mov [rax
], BYTE maltype_empty_list
1812 .
cons_quasiquote_ast:
1816 mov rdx
, rax
; List in RDX
1819 call release_object
; Release input
1821 pop rcx
; Value in RCX
1824 ; Work from the end of the list to the front
1827 mov [rax
], BYTE (container_list
+ content_pointer
)
1828 mov [rax
+ Cons.car
], rdx
; The rest of AST
1830 ; Link to the RCX Cons
1831 mov [rcx
+ Cons.typecdr
], BYTE content_pointer
1832 mov [rcx
+ Cons.cdr
], rax
1835 call alloc_cons
; Cons for cons symbol
1836 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1837 mov [rax
+ Cons.cdr
], rdx
1840 ; Get the cons symbol
1841 mov rsi
, cons_symbol
1844 mov [rdx
], BYTE (container_list
+ content_pointer
)
1845 mov [rdx
+ Cons.car
], rsi
1851 ; Return (quote RSI)
1853 call incref_object
; RSI reference count
1857 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
1858 mov [rax
+ Cons.car
], rsi
1861 ; Cons for quote symbol
1864 mov [rbx
+ Cons.typecdr
], BYTE content_pointer
1865 mov [rbx
+ Cons.cdr
], rsi
1867 ; Get a quote symbol, incrementing references
1868 mov rsi
, quote_symbol
1871 ; Put into the Cons in RBX
1872 mov [rbx
+ Cons.car
], rsi
1873 mov [rbx
], BYTE (block_cons
+ container_list
+ content_pointer
)
1876 ; -----------------------
1880 ; Got unquote symbol. Return second element of RSI
1881 mov al, BYTE [rsi
+ Cons.typecdr
]
1882 cmp al, content_pointer
1883 jne .empty_list
; No second element
1885 mov rsi
, [rsi
+ Cons.cdr
]
1887 ; Check if it's a value or pointer
1889 and cl, content_mask
1890 cmp cl, content_pointer
1893 ; A value, so need a new Cons
1895 mov [rax
], BYTE cl ; content
1896 mov rbx
, [rsi
+ Cons.car
]
1897 mov [rax
+ Cons.car
], rbx
; Copy content
1901 mov rsi
, [rsi
+ Cons.car
]
1906 ; -----------------------
1908 ; RSI -> ( RBX->( splice-unquote A ) B )
1910 ; RBX Car points to splice-unquote symbol
1912 ; Check if there is anything after the symbol
1913 mov al, BYTE [rbx
+ Cons.typecdr
]
1914 cmp al, content_pointer
1915 jne .splice_unquote_empty
1917 ; Point to the second element of the splice-unquote list
1918 mov rcx
, [rbx
+ Cons.cdr
]
1920 ; Check whether it's a value or pointer
1922 and al, content_mask
1923 cmp al, content_pointer
1924 je .splice_unquote_pointer
1926 ; A value, so change the container to a value
1928 ; Remove pointer from RBX
1929 mov [rbx
+ Cons.typecdr
], BYTE 0
1930 jmp .splice_unquote_first
; Got the value in RCX
1932 .
splice_unquote_pointer:
1933 mov rcx
, [rcx
+ Cons.car
] ; Get the object pointed to
1936 xchg rcx
, rsi
; Object in RCX
1938 .
splice_unquote_first: ; Got the first object in RCX
1940 ; Check if RSI contains anything else
1941 mov al, BYTE [rsi
+ Cons.typecdr
]
1942 cmp al, content_pointer
1943 jne .splice_unquote_notail
1945 mov rsi
, [rsi
+ Cons.cdr
]
1948 ; ( ( splice-unquote A ) B )
1950 ; Need to call quasiquote on the rest of the list
1955 ; Need to concat rcx and rdx
1956 ; Work from the end of the list to the front
1959 mov [rax
], BYTE (container_list
+ content_pointer
)
1960 mov [rax
+ Cons.car
], rdx
; The rest of AST
1961 mov rdx
, rax
; Push list into RDX
1964 mov [rax
], BYTE (container_list
+ content_pointer
)
1965 mov [rax
+ Cons.car
], rcx
; The splice-unquote object
1966 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1967 mov [rax
+ Cons.cdr
], rdx
1970 call alloc_cons
; Cons for concat symbol
1971 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1972 mov [rax
+ Cons.cdr
], rdx
1975 ; Get the concat symbol
1976 mov rsi
, concat_symbol
1979 mov [rdx
], BYTE (container_list
+ content_pointer
)
1980 mov [rdx
+ Cons.car
], rsi
1985 .
splice_unquote_notail:
1986 ; Just return the object in RCX
1987 ; since nothing to concatenate with
1991 .
splice_unquote_empty:
1992 ; Nothing in the (splice-unquote) list, so ignore
1993 ; Just call quasiquote on the rest of RSI
1995 mov al, BYTE [rsi
+ Cons.typecdr
]
1996 cmp al, content_pointer
1997 jne .empty_list
; Nothing else
1999 mov rsi
, [rsi
+ Cons.cdr
]
2000 jmp quasiquote
; Tail call
2003 ; Return an empty list
2005 mov [rax
], BYTE maltype_empty_list
2012 ;; Read-Eval-Print in sequence
2014 ;; Input string in RSI
2022 mov rsi
, rax
; Form to evaluate
2023 mov rdi
, [repl_env
] ; Environment
2026 call incref_object
; Environment increment refs
2027 xchg rsi
, rdi
; since it will be decremented by eval
2029 call eval
; This releases Env and Form/AST
2030 push rax
; Save result of eval
2037 mov rdi
, 1 ; print_readably
2039 push rax
; Save output string
2041 mov rsi
, rax
; Put into input of print_string
2044 ; Release string from pr_str
2048 ; Release result of eval
2052 ; The AST from read_str is released by eval
2058 ; Create and print the core environment
2059 call core_environment
; Environment in RAX
2061 mov [repl_env
], rax
; store in memory
2063 ; Set the error handler
2064 mov rsi
, rsp
; Stack pointer
2065 mov rdi
, .catch
; Address to jump to
2066 xor rcx
, rcx
; No data
2067 call error_handler_push
2069 ; Evaluate the startup string
2071 mov rsi
, mal_startup_string
2072 mov edx, mal_startup_string.len
2073 call raw_to_string
; String in RAX
2077 call read_str
; AST in RAX
2081 call release_array
; string
2082 pop rdi
; AST in RDI
2084 mov rsi
, [repl_env
] ; Environment in RSI
2086 call incref_object
; Environment increment refs
2087 xchg rsi
, rdi
; Env in RDI, AST in RSI
2092 call release_object
; Return from eval
2094 ; -----------------------------
2095 ; Check command-line arguments
2097 pop rax
; Number of arguments
2098 cmp rax
, 1 ; Always have at least one, the path to executable
2101 ; No extra arguments, so just set *ARGV* to an empty list
2102 call alloc_cons
; in RAX
2103 mov [rax
], BYTE maltype_empty_list
2104 mov rcx
, rax
; value (empty list)
2105 mov rdi
, argv_symbol
; symbol (*ARGV*)
2106 mov rsi
, [repl_env
] ; environment
2109 ; -----------------------------
2114 print_str_mac prompt_string
2118 ; Check if we have a zero-length string
2119 cmp DWORD [rax
+Array.
length], 0
2122 push rax
; Save address of the input string
2128 ; Release the input string
2138 ; Jumps here on error
2140 ; Check if an object was thrown
2142 je .catch_done_print
; nothing to print
2148 jmp .mainLoop
; Go back to the prompt
2153 ; Called with number of command-line arguments in RAX
2155 pop rbx
; executable
2158 pop rsi
; Address of first arg
2159 call cstring_to_string
; string in RAX
2162 ; get the rest of the args
2167 ; Got some arguments
2169 ; Got an argument left.
2170 pop rsi
; Address of C string
2171 call cstring_to_string
; String in RAX
2174 ;Make a Cons to point to the string
2175 call alloc_cons
; in RAX
2176 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
2177 mov [rax
+ Cons.car
], r12
2182 ; R10 zero, so first arg
2183 mov r10
, rax
; Head of list
2184 mov r11
, rax
; Tail of list
2187 ; R10 not zero, so append to list tail
2188 mov [r11
+ Cons.cdr
], rax
2189 mov [r11
+ Cons.typecdr
], BYTE content_pointer
2197 ; No arguments. Create an emoty list
2198 call alloc_cons
; in RAX
2199 mov [rax
], BYTE maltype_empty_list
2203 push r9
; File name string
2205 mov rcx
, r10
; value (list)
2206 mov rdi
, argv_symbol
; symbol (*ARGV*)
2207 mov rsi
, [repl_env
] ; environment
2210 mov rsi
, run_script_string
; load-file function
2211 mov edx, run_script_string.len
2212 call raw_to_string
; String in RAX
2215 pop rdx
; File name string
2216 call string_append_string
2219 call string_append_char
2221 call string_append_char
; closing brace
2223 ; Read-Eval-Print "(load-file <file>)"