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 ;; Command to run, appending the name of the script to run
74 static run_script_string
, db "(load-file ",34
77 ;; ----------------------------------------------
81 ;; A handler consists of:
82 ;; - A stack pointer address to reset to
83 ;; - An address to jump to
84 ;; - An optional data structure to pass
86 ;; When jumped to, an error handler will be given:
87 ;; - the object thrown in RSI
88 ;; - the optional data structure in RDI
92 ;; Add an error handler to the front of the list
94 ;; Input: RSI - Stack pointer
95 ;; RDI - Address to jump to
96 ;; RCX - Data structure. Set to zero for none.
97 ;; If not zero, reference count incremented
99 ;; Modifies registers:
104 ; car will point to a list (stack, addr, data)
105 ; cdr will point to the previous handler
106 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
107 mov rbx
, [error_handler
]
108 cmp rbx
, 0 ; Check if previous handler was zero
109 je .create_handler
; Zero, so leave null
110 ; Not zero, so create pointer to it
111 mov [rax
+ Cons.typecdr
], BYTE content_pointer
112 mov [rax
+ Cons.cdr
], rbx
114 ; note: not incrementing reference count, since
115 ; we're replacing one reference with another
117 mov [error_handler
], rax
; new error handler
121 mov [rdx
+ Cons.car
], rax
122 ; Store stack pointer
123 mov [rax
], BYTE (block_cons
+ container_list
+ content_function
)
124 mov [rax
+ Cons.car
], rsi
; stack pointer
128 mov [rdx
+ Cons.typecdr
], BYTE content_pointer
129 mov [rdx
+ Cons.cdr
], rax
130 ; Store function pointer to jump to
131 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
132 mov [rax
+ Cons.car
], rdi
134 ; Check if there is an object to pass to handler
138 ; Set the final CDR to point to the object
139 mov [rax
+ Cons.typecdr
], BYTE content_pointer
140 mov [rax
+ Cons.cdr
], rcx
149 ;; Removes an error handler from the list
151 ;; Modifies registers:
157 mov rsi
, [error_handler
]
159 je .done
; Nothing to remove
162 mov rsi
, [rsi
+ Cons.cdr
] ; next handler
163 mov [error_handler
], rsi
164 call incref_object
; needed because releasing soon
166 pop rsi
; handler being removed
174 ;; Object to pass to handler should be in RSI
176 ; Get the next error handler
177 mov rax
, [error_handler
]
182 mov rax
, [rax
+ Cons.car
] ; handler
183 mov rbx
, [rax
+ Cons.car
] ; stack pointer
184 mov rax
, [rax
+ Cons.cdr
]
185 mov rcx
, [rax
+ Cons.car
] ; function
186 mov rdi
, [rax
+ Cons.cdr
] ; data structure
191 ; Jump to the handler
195 ; Print the object in RSI then quit
197 je .done
; nothing to print
198 mov rdi
, 1 ; print_readably
205 ;; ----------------------------------------------
208 ;; Inputs: RSI Form to evaluate
212 mov r15
, rdi
; Save Env in r15
217 ; Check if this is a list
219 and ah, container_mask
220 cmp ah, container_list
223 cmp ah, container_map
226 cmp ah, container_vector
229 ; Not a list, map or vector
230 cmp ah, container_symbol
233 ; Not a symbol, list, map or vector
234 call incref_object
; Increment reference count
240 ; Check if first character of symbol is ':'
241 mov al, BYTE [rsi
+ Array.data
]
245 ; look in environment
248 ; symbol is the key in rdi
252 je .done
; result in RAX
254 ; Not found, throw an error
256 print_str_mac error_string
; print 'Error: '
260 mov edx, [rsi
+ Array.
length]
262 call print_rawstring
; print symbol
264 print_str_mac not_found_string
; print ' not found'
269 ; ------------------------------
272 ; Just return keywords unaltered
277 ; ------------------------------
279 ; Evaluate each element of the list
281 xor r8
, r8
; The list to return
282 ; r9 contains head of list
285 mov al, BYTE [rsi
] ; Check type
288 cmp ah, content_pointer
291 ; A value in RSI, so copy
296 add bl, (block_cons
+ container_list
)
297 mov [rax
], BYTE bl ; set type
298 mov rbx
, [rsi
+ Cons.car
]
299 mov [rax
+ Cons.car
], rbx
; copy value
305 ; List element is a pointer to something
310 mov rsi
, [rsi
+ Cons.car
] ; Get the address
314 call incref_object
; Environment increment refs
317 call eval
; Evaluate it, result in rax
323 ; Check the type it's evaluated to
326 and bh, (block_mask
+ container_mask
)
327 cmp bh, (block_cons
+ container_value
)
330 ; Not a value, so need a pointer to it
333 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
334 pop rbx
; Address to point to
335 mov [rax
+ Cons.car
], rbx
339 ; Got value in RAX, so copy
341 call alloc_cons
; Copy in RAX
342 pop rbx
; Value to copy in RBX
345 or cl, (block_cons
+ container_list
)
346 mov [rax
], BYTE cl ; set type
347 mov rcx
, [rbx
+ Cons.car
]
348 mov [rax
+ Cons.car
], rcx
; copy value
350 ; Release the value in RBX
358 ; Fall through to .list_append
362 cmp r8
, 0 ; Check if this is the first
366 mov [r9
+ Cons.cdr
], rax
367 mov [r9
+ Cons.typecdr
], BYTE content_pointer
374 ; fall through to .list_next
377 ; Check if there's another
378 mov al, BYTE [rsi
+ Cons.typecdr
]
379 cmp al, content_pointer
380 jne .list_done
; finished list
381 mov rsi
, [rsi
+ Cons.cdr
] ; next in list
385 mov rax
, r8
; Return the list
388 ; ---------------------
390 ; Create a new map, evaluating all the values
392 ; Check if the map is empty
393 cmp al, maltype_empty_map
396 ; map empty. Just return it
403 mov r10
, rsi
; input in R10
404 xor r12
, r12
; New map in r12
406 ; Now loop through each key-value pair
407 ; NOTE: This method relies on the implementation
412 call alloc_cons
; New Cons in RAX
414 mov bl, [r10
+ Cons.typecar
] ; Type in BL
415 mov [rax
+ Cons.typecar
], bl
416 mov rcx
, [r10
+ Cons.car
] ; Value in RCX
417 mov [rax
+ Cons.car
], rcx
419 ; Check the type of the key
421 cmp bl, content_pointer
422 jne .map_got_key
; a value
424 ; a pointer, so increment reference count
425 mov bx, WORD [rcx
+ Cons.refcount
]
427 mov [rcx
+ Cons.refcount
], WORD bx
439 ; Appending to previous value in r13
440 mov [r13
+ Cons.typecdr
], BYTE content_pointer
441 mov [r13
+ Cons.cdr
], rax
445 ; Check that we have a value
446 mov al, BYTE [r10
+ Cons.typecdr
]
447 cmp al, content_pointer
448 jne .map_error_missing_value
449 mov r10
, [r10
+ Cons.cdr
]
451 ; Now got value in r10
453 ; Check the type of the value
454 mov bl, [r10
+ Cons.typecar
] ; Type in BL
456 cmp bl, content_pointer
457 je .map_value_pointer
459 ; Not a pointer, so make a copy
461 mov bl, [r10
+ Cons.typecar
]
462 mov [rax
+ Cons.typecar
], bl
463 mov rcx
, [r10
+ Cons.car
]
464 mov [rax
+ Cons.car
], rcx
468 ; A pointer, so need to evaluate
470 push r12
; start of result
471 push r13
; Current head of result
473 mov rsi
, [r10
+ Cons.car
] ; Get the address
477 call incref_object
; Environment increment refs
480 call eval
; Evaluate it, result in rax
486 ; Check the type it's evaluated to
489 and bh, (block_mask
+ container_mask
)
490 cmp bh, (block_cons
+ container_value
)
492 jne .map_eval_pointer
494 ; A value, so just change the type to a map
496 add bl, (block_cons
+ container_map
)
501 ; Not a value, so need a pointer to it
504 mov [rax
], BYTE (block_cons
+ container_map
+ content_pointer
)
505 pop rbx
; Address to point to
506 mov [rax
+ Cons.car
], rbx
509 ; Append RAX to list in R13
510 mov [r13
+ Cons.typecdr
], BYTE content_pointer
511 mov [r13
+ Cons.cdr
], rax
514 ; Check if there's another key
515 mov al, BYTE [r10
+ Cons.typecdr
]
516 cmp al, content_pointer
517 jne .map_done
; finished map
518 mov r10
, [r10
+ Cons.cdr
] ; next in map
525 .
map_error_missing_value:
529 ; ------------------------------
531 ; Evaluate each element of the vector
533 xor r8
, r8
; The vector to return
534 ; r9 contains head of vector
537 mov al, BYTE [rsi
] ; Check type
540 cmp ah, content_pointer
547 add bl, (block_cons
+ container_vector
)
548 mov [rax
], BYTE bl ; set type
549 mov rbx
, [rsi
+ Cons.car
]
550 mov [rax
+ Cons.car
], rbx
; copy value
556 ; Vector element is a pointer to something
561 mov rsi
, [rsi
+ Cons.car
] ; Get the address
565 call incref_object
; Environment increment refs
568 call eval
; Evaluate it, result in rax
574 ; Check the type it's evaluated to
577 and bh, (block_mask
+ container_mask
)
578 cmp bh, (block_cons
+ container_value
)
579 je .vector_eval_value
581 ; Not a value, so need a pointer to it
584 mov [rax
], BYTE (block_cons
+ container_vector
+ content_pointer
)
585 pop rbx
; Address to point to
586 mov [rax
+ Cons.car
], rbx
590 ; Got value in RAX, so copy
592 call alloc_cons
; Copy in RAX
593 pop rbx
; Value to copy in RBX
596 or cl, (block_cons
+ container_vector
)
597 mov [rax
], BYTE cl ; set type
598 mov rcx
, [rbx
+ Cons.car
]
599 mov [rax
+ Cons.car
], rcx
; copy value
601 ; Release the value in RBX
612 cmp r8
, 0 ; Check if this is the first
616 mov [r9
+ Cons.cdr
], rax
617 mov [r9
+ Cons.typecdr
], BYTE content_pointer
624 ; fall through to .vector_next
627 ; Check if there's another
628 mov al, BYTE [rsi
+ Cons.typecdr
]
629 cmp al, content_pointer
630 jne .vector_done
; finished vector
631 mov rsi
, [rsi
+ Cons.cdr
] ; next in vector
635 mov rax
, r8
; Return the vector
638 ; ---------------------
644 ;; Comparison of symbols for eval function
645 ;; Compares the symbol in RSI with specified symbol
646 ;; Preserves RSI and RBX
648 %macro eval_cmp_symbol
1
653 call compare_char_array
656 test rax
, rax
; ZF set if rax = 0 (equal)
659 ;; ----------------------------------------------------
662 ;; Input: RSI Form to evaluate
665 ;; Returns: Result in RAX
667 ;; Note: The environment in RDI will have its reference count
668 ;; reduced by one (released). This is to make tail call optimisation easier
675 cmp al, maltype_empty_list
678 and al, container_mask
679 cmp al, container_list
682 ; Not a list. Evaluate and return
684 jmp .return
; Releases Env
686 ; --------------------
690 ; Check if the first element is a symbol
694 cmp al, content_pointer
697 mov rbx
, [rsi
+ Cons.car
]
699 cmp al, maltype_symbol
702 ; Is a symbol, address in RBX
704 ; Compare against special form symbols
706 eval_cmp_symbol def_symbol
; def!
709 eval_cmp_symbol let_symbol
; let*
712 eval_cmp_symbol do_symbol
; do
715 eval_cmp_symbol if_symbol
; if
718 eval_cmp_symbol fn_symbol
; fn
721 eval_cmp_symbol quote_symbol
; quote
724 eval_cmp_symbol quasiquote_symbol
; quasiquote
725 je .quasiquote_symbol
731 ; Define a new symbol in current environment
733 ; Next item should be a symbol
734 mov al, BYTE [rsi
+ Cons.typecdr
]
735 cmp al, content_pointer
736 jne .def_error_missing_arg
737 mov rsi
, [rsi
+ Cons.cdr
]
739 ; Now should have a symbol
741 mov al, BYTE [rsi
+ Cons.typecar
]
743 cmp al, content_pointer
744 jne .def_error_expecting_symbol
745 mov r8
, [rsi
+ Cons.car
] ; Symbol (?)
748 cmp al, maltype_symbol
749 jne .def_error_expecting_symbol
751 ; R8 now contains a symbol
753 ; expecting a value or pointer next
754 mov al, BYTE [rsi
+ Cons.typecdr
]
755 cmp al, content_pointer
756 jne .def_error_missing_arg
757 mov rsi
, [rsi
+ Cons.cdr
]
759 ; Check if this is a pointer
763 cmp ah, content_pointer
769 pop rbx
; BL now contains type
771 add bl, (block_cons
+ container_value
)
773 mov rcx
, [rsi
+ Cons.car
]
774 mov [rax
+ Cons.car
], rcx
780 ; A pointer, so evaluate
782 ; This may throw an error, so define a handler
787 mov rsi
, [rsi
+ Cons.car
] ; Pointer
791 call incref_object
; Environment increment refs
792 xchg rsi
, rdi
; since it will be decremented by eval
800 ; Symbol in R8, value in RSI
801 mov rdi
, r8
; key (symbol)
803 mov rsi
, r15
; Environment
809 .
def_error_missing_arg:
810 mov rsi
, def_missing_arg_string
811 mov rdx
, def_missing_arg_string.len
812 jmp .def_handle_error
814 .
def_error_expecting_symbol:
815 mov rsi
, def_expecting_symbol_string
816 mov rdx
, def_expecting_symbol_string.len
817 jmp .def_handle_error
822 print_str_mac error_string
; print 'Error: '
826 call print_rawstring
; print message
828 xor rsi
, rsi
; no object to throw
829 jmp error_throw
; No return
831 ; -----------------------------
833 ; Create a new environment
835 mov r11
, rsi
; Let form in R11
837 mov rsi
, r15
; Outer env
838 call env_new
; Increments R15's ref count
839 mov r14
, rax
; New environment in R14
842 call release_object
; Decrement R15 ref count
844 ; Second element should be the bindings
846 mov al, BYTE [r11
+ Cons.typecdr
]
847 cmp al, content_pointer
848 jne .let_error_missing_bindings
849 mov r11
, [r11
+ Cons.cdr
]
853 cmp al, content_pointer
854 jne .let_error_bindings_list
856 mov r12
, [r11
+ Cons.car
] ; should be bindings list
858 and al, (block_mask
+ container_mask
)
859 ; Can be either a list or vector
860 cmp al, block_cons
+ container_list
862 cmp al, block_cons
+ container_vector
865 ; Not a list or vector
866 jmp .let_error_bindings_list
869 ; R12 now contains a list with an even number of items
870 ; The first should be a symbol, then a value to evaluate
875 cmp al, content_pointer
876 jne .let_error_bind_symbol
878 mov r13
, [r12
+ Cons.car
] ; Symbol (?)
880 cmp al, maltype_symbol
881 jne .let_error_bind_symbol
883 ; R13 now contains a symbol to bind
884 ; The next item in the bindings list (R12)
885 ; should be a value or expression to evaluate
887 mov al, BYTE [r12
+ Cons.typecdr
]
889 cmp al, content_pointer
890 jne .let_error_bind_value
891 mov r12
, [r12
+ Cons.cdr
]
895 ; Check the type of the value
896 mov bl, [r12
+ Cons.typecar
] ; Type in BL
898 cmp bl, content_pointer
899 je .let_value_pointer
901 ; Not a pointer, so make a copy
903 mov bl, [r12
+ Cons.typecar
]
905 ;or bl, (block_cons + container_value) ; 0
906 mov [rax
+ Cons.typecar
], bl
907 mov rcx
, [r12
+ Cons.car
]
908 mov [rax
+ Cons.car
], rcx
913 ; A pointer, so need to evaluate
914 push r11
; let* form list
915 push r12
; Position in bindings list
916 push r13
; symbol to bind
917 push r14
; new environment
918 mov rsi
, [r12
+ Cons.car
] ; Get the address
920 call eval
; Evaluate it, result in rax
934 mov rsi
, rcx
; The value
937 ; Check if there are more bindings
938 mov al, BYTE [r12
+ Cons.typecdr
]
939 cmp al, content_pointer
940 jne .let_done_binding
941 mov r12
, [r12
+ Cons.cdr
] ; Next
946 ; Evaluate next item in let* form in new environment
948 mov al, BYTE [r11
+ Cons.typecdr
]
949 cmp al, content_pointer
950 jne .let_error_missing_body
951 mov r11
, [r11
+ Cons.cdr
] ; Now contains value to evaluate
952 ; Check type of the value
954 and al, block_mask
+ content_mask
955 cmp al, content_pointer
958 ; Just a value, so copy
962 mov [rax
], BYTE bl ; set type
963 mov rbx
, [r11
+ Cons.car
]
964 mov [rax
+ Cons.car
], rbx
; copy value
968 ; Evaluate using new environment
970 mov rsi
, [r11
+ Cons.car
] ; Object pointed to
971 mov rdi
, r14
; New environment
974 ; Note: eval will release the new environment on return
977 ; Release the new environment
982 ret ; already released env
984 .
let_error_missing_bindings:
985 mov rsi
, let_missing_bindings_string
986 mov rdx
, let_missing_bindings_string.len
987 jmp .let_handle_error
989 .
let_error_bindings_list: ; expected a list or vector, got something else
990 mov rsi
, let_bindings_list_string
991 mov rdx
, let_bindings_list_string.len
992 jmp .let_handle_error
994 .
let_error_bind_symbol: ; expected a symbol, got something else
995 mov rsi
, let_bind_symbol_string
996 mov rdx
, let_bind_symbol_string.len
997 jmp .let_handle_error
999 .
let_error_bind_value: ; Missing value in binding list
1000 mov rsi
, let_bind_value_string
1001 mov rdx
, let_bind_value_string.len
1002 jmp .let_handle_error
1004 .
let_error_missing_body: ; Missing body to evaluate
1005 mov rsi
, let_missing_body_string
1006 mov rdx
, let_missing_body_string.len
1007 jmp .let_handle_error
1010 push r11
; For printing later
1015 print_str_mac error_string
; print 'Error: '
1019 call print_rawstring
; print message
1022 jmp error_throw
; No return
1024 ; -----------------------------
1027 mov r11
, rsi
; do form in RSI
1028 ; Environment in R15
1030 ; Check if there is a body
1031 mov al, BYTE [r11
+ Cons.typecdr
]
1032 cmp al, content_pointer
1033 jne .do_no_body
; error
1035 mov r11
, [r11
+ Cons.cdr
] ; Body in R11
1039 ; Need to test if this is the last form
1040 ; so we can handle tail call
1042 mov bl, BYTE [r11
+ Cons.typecdr
]
1043 cmp bl, content_pointer
1044 jne .do_body_last
; Last expression
1046 ; not the last expression
1048 ; Check if this is a value or pointer
1050 and al, block_mask
+ content_mask
1051 cmp al, content_pointer
1052 jne .do_next
; A value, so skip
1054 ; A pointer, so evaluate
1060 call incref_object
; Increase Env reference
1061 ; since eval will release Env
1063 mov rsi
, [r11
+ Cons.car
] ; Form
1065 call eval
; Result in RAX
1067 ; Another form after this.
1068 ; Discard the result of the last eval
1076 mov r11
, [r11
+ Cons.cdr
] ; Next in list
1081 ; The last form is in R11, which will be returned
1083 ; Check if this is a value or pointer
1085 and al, block_mask
+ content_mask
1086 cmp al, content_pointer
1087 jne .do_body_value_return
1088 jmp .do_body_expr_return
1090 .
do_body_value_return:
1091 ; Got a value as last form (in R11).
1094 push rax
; Type of value to return
1100 ; Allocate a Cons object to hold value
1102 pop rbx
; type in BL
1104 mov rbx
, [r11
+ Cons.car
]
1105 mov [rax
+ Cons.car
], rbx
1108 .
do_body_expr_return:
1109 ; An expression to evaluate as the last form
1110 ; Tail call optimise, jumping to eval
1111 ; Don't increment Env reference count
1113 mov rsi
, [r11
+ Cons.car
] ; Form
1115 jmp eval
; Tail call
1118 ; No expressions to evaluate. Return nil
1121 call release_object
; Release Env
1124 mov [rax
], BYTE maltype_nil
1125 mov [rax
+ Cons.typecdr
], BYTE content_nil
1128 ; -----------------------------
1131 mov r11
, rsi
; if form in R11
1132 ; Environment in R15
1134 mov al, BYTE [r11
+ Cons.typecdr
]
1135 cmp al, content_pointer
1136 jne .if_no_condition
1138 mov r11
, [r11
+ Cons.cdr
] ; Should be a condition
1140 ; Check if value or pointer
1142 and al, content_mask
1143 cmp al, content_pointer
1146 ; A pointer, so evaluate
1152 call incref_object
; Increase Env reference
1154 mov rsi
, [r11
+ Cons.car
] ; Form
1156 call eval
; Result in RAX
1160 ; Get type of result
1172 cmp bl, maltype_false
1182 cmp al, content_false
1188 ; Skip the next item
1189 mov al, BYTE [r11
+ Cons.typecdr
]
1190 cmp al, content_pointer
1193 mov r11
, [r11
+ Cons.cdr
]
1196 ; Get the next item in the list and evaluate it
1197 mov al, BYTE [r11
+ Cons.typecdr
]
1198 cmp al, content_pointer
1199 jne .return_nil
; Nothing to return
1201 mov r11
, [r11
+ Cons.cdr
]
1203 ; Check if value or pointer
1205 and al, content_mask
1206 cmp al, content_pointer
1213 and bl, content_mask
1215 mov rbx
, [r11
+ Cons.car
]
1216 mov [rax
+ Cons.car
], rbx
1221 mov rsi
, [r11
+ Cons.car
] ; Form
1223 jmp eval
; Tail call
1225 .
if_no_condition: ; just (if) without a condition
1227 print_str_mac error_string
1228 print_str_mac if_missing_condition_string
1230 ; Release environment
1233 xor rsi
, rsi
; No object to throw
1238 mov [rax
], BYTE maltype_nil
1239 mov [rax
+ Cons.typecdr
], BYTE content_nil
1243 ; Release environment
1250 ; -----------------------------
1253 mov r11
, rsi
; fn form in R11
1254 ; Environment in R15
1256 ; Get the binds and body of the function
1257 mov al, BYTE [r11
+ Cons.typecdr
]
1258 cmp al, content_pointer
1261 mov r11
, [r11
+ Cons.cdr
]
1263 and al, content_mask
1264 cmp al, content_pointer
1265 jne .fn_binds_not_list
1267 mov r12
, [r11
+ Cons.car
] ; Should be binds list
1269 and al, (block_mask
+ container_mask
)
1270 cmp al, (block_cons
+ container_list
)
1271 je .fn_got_binds
; Can be list
1272 cmp al, (block_cons
+ container_vector
)
1273 je .fn_got_binds
; or vector
1274 jmp .fn_binds_not_list
1278 ; Next get the body of the function
1279 mov al, BYTE [r11
+ Cons.typecdr
]
1280 cmp al, content_pointer
1283 mov r11
, [r11
+ Cons.cdr
]
1284 ; Check value or pointer
1286 and al, content_mask
1287 cmp al, content_pointer
1288 jne .fn_is_value
; Body in r11
1289 mov r11
, [r11
+ Cons.car
]
1293 ; Body is just a value, no expression
1294 mov [r11
], BYTE al ; Mark as value, not list
1298 ; Now put into function type
1299 ; Addr is "apply_fn", the address to call
1305 mov [rax
], BYTE (block_cons
+ container_function
+ content_function
)
1307 mov [rax
+ Cons.car
], rbx
; Address of apply function
1308 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1310 mov r13
, rax
; Return list in R13
1313 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1314 mov [rax
+ Cons.car
], r15
; Environment
1315 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1317 mov [r13
+ Cons.cdr
], rax
; Append to list
1326 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1327 mov [rax
+ Cons.car
], r12
; Binds list
1328 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1330 mov [r14
+ Cons.cdr
], rax
; Append to list
1339 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1340 mov [rax
+ Cons.car
], r11
; Body of function
1342 mov [r14
+ Cons.cdr
], rax
1355 mov [rax
], BYTE maltype_nil
1356 mov [rax
+ Cons.typecdr
], BYTE content_nil
1359 ; -----------------------------
1362 ; Just return the arguments in rsi cdr
1364 mov al, BYTE [rsi
+ Cons.typecdr
]
1365 cmp al, content_pointer
1366 jne .return_nil
; quote empty, so return nil
1368 mov rsi
, [rsi
+ Cons.cdr
]
1370 ; Check if this is a value or pointer
1371 mov al, BYTE [rsi
+ Cons.typecar
]
1372 and al, content_mask
1373 cmp al, content_pointer
1376 ; RSI contains a value. Remove the list container
1377 mov [rsi
+ Cons.typecar
], BYTE al
1383 ; RSI contains a pointer, so get the object pointed to
1384 mov rsi
, [rsi
+ Cons.car
]
1389 ; -----------------------------
1392 ; call quasiquote function with first argument
1394 mov al, BYTE [rsi
+ Cons.typecdr
]
1395 cmp al, content_pointer
1396 jne .return_nil
; quasiquote empty, so return nil
1398 mov r11
, rsi
; Save original AST in R11
1400 mov rsi
, [rsi
+ Cons.cdr
]
1402 ; Check if this is a value or pointer
1403 mov al, BYTE [rsi
+ Cons.typecar
]
1404 and al, content_mask
1405 cmp al, content_pointer
1406 je .quasiquote_pointer
1408 ; RSI contains a value. Remove the list container
1409 mov [rsi
+ Cons.typecar
], BYTE al
1414 .
quasiquote_pointer:
1415 ; RSI contains a pointer, so get the object pointed to
1416 mov rsi
, [rsi
+ Cons.car
]
1421 push r15
; Environment
1422 push r11
; Original AST
1427 call release_object
; Release old AST
1429 pop rdi
; Environment
1431 jmp eval
; Tail call
1433 ; -----------------------------
1437 mov rdi
, r15
; Environment
1439 call eval_ast
; List of evaluated forms in RAX
1445 ; This point can be called to run a function
1448 ; Inputs: RAX - List with function as first element
1449 ; NOTE: This list is released
1451 ; Check that the first element of the return is a function
1453 and bl, content_mask
1454 cmp bl, content_pointer
1455 jne .list_not_function
1457 mov rbx
, [rax
+ Cons.car
] ; Get the address
1459 cmp cl, maltype_function
1460 jne .list_not_function
1462 ; Check the rest of the args
1463 mov cl, BYTE [rax
+ Cons.typecdr
]
1464 cmp cl, content_pointer
1470 mov [rax
], BYTE maltype_empty_list
1473 jmp .list_function_call
1475 mov rsi
, [rax
+ Cons.cdr
] ; Rest of list
1476 .
list_function_call:
1477 ; Call the function with the rest of the list in RSI
1479 mov rdx
, rax
; List to release
1480 mov rdi
, rbx
; Function object in RDI
1482 mov rbx
, [rbx
+ Cons.car
] ; Call function
1484 je apply_fn
; Jump to user function apply
1486 ; A built-in function, so call (no recursion)
1494 pop rsi
; eval'ed list
1500 jmp .return
; Releases Env
1503 ; Not a function. Probably an error
1509 print_str_mac error_string
1510 print_str_mac eval_list_not_function
1515 ;; Applies a user-defined function
1517 ;; Input: RSI - Arguments to bind
1518 ;; RDI - Function object
1519 ;; RDX - list to release after binding
1520 ;; R15 - Env (will be released)
1522 ;; Output: Result in RAX
1525 ; Extract values from the list in RDI
1526 mov rax
, [rdi
+ Cons.cdr
]
1527 mov rsi
, [rax
+ Cons.car
] ; Env
1528 mov rax
, [rax
+ Cons.cdr
]
1529 mov rdi
, [rax
+ Cons.car
] ; Binds
1530 mov rax
, [rax
+ Cons.cdr
]
1531 mov rax
, [rax
+ Cons.car
] ; Body
1534 ; Check the type of the body
1536 and bl, block_mask
+ container_mask
1538 ; Just a value (in RAX). No eval needed
1544 ; Release the list passed in RDX
1548 ; Release the environment
1555 ; Create a new environment, binding arguments
1562 mov rdi
, rax
; New environment in RDI
1564 ; Release the list passed in RDX
1569 ; Release the environment
1575 jmp eval
; Tail call
1576 ; The new environment (in RDI) will be released by eval
1579 ;; Set ZF if RSI is a non-empty list or vector
1580 ;; Modifies RAX, does not modify RSI
1584 jnz .false
; Not a Cons
1585 cmp al, maltype_empty_list
1586 je .false
; Empty list
1587 cmp al, maltype_empty_vector
1588 je .false
; Empty vector
1590 ; Something non empty
1591 and al, container_mask
1592 cmp al, container_list
1594 cmp al, container_vector
1596 ; Not a list or vector -> false
1600 and ah, 255-64 ; clear zero flag
1605 or ah, 64 ; set zero flag
1609 ;; Called by eval with AST in RSI
1611 ; i. Check if AST is an empty list
1615 ; ii. Check if the first element of RSI is the symbol
1619 and al, content_mask
1620 cmp al, content_pointer
1621 jne .not_unquote
; Not a pointer
1623 mov rdi
, [rsi
+ Cons.car
] ; Get the pointer
1625 cmp cl, maltype_symbol
1628 ; Compare against 'unquote'
1632 mov rsi
, unquote_symbol
1633 call compare_char_array
1642 ; iii. Handle splice-unquote
1643 ; RSI -> ( ( splice-unquote ? ) ? )
1645 ; Test if RSI contains a pointer
1647 cmp al, content_pointer
1650 mov rbx
, [rsi
+ Cons.car
] ; Get the object pointer
1652 ; RBX -> ( splice-unquote ? )
1657 jne .not_splice
; First element not a pair
1659 ; Check if this list in RBX starts with 'splice-unquote' symbol
1661 and al, content_mask
1662 cmp al, content_pointer
1666 mov rdi
, [rbx
+ Cons.car
] ; Get the pointer
1668 cmp al, maltype_symbol
1674 ; Compare against 'splice-unquote'
1675 mov rsi
, splice_unquote_symbol
1676 call compare_char_array
1686 ; iv. Cons first and rest of AST in RSI
1688 ; Check if this is the end of the list
1689 mov al, BYTE [rsi
+ Cons.typecdr
]
1690 cmp al, content_pointer
1691 jne .quote_ast
; Put in quote
1693 ; Not the end of the AST, so need to cons
1694 ; check if pointer or value
1696 and cl, content_mask
1697 cmp cl, content_pointer
1702 or cl, container_list
1703 mov [rax
], BYTE cl ; List + Content
1704 mov rbx
, [rsi
+ Cons.car
]
1705 mov [rax
+ Cons.car
], rbx
1710 ; Get the pointer and call quasiquote
1712 mov rsi
, [rsi
+ Cons.car
]
1718 mov [rax
], BYTE (container_list
+ content_pointer
)
1719 mov [rax
+ Cons.car
], rcx
1723 ; Have Cons with first object in RCX
1725 ; Call quasiquote on the rest of the AST
1727 mov rsi
, [rsi
+ Cons.cdr
]
1729 mov rdx
, rax
; List in RDX
1730 pop rcx
; Value in RCX
1733 ; Work from the end of the list to the front
1736 mov [rax
], BYTE (container_list
+ content_pointer
)
1737 mov [rax
+ Cons.car
], rdx
; The rest of AST
1739 ; Link to the RCX Cons
1740 mov [rcx
+ Cons.typecdr
], BYTE content_pointer
1741 mov [rcx
+ Cons.cdr
], rax
1744 call alloc_cons
; Cons for cons symbol
1745 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1746 mov [rax
+ Cons.cdr
], rdx
1749 ; Get the cons symbol
1750 mov rsi
, cons_symbol
1753 mov [rdx
], BYTE (container_list
+ content_pointer
)
1754 mov [rdx
+ Cons.car
], rsi
1760 ; Return (quote RSI)
1762 call incref_object
; RSI reference count
1766 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
1767 mov [rax
+ Cons.car
], rsi
1770 ; Cons for quote symbol
1773 mov [rbx
+ Cons.typecdr
], BYTE content_pointer
1774 mov [rbx
+ Cons.cdr
], rsi
1776 ; Get a quote symbol, incrementing references
1777 mov rsi
, quote_symbol
1780 ; Put into the Cons in RBX
1781 mov [rbx
+ Cons.car
], rsi
1782 mov [rbx
], BYTE (block_cons
+ container_list
+ content_pointer
)
1785 ; -----------------------
1789 ; Got unquote symbol. Return second element of RSI
1790 mov al, BYTE [rsi
+ Cons.typecdr
]
1791 cmp al, content_pointer
1792 jne .empty_list
; No second element
1794 mov rsi
, [rsi
+ Cons.cdr
]
1796 ; Check if it's a value or pointer
1798 and cl, content_mask
1799 cmp cl, content_pointer
1802 ; A value, so need a new Cons
1804 mov [rax
], BYTE cl ; content
1805 mov rbx
, [rsi
+ Cons.car
]
1806 mov [rax
+ Cons.car
], rbx
; Copy content
1810 mov rsi
, [rsi
+ Cons.car
]
1815 ; -----------------------
1817 ; RSI -> ( RBX->( splice-unquote A ) B )
1819 ; RBX Car points to splice-unquote symbol
1821 ; Check if there is anything after the symbol
1822 mov al, BYTE [rbx
+ Cons.typecdr
]
1823 cmp al, content_pointer
1824 jne .splice_unquote_empty
1826 ; Point to the second element of the splice-unquote list
1827 mov rcx
, [rbx
+ Cons.cdr
]
1829 ; Check whether it's a value or pointer
1831 and al, content_mask
1832 cmp al, content_pointer
1833 je .splice_unquote_pointer
1835 ; A value, so change the container to a value
1837 ; Remove pointer from RBX
1838 mov [rbx
+ Cons.typecdr
], BYTE 0
1839 jmp .splice_unquote_first
; Got the value in RCX
1841 .
splice_unquote_pointer:
1842 mov rcx
, [rcx
+ Cons.car
] ; Get the object pointed to
1845 xchg rcx
, rsi
; Object in RCX
1847 .
splice_unquote_first: ; Got the first object in RCX
1849 ; Check if RSI contains anything else
1850 mov al, BYTE [rsi
+ Cons.typecdr
]
1851 cmp al, content_pointer
1852 jne .splice_unquote_notail
1854 mov rsi
, [rsi
+ Cons.cdr
]
1857 ; ( ( splice-unquote A ) B )
1859 ; Need to call quasiquote on the rest of the list
1864 ; Need to concat rcx and rdx
1865 ; Work from the end of the list to the front
1868 mov [rax
], BYTE (container_list
+ content_pointer
)
1869 mov [rax
+ Cons.car
], rdx
; The rest of AST
1870 mov rdx
, rax
; Push list into RDX
1873 mov [rax
], BYTE (container_list
+ content_pointer
)
1874 mov [rax
+ Cons.car
], rcx
; The splice-unquote object
1875 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1876 mov [rax
+ Cons.cdr
], rdx
1879 call alloc_cons
; Cons for concat symbol
1880 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1881 mov [rax
+ Cons.cdr
], rdx
1884 ; Get the concat symbol
1885 mov rsi
, concat_symbol
1888 mov [rdx
], BYTE (container_list
+ content_pointer
)
1889 mov [rdx
+ Cons.car
], rsi
1894 .
splice_unquote_notail:
1895 ; Just return the object in RCX
1896 ; since nothing to concatenate with
1900 .
splice_unquote_empty:
1901 ; Nothing in the (splice-unquote) list, so ignore
1902 ; Just call quasiquote on the rest of RSI
1904 mov al, BYTE [rsi
+ Cons.typecdr
]
1905 cmp al, content_pointer
1906 jne .empty_list
; Nothing else
1908 mov rsi
, [rsi
+ Cons.cdr
]
1909 jmp quasiquote
; Tail call
1912 ; Return an empty list
1914 mov [rax
], BYTE maltype_empty_list
1921 ;; Read-Eval-Print in sequence
1923 ;; Input string in RSI
1932 mov rsi
, rax
; Form to evaluate
1933 mov rdi
, [repl_env
] ; Environment
1936 call incref_object
; Environment increment refs
1937 xchg rsi
, rdi
; since it will be decremented by eval
1940 push rax
; Save result of eval
1947 mov rdi
, 1 ; print_readably
1949 push rax
; Save output string
1951 mov rsi
, rax
; Put into input of print_string
1954 ; Release string from pr_str
1958 ; Release result of eval
1962 ; Release the object from read_str
1964 call release_object
; Could be Cons or Array
1969 ; Create and print the core environment
1970 call core_environment
; Environment in RAX
1972 mov [repl_env
], rax
; store in memory
1974 ; Set the error handler
1975 mov rsi
, rsp
; Stack pointer
1976 mov rdi
, .catch
; Address to jump to
1977 xor rcx
, rcx
; No data
1978 call error_handler_push
1980 ; Evaluate the startup string
1982 mov rsi
, mal_startup_string
1983 mov edx, mal_startup_string.len
1984 call raw_to_string
; String in RAX
1988 call read_str
; AST in RAX
1992 call release_array
; string
1996 mov rdi
, [repl_env
] ; Environment
1999 call incref_object
; Environment increment refs
2000 xchg rsi
, rdi
; since it will be decremented by eval
2006 call release_object
; AST
2008 call release_object
; Return from eval
2010 ; -----------------------------
2011 ; Check command-line arguments
2013 pop rax
; Number of arguments
2014 cmp rax
, 1 ; Always have at least one, the path to executable
2017 ; No extra arguments, so just set *ARGV* to an empty list
2018 call alloc_cons
; in RAX
2019 mov [rax
], BYTE maltype_empty_list
2020 mov rcx
, rax
; value (empty list)
2021 mov rdi
, argv_symbol
; symbol (*ARGV*)
2022 mov rsi
, [repl_env
] ; environment
2025 ; -----------------------------
2030 print_str_mac prompt_string
2034 ; Check if we have a zero-length string
2035 cmp DWORD [rax
+Array.
length], 0
2038 push rax
; Save address of the input string
2044 ; Release the input string
2054 ; Jumps here on error
2056 ; Check if an object was thrown
2058 je .catch_done_print
; nothing to print
2064 jmp .mainLoop
; Go back to the prompt
2069 ; Called with number of command-line arguments in RAX
2071 pop rbx
; executable
2074 pop rsi
; Address of first arg
2075 call cstring_to_string
; string in RAX
2078 ; get the rest of the args
2083 ; Got some arguments
2085 ; Got an argument left.
2086 pop rsi
; Address of C string
2087 call cstring_to_string
; String in RAX
2090 ;Make a Cons to point to the string
2091 call alloc_cons
; in RAX
2092 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
2093 mov [rax
+ Cons.car
], r12
2098 ; R10 zero, so first arg
2099 mov r10
, rax
; Head of list
2100 mov r11
, rax
; Tail of list
2103 ; R10 not zero, so append to list tail
2104 mov [r11
+ Cons.cdr
], rax
2105 mov [r11
+ Cons.typecdr
], BYTE content_pointer
2113 ; No arguments. Create an emoty list
2114 call alloc_cons
; in RAX
2115 mov [rax
], BYTE maltype_empty_list
2119 push r9
; File name string
2121 mov rcx
, r10
; value (list)
2122 mov rdi
, argv_symbol
; symbol (*ARGV*)
2123 mov rsi
, [repl_env
] ; environment
2126 mov rsi
, run_script_string
; load-file function
2127 mov edx, run_script_string.len
2128 call raw_to_string
; String in RAX
2131 pop rdx
; File name string
2132 call string_append_string
2135 call string_append_char
2137 call string_append_char
; closing brace
2139 ; Read-Eval-Print "(load-file <file>)"