2 ;; nasm -felf64 stepA_mal.asm && ld stepA_mal.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 defmacro_expecting_function_string
, db "defmacro expects function",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 static try_missing_catch
, db "try* missing catch*"
54 static catch_missing_symbol
, db "catch* missing symbol"
55 static catch_missing_form
, db "catch* missing form"
57 ;; Symbols used for comparison
59 static_symbol def_symbol
, 'def!'
60 static_symbol let_symbol
, 'let*'
61 static_symbol do_symbol
, 'do'
62 static_symbol if_symbol
, 'if'
63 static_symbol fn_symbol
, 'fn*'
64 static_symbol defmacro_symbol
, 'defmacro!'
65 static_symbol macroexpand_symbol
, 'macroexpand'
66 static_symbol try_symbol
, 'try*'
67 static_symbol catch_symbol
, 'catch*'
69 static_symbol argv_symbol
, '*ARGV*'
71 static_symbol quote_symbol
, 'quote'
72 static_symbol quasiquote_symbol
, 'quasiquote'
73 static_symbol unquote_symbol
, 'unquote'
74 static_symbol splice_unquote_symbol
, 'splice-unquote'
75 static_symbol concat_symbol
, 'concat'
76 static_symbol cons_symbol
, 'cons'
78 ;; Startup string. This is evaluated on startup
79 static mal_startup_string
, db "(do \
80 (def! not (fn* (a) (if a false true))) \
81 (def! load-file (fn* (f) (eval (read-string (str ",34,"(do",34," (slurp f) ",34,")",34," ))))) \
82 (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw ",34,"odd number of forms to cond",34,")) (cons 'cond (rest (rest xs))))))) \
83 (def! inc (fn* [x] (+ x 1))) \
84 (def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str ",34,"G__",34," (swap! counter inc)))))) \
85 (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs)))))))) \
86 (def! *host-language* ",34,"nasm",34,")\
90 ;; Command to run, appending the name of the script to run
91 static run_script_string
, db "(load-file ",34
93 ;; Command to run at start of REPL
94 static mal_startup_header
, db "(println (str ",34,"Mal [",34," *host-language* ",34,"]",34,"))"
98 ;; ----------------------------------------------
101 ;; Inputs: RSI Form to evaluate
105 mov r15
, rdi
; Save Env in r15
110 ; Check if this is a list
112 and ah, container_mask
113 cmp ah, container_list
116 cmp ah, container_map
119 cmp ah, container_vector
122 ; Not a list, map or vector
123 cmp ah, container_symbol
126 ; Not a symbol, list, map or vector
127 call incref_object
; Increment reference count
133 ; Check if first character of symbol is ':'
134 mov al, BYTE [rsi
+ Array.data
]
138 ; look in environment
141 ; symbol is the key in rdi
145 je .done
; result in RAX
147 ; Not found, throw an error
148 mov r11
, rsi
; Symbol in R11
151 mov rsi
, rax
; New string in RSI
154 call string_append_char
156 mov rdx
, r11
; symbol
157 call string_append_string
160 call string_append_char
164 mov rsi
, not_found_string
165 mov edx, not_found_string.len
166 call raw_to_string
; ' not found'
172 call string_append_string
181 ; ------------------------------
184 ; Just return keywords unaltered
189 ; ------------------------------
191 ; Evaluate each element of the list
193 xor r8
, r8
; The list to return
194 ; r9 contains head of list
197 mov al, BYTE [rsi
] ; Check type
200 cmp ah, content_pointer
203 ; A value in RSI, so copy
208 add bl, (block_cons
+ container_list
)
209 mov [rax
], BYTE bl ; set type
210 mov rbx
, [rsi
+ Cons.car
]
211 mov [rax
+ Cons.car
], rbx
; copy value
217 ; List element is a pointer to something
222 mov rdi
, [rsi
+ Cons.car
] ; Get the address
225 call incref_object
; Environment increment refs
226 xchg rsi
, rdi
; Env in RDI, AST in RSI
228 call incref_object
; AST increment refs
230 call eval
; Evaluate it, result in rax
236 ; Check the type it's evaluated to
239 and bh, (block_mask
+ container_mask
)
240 cmp bh, (block_cons
+ container_value
)
243 ; Not a value, so need a pointer to it
246 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
247 pop rbx
; Address to point to
248 mov [rax
+ Cons.car
], rbx
252 ; Got value in RAX, so copy
254 call alloc_cons
; Copy in RAX
255 pop rbx
; Value to copy in RBX
258 or cl, (block_cons
+ container_list
)
259 mov [rax
], BYTE cl ; set type
260 mov rcx
, [rbx
+ Cons.car
]
261 mov [rax
+ Cons.car
], rcx
; copy value
263 ; Release the value in RBX
271 ; Fall through to .list_append
275 cmp r8
, 0 ; Check if this is the first
279 mov [r9
+ Cons.cdr
], rax
280 mov [r9
+ Cons.typecdr
], BYTE content_pointer
287 ; fall through to .list_next
290 ; Check if there's another
291 mov al, BYTE [rsi
+ Cons.typecdr
]
292 cmp al, content_pointer
293 jne .list_done
; finished list
294 mov rsi
, [rsi
+ Cons.cdr
] ; next in list
298 mov rax
, r8
; Return the list
301 ; ---------------------
303 ; Create a new map, evaluating all the values
305 ; Check if the map is empty
306 cmp al, maltype_empty_map
309 ; map empty. Just return it
316 mov r10
, rsi
; input in R10
317 xor r12
, r12
; New map in r12
319 ; Now loop through each key-value pair
320 ; NOTE: This method relies on the implementation
325 call alloc_cons
; New Cons in RAX
327 mov bl, [r10
+ Cons.typecar
] ; Type in BL
328 mov [rax
+ Cons.typecar
], bl
329 mov rcx
, [r10
+ Cons.car
] ; Value in RCX
330 mov [rax
+ Cons.car
], rcx
332 ; Check the type of the key
334 cmp bl, content_pointer
335 jne .map_got_key
; a value
337 ; a pointer, so increment reference count
338 mov bx, WORD [rcx
+ Cons.refcount
]
340 mov [rcx
+ Cons.refcount
], WORD bx
352 ; Appending to previous value in r13
353 mov [r13
+ Cons.typecdr
], BYTE content_pointer
354 mov [r13
+ Cons.cdr
], rax
358 ; Check that we have a value
359 mov al, BYTE [r10
+ Cons.typecdr
]
360 cmp al, content_pointer
361 jne .map_error_missing_value
362 mov r10
, [r10
+ Cons.cdr
]
364 ; Now got value in r10
366 ; Check the type of the value
367 mov bl, [r10
+ Cons.typecar
] ; Type in BL
369 cmp bl, content_pointer
370 je .map_value_pointer
372 ; Not a pointer, so make a copy
374 mov bl, [r10
+ Cons.typecar
]
375 mov [rax
+ Cons.typecar
], bl
376 mov rcx
, [r10
+ Cons.car
]
377 mov [rax
+ Cons.car
], rcx
381 ; A pointer, so need to evaluate
383 push r12
; start of result
384 push r13
; Current head of result
386 mov rsi
, [r10
+ Cons.car
] ; Get the address
390 call incref_object
; Environment increment refs
395 call eval
; Evaluate it, result in rax
401 ; Check the type it's evaluated to
404 and bh, (block_mask
+ container_mask
)
405 cmp bh, (block_cons
+ container_value
)
407 jne .map_eval_pointer
409 ; A value, so just change the type to a map
411 add bl, (block_cons
+ container_map
)
416 ; Not a value, so need a pointer to it
419 mov [rax
], BYTE (block_cons
+ container_map
+ content_pointer
)
420 pop rbx
; Address to point to
421 mov [rax
+ Cons.car
], rbx
424 ; Append RAX to list in R13
425 mov [r13
+ Cons.typecdr
], BYTE content_pointer
426 mov [r13
+ Cons.cdr
], rax
429 ; Check if there's another key
430 mov al, BYTE [r10
+ Cons.typecdr
]
431 cmp al, content_pointer
432 jne .map_done
; finished map
433 mov r10
, [r10
+ Cons.cdr
] ; next in map
440 .
map_error_missing_value:
444 ; ------------------------------
446 ; Evaluate each element of the vector
448 xor r8
, r8
; The vector to return
449 ; r9 contains head of vector
452 mov al, BYTE [rsi
] ; Check type
455 cmp ah, content_pointer
462 add bl, (block_cons
+ container_vector
)
463 mov [rax
], BYTE bl ; set type
464 mov rbx
, [rsi
+ Cons.car
]
465 mov [rax
+ Cons.car
], rbx
; copy value
471 ; Vector element is a pointer to something
476 mov rsi
, [rsi
+ 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
)
496 je .vector_eval_value
498 ; Not a value, so need a pointer to it
501 mov [rax
], BYTE (block_cons
+ container_vector
+ content_pointer
)
502 pop rbx
; Address to point to
503 mov [rax
+ Cons.car
], rbx
507 ; Got value in RAX, so copy
509 call alloc_cons
; Copy in RAX
510 pop rbx
; Value to copy in RBX
513 or cl, (block_cons
+ container_vector
)
514 mov [rax
], BYTE cl ; set type
515 mov rcx
, [rbx
+ Cons.car
]
516 mov [rax
+ Cons.car
], rcx
; copy value
518 ; Release the value in RBX
529 cmp r8
, 0 ; Check if this is the first
533 mov [r9
+ Cons.cdr
], rax
534 mov [r9
+ Cons.typecdr
], BYTE content_pointer
541 ; fall through to .vector_next
544 ; Check if there's another
545 mov al, BYTE [rsi
+ Cons.typecdr
]
546 cmp al, content_pointer
547 jne .vector_done
; finished vector
548 mov rsi
, [rsi
+ Cons.cdr
] ; next in vector
552 mov rax
, r8
; Return the vector
555 ; ---------------------
561 ;; Comparison of symbols for eval function
562 ;; Compares the symbol in RSI with specified symbol
563 ;; Preserves RSI and RBX
565 %macro eval_cmp_symbol
1
570 call compare_char_array
573 test rax
, rax
; ZF set if rax = 0 (equal)
576 ;; ----------------------------------------------------
579 ;; Input: RSI AST to evaluate [ Released ]
580 ;; RDI Environment [ Released ]
582 ;; Returns: Result in RAX
584 ;; Note: Both the form and environment will have their reference count
585 ;; reduced by one (released). This is for tail call optimisation (Env),
586 ;; quasiquote and macroexpand (AST)
591 push rsi
; AST pushed, must be popped before return
595 cmp al, maltype_empty_list
596 je .empty_list
; empty list, return unchanged
598 and al, container_mask
599 cmp al, container_list
602 ; Not a list. Evaluate and return
604 jmp .return
; Releases Env
606 ; --------------------
611 pop rax
; Old AST, discard from stack
612 call macroexpand
; Replaces RSI
615 ; Check if RSI is a list, and if
616 ; the first element is a symbol
621 cmp al, maltype_empty_list
622 je .empty_list
; empty list, return unchanged
625 and ah, container_mask
626 cmp ah, container_list
629 ; Not a list, so call eval_ast on it
630 mov rdi
, r15
; Environment
636 cmp al, content_pointer
639 mov rbx
, [rsi
+ Cons.car
]
641 cmp al, maltype_symbol
644 ; Is a symbol, address in RBX
646 ; Compare against special form symbols
648 eval_cmp_symbol def_symbol
; def!
651 eval_cmp_symbol let_symbol
; let*
654 eval_cmp_symbol do_symbol
; do
657 eval_cmp_symbol if_symbol
; if
660 eval_cmp_symbol fn_symbol
; fn
663 eval_cmp_symbol quote_symbol
; quote
666 eval_cmp_symbol quasiquote_symbol
; quasiquote
667 je .quasiquote_symbol
669 eval_cmp_symbol defmacro_symbol
; defmacro!
672 eval_cmp_symbol macroexpand_symbol
; macroexpand
673 je .macroexpand_symbol
675 eval_cmp_symbol try_symbol
; try*
682 ; -----------------------------
688 xor r9
, r9
; Set R9 to 0
690 ; Define a new symbol in current environment
691 ; If R9 is set to 1 then defmacro
693 ; Next item should be a symbol
694 mov al, BYTE [rsi
+ Cons.typecdr
]
695 cmp al, content_pointer
696 jne .def_error_missing_arg
697 mov rsi
, [rsi
+ Cons.cdr
]
699 ; Now should have a symbol
701 mov al, BYTE [rsi
+ Cons.typecar
]
703 cmp al, content_pointer
704 jne .def_error_expecting_symbol
705 mov r8
, [rsi
+ Cons.car
] ; Symbol (?)
708 cmp al, maltype_symbol
709 jne .def_error_expecting_symbol
711 ; R8 now contains a symbol
713 ; expecting a value or pointer next
714 mov al, BYTE [rsi
+ Cons.typecdr
]
715 cmp al, content_pointer
716 jne .def_error_missing_arg
717 mov rsi
, [rsi
+ Cons.cdr
]
719 ; Check if this is a pointer
723 cmp ah, content_pointer
728 ; Test if this is defmacro!
730 jnz .defmacro_not_function
734 pop rbx
; BL now contains type
736 add bl, (block_cons
+ container_value
)
738 mov rcx
, [rsi
+ Cons.car
]
739 mov [rax
+ Cons.car
], rcx
745 ; A pointer, so evaluate
747 ; This may throw an error, so define a handler
752 mov rsi
, [rsi
+ Cons.car
] ; Pointer
756 call incref_object
; Environment increment refs
757 xchg rsi
, rdi
; since it will be decremented by eval
759 call incref_object
; AST increment refs
766 ; If this is defmacro, and the object in RSI is a function,
767 ; then change to a macro
769 jz .def_not_macro
; Not defmacro
773 cmp al, maltype_function
774 jne .defmacro_not_function
776 ; Got a function, change to macro
777 mov [rsi
], BYTE maltype_macro
785 ; Symbol in R8, value in RSI
786 mov rdi
, r8
; key (symbol)
788 mov rsi
, r15
; Environment
794 .
def_error_missing_arg:
795 mov rsi
, def_missing_arg_string
796 mov rdx
, def_missing_arg_string.len
797 jmp .def_handle_error
799 .
def_error_expecting_symbol:
800 mov rsi
, def_expecting_symbol_string
801 mov rdx
, def_expecting_symbol_string.len
802 jmp .def_handle_error
804 .
defmacro_not_function:
805 mov rsi
, defmacro_expecting_function_string
806 mov rdx
, defmacro_expecting_function_string.len
807 jmp .def_handle_error
812 print_str_mac error_string
; print 'Error: '
816 call print_rawstring
; print message
818 xor rsi
, rsi
; no object to throw
819 jmp error_throw
; No return
821 ; -----------------------------
823 ; Create a new environment
825 mov r11
, rsi
; Let form in R11
827 mov rsi
, r15
; Outer env
828 call env_new
; Increments R15's ref count
829 mov r14
, rax
; New environment in R14
832 call release_object
; Decrement R15 ref count
834 ; Second element should be the bindings
836 mov al, BYTE [r11
+ Cons.typecdr
]
837 cmp al, content_pointer
838 jne .let_error_missing_bindings
839 mov r11
, [r11
+ Cons.cdr
]
843 cmp al, content_pointer
844 jne .let_error_bindings_list
846 mov r12
, [r11
+ Cons.car
] ; should be bindings list
848 and al, (block_mask
+ container_mask
)
849 ; Can be either a list or vector
850 cmp al, block_cons
+ container_list
852 cmp al, block_cons
+ container_vector
855 ; Not a list or vector
856 jmp .let_error_bindings_list
859 ; R12 now contains a list with an even number of items
860 ; The first should be a symbol, then a value to evaluate
865 cmp al, content_pointer
866 jne .let_error_bind_symbol
868 mov r13
, [r12
+ Cons.car
] ; Symbol (?)
870 cmp al, maltype_symbol
871 jne .let_error_bind_symbol
873 ; R13 now contains a symbol to bind
874 ; The next item in the bindings list (R12)
875 ; should be a value or expression to evaluate
877 mov al, BYTE [r12
+ Cons.typecdr
]
879 cmp al, content_pointer
880 jne .let_error_bind_value
881 mov r12
, [r12
+ Cons.cdr
]
885 ; Check the type of the value
886 mov bl, [r12
+ Cons.typecar
] ; Type in BL
888 cmp bl, content_pointer
889 je .let_value_pointer
891 ; Not a pointer, so make a copy
893 mov bl, [r12
+ Cons.typecar
]
895 ;or bl, (block_cons + container_value) ; 0
896 mov [rax
+ Cons.typecar
], bl
897 mov rcx
, [r12
+ Cons.car
]
898 mov [rax
+ Cons.car
], rcx
903 ; A pointer, so need to evaluate
904 push r11
; let* form list
905 push r12
; Position in bindings list
906 push r13
; symbol to bind
907 push r14
; new environment
913 mov rsi
, [r12
+ Cons.car
] ; Get the address
915 call incref_object
; Increment ref count of AST
917 call eval
; Evaluate it, result in rax
931 mov rsi
, rcx
; The value
934 ; Check if there are more bindings
935 mov al, BYTE [r12
+ Cons.typecdr
]
936 cmp al, content_pointer
937 jne .let_done_binding
938 mov r12
, [r12
+ Cons.cdr
] ; Next
943 ; Evaluate next item in let* form in new environment
945 mov al, BYTE [r11
+ Cons.typecdr
]
946 cmp al, content_pointer
947 jne .let_error_missing_body
948 mov r11
, [r11
+ Cons.cdr
] ; Now contains value to evaluate
949 ; Check type of the value
951 and al, block_mask
+ content_mask
952 cmp al, content_pointer
955 ; Just a value, so copy
959 mov [rax
], BYTE bl ; set type
960 mov rbx
, [r11
+ Cons.car
]
961 mov [rax
+ Cons.car
], rbx
; copy value
965 ; Evaluate using new environment
967 mov rsi
, [r11
+ Cons.car
] ; Object pointed to
968 call incref_object
; will be released by eval
970 mov r11
, rsi
; save new AST
973 mov rsi
, r11
; New AST
975 mov rdi
, r14
; New environment
978 ; Note: eval will release the new environment on return
981 ; Release the new environment
992 ret ; already released env
994 .
let_error_missing_bindings:
995 mov rsi
, let_missing_bindings_string
996 mov rdx
, let_missing_bindings_string.len
997 jmp .let_handle_error
999 .
let_error_bindings_list: ; expected a list or vector, got something else
1000 mov rsi
, let_bindings_list_string
1001 mov rdx
, let_bindings_list_string.len
1002 jmp .let_handle_error
1004 .
let_error_bind_symbol: ; expected a symbol, got something else
1005 mov rsi
, let_bind_symbol_string
1006 mov rdx
, let_bind_symbol_string.len
1007 jmp .let_handle_error
1009 .
let_error_bind_value: ; Missing value in binding list
1010 mov rsi
, let_bind_value_string
1011 mov rdx
, let_bind_value_string.len
1012 jmp .let_handle_error
1014 .
let_error_missing_body: ; Missing body to evaluate
1015 mov rsi
, let_missing_body_string
1016 mov rdx
, let_missing_body_string.len
1017 jmp .let_handle_error
1020 push r11
; For printing later
1025 print_str_mac error_string
; print 'Error: '
1029 call print_rawstring
; print message
1032 jmp error_throw
; No return
1034 ; -----------------------------
1037 mov r11
, rsi
; do form in RSI
1038 ; Environment in R15
1040 ; Check if there is a body
1041 mov al, BYTE [r11
+ Cons.typecdr
]
1042 cmp al, content_pointer
1043 jne .do_no_body
; error
1045 mov r11
, [r11
+ Cons.cdr
] ; Body in R11
1049 ; Need to test if this is the last form
1050 ; so we can handle tail call
1052 mov bl, BYTE [r11
+ Cons.typecdr
]
1053 cmp bl, content_pointer
1054 jne .do_body_last
; Last expression
1056 ; not the last expression
1058 ; Check if this is a value or pointer
1060 and al, block_mask
+ content_mask
1061 cmp al, content_pointer
1062 jne .do_next
; A value, so skip
1064 ; A pointer, so evaluate
1070 call incref_object
; Increase Env reference
1071 ; since eval will release Env
1073 mov rsi
, [r11
+ Cons.car
] ; Form
1074 call incref_object
; Increment ref count since eval will release
1077 call eval
; Result in RAX
1079 ; Another form after this.
1080 ; Discard the result of the last eval
1088 mov r11
, [r11
+ Cons.cdr
] ; Next in list
1093 ; The last form is in R11, which will be returned
1095 ; Check if this is a value or pointer
1097 and al, block_mask
+ content_mask
1098 cmp al, content_pointer
1099 jne .do_body_value_return
1100 jmp .do_body_expr_return
1102 .
do_body_value_return:
1103 ; Got a value as last form (in R11).
1106 push rax
; Type of value to return
1112 ; Allocate a Cons object to hold value
1114 pop rbx
; type in BL
1116 mov rbx
, [r11
+ Cons.car
]
1117 mov [rax
+ Cons.car
], rbx
1121 mov r15
, rax
; not modified by release
1127 .
do_body_expr_return:
1128 ; An expression to evaluate as the last form
1129 ; Tail call optimise, jumping to eval
1130 ; Don't increment Env reference count
1132 mov rsi
, [r11
+ Cons.car
] ; new AST form
1133 call incref_object
; This will be released by eval
1135 mov r11
, rsi
; Save new AST
1136 pop rsi
; Remove old AST from stack
1141 jmp eval
; Tail call
1144 ; No expressions to evaluate. Return nil
1147 call release_object
; Release Env
1154 mov [rax
], BYTE maltype_nil
1155 mov [rax
+ Cons.typecdr
], BYTE content_nil
1158 ; -----------------------------
1161 mov r11
, rsi
; if form in R11
1162 ; Environment in R15
1164 mov al, BYTE [r11
+ Cons.typecdr
]
1165 cmp al, content_pointer
1166 jne .if_no_condition
1168 mov r11
, [r11
+ Cons.cdr
] ; Should be a condition
1170 ; Check if value or pointer
1172 and al, content_mask
1173 cmp al, content_pointer
1176 ; A pointer, so evaluate
1182 call incref_object
; Increase Env reference
1184 mov rsi
, [r11
+ Cons.car
] ; Form
1185 call incref_object
; Increase Form/AST ref count
1188 call eval
; Result in RAX
1192 ; Get type of result
1204 cmp bl, maltype_false
1214 cmp al, content_false
1220 ; Skip the next item
1221 mov al, BYTE [r11
+ Cons.typecdr
]
1222 cmp al, content_pointer
1225 mov r11
, [r11
+ Cons.cdr
]
1228 ; Get the next item in the list and evaluate it
1229 mov al, BYTE [r11
+ Cons.typecdr
]
1230 cmp al, content_pointer
1231 jne .return_nil
; Nothing to return
1233 mov r11
, [r11
+ Cons.cdr
]
1235 ; Check if value or pointer
1237 and al, content_mask
1238 cmp al, content_pointer
1245 and bl, content_mask
1247 mov rbx
, [r11
+ Cons.car
]
1248 mov [rax
+ Cons.car
], rbx
1253 mov rsi
, [r11
+ Cons.car
] ; Form
1254 call incref_object
; Will be released by eval
1258 call release_object
; Release old AST
1259 mov rsi
, r11
; New AST
1262 jmp eval
; Tail call
1264 .
if_no_condition: ; just (if) without a condition
1266 print_str_mac error_string
1267 print_str_mac if_missing_condition_string
1269 ; Release environment
1272 xor rsi
, rsi
; No object to throw
1277 mov [rax
], BYTE maltype_nil
1278 mov [rax
+ Cons.typecdr
], BYTE content_nil
1281 ; Release environment
1283 mov r15
, rax
; Save RAX (return value)
1287 pop rsi
; Pushed at start of eval
1290 mov rax
, r15
; return value
1293 ; -----------------------------
1296 mov r11
, rsi
; fn form in R11
1297 ; Environment in R15
1299 ; Get the binds and body of the function
1300 mov al, BYTE [r11
+ Cons.typecdr
]
1301 cmp al, content_pointer
1304 mov r11
, [r11
+ Cons.cdr
]
1306 and al, content_mask
1307 cmp al, content_pointer
1308 jne .fn_binds_not_list
1310 mov r12
, [r11
+ Cons.car
] ; Should be binds list
1312 and al, (block_mask
+ container_mask
)
1313 cmp al, (block_cons
+ container_list
)
1314 je .fn_got_binds
; Can be list
1315 cmp al, (block_cons
+ container_vector
)
1316 je .fn_got_binds
; or vector
1317 jmp .fn_binds_not_list
1321 ; Next get the body of the function
1322 mov al, BYTE [r11
+ Cons.typecdr
]
1323 cmp al, content_pointer
1326 mov r11
, [r11
+ Cons.cdr
]
1327 ; Check value or pointer
1329 and al, content_mask
1330 cmp al, content_pointer
1331 jne .fn_is_value
; Body in r11
1332 mov r11
, [r11
+ Cons.car
]
1336 ; Body is just a value, no expression
1337 mov [r11
], BYTE al ; Mark as value, not list
1341 ; Now put into function type
1342 ; Addr is "apply_fn", the address to call
1348 mov [rax
], BYTE (block_cons
+ container_function
+ content_function
)
1350 mov [rax
+ Cons.car
], rbx
; Address of apply function
1351 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1353 mov r13
, rax
; Return list in R13
1358 mov [rax
], BYTE maltype_nil
1359 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1361 mov [r13
+ Cons.cdr
], rax
; Append
1367 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1368 mov [rax
+ Cons.car
], r15
; Environment
1369 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1371 mov [r14
+ Cons.cdr
], rax
; Append to list
1382 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1383 mov [rax
+ Cons.car
], r12
; Binds list
1384 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1386 mov [r14
+ Cons.cdr
], rax
; Append to list
1395 mov [rax
], BYTE (block_cons
+ container_function
+ content_pointer
)
1396 mov [rax
+ Cons.car
], r11
; Body of function
1398 mov [r14
+ Cons.cdr
], rax
1411 mov [rax
], BYTE maltype_nil
1412 mov [rax
+ Cons.typecdr
], BYTE content_nil
1415 ; -----------------------------
1418 ; Just return the arguments in rsi cdr
1420 mov al, BYTE [rsi
+ Cons.typecdr
]
1421 cmp al, content_pointer
1422 jne .return_nil
; quote empty, so return nil
1424 mov rsi
, [rsi
+ Cons.cdr
]
1426 ; Check if this is a value or pointer
1427 mov al, BYTE [rsi
+ Cons.typecar
]
1428 and al, content_mask
1429 cmp al, content_pointer
1432 ; RSI contains a value. Remove the list container
1433 mov [rsi
+ Cons.typecar
], BYTE al
1439 ; RSI contains a pointer, so get the object pointed to
1440 mov rsi
, [rsi
+ Cons.car
]
1445 ; -----------------------------
1448 ; call quasiquote function with first argument
1450 mov al, BYTE [rsi
+ Cons.typecdr
]
1451 cmp al, content_pointer
1452 jne .return_nil
; quasiquote empty, so return nil
1454 mov r11
, rsi
; Save original AST in R11
1456 mov rsi
, [rsi
+ Cons.cdr
]
1458 ; Check if this is a value or pointer
1459 mov al, BYTE [rsi
+ Cons.typecar
]
1460 and al, content_mask
1461 cmp al, content_pointer
1462 je .quasiquote_pointer
1464 ; RSI contains a value. Remove the list container
1465 mov [rsi
+ Cons.typecar
], BYTE al
1470 .
quasiquote_pointer:
1471 ; RSI contains a pointer, so get the object pointed to
1472 mov rsi
, [rsi
+ Cons.car
]
1474 push r15
; Environment
1475 ; Original AST already on stack
1479 pop rdi
; Environment
1482 mov r11
, rax
; New AST
1483 call release_object
; Release old AST
1484 mov rsi
, r11
; New AST in RSI
1486 jmp eval
; Tail call
1488 ; -----------------------------
1489 .
macroexpand_symbol:
1490 ; Check if we have a second list element
1492 mov al, BYTE [rsi
+ Cons.typecdr
]
1493 cmp al, content_pointer
1494 jne .return_nil
; No argument
1496 mov rsi
, [rsi
+ Cons.cdr
]
1498 ; Check if this is a value or pointer
1499 mov al, BYTE [rsi
+ Cons.typecar
]
1500 and al, content_mask
1501 cmp al, content_pointer
1502 je .macroexpand_pointer
1504 ; RSI contains a value. Remove the list container
1505 mov [rsi
+ Cons.typecar
], BYTE al
1510 .
macroexpand_pointer:
1511 mov rsi
, [rsi
+ Cons.car
]
1512 call incref_object
; Since RSI will be released
1514 call macroexpand
; May release and replace RSI
1517 jmp .return
; Releases original AST
1519 ; -----------------------------
1522 ; Should have the form
1524 ; (try* A (catch* B C))
1526 ; where B is a symbol, A and C are forms to evaluate
1529 mov al, BYTE [rsi
+ Cons.typecdr
]
1530 cmp al, content_pointer
1531 jne .return_nil
; No argument
1533 mov rsi
, [rsi
+ Cons.cdr
]
1535 ; Check if this is a value or pointer
1536 mov al, BYTE [rsi
+ Cons.typecar
]
1537 and al, content_mask
1538 cmp al, content_pointer
1541 ; RSI contains a value. Copy and return
1544 mov [rax
], BYTE cl ; Set type
1545 mov rbx
, [rsi
+ Cons.car
]
1546 mov [rax
+ Cons.car
], rbx
1551 mov r8
, [rsi
+ Cons.car
] ; form A in R8
1553 ; Check second arg B
1555 mov al, BYTE [rsi
+ Cons.typecdr
]
1556 ; If nil (catchless try)
1560 cmp al, content_pointer
1561 jne .try_missing_catch
1563 mov rsi
, [rsi
+ Cons.cdr
]
1566 and al, content_mask
1567 cmp al, content_pointer
1568 jne .try_missing_catch
1570 mov r9
, [rsi
+ Cons.car
] ; (catch* B C) in R9
1573 cmp al, (container_list
+ content_pointer
)
1574 jne .try_missing_catch
1576 mov rsi
, [r9
+ Cons.car
] ; Should be catch* symbol
1578 cmp al, maltype_symbol
1579 jne .try_missing_catch
1581 mov rdi
, catch_symbol
1582 call compare_char_array
1583 test rax
, rax
; ZF set if rax = 0 (equal)
1584 jnz .try_missing_catch
1586 ; Check that B is a symbol
1587 mov al, [r9
+ Cons.typecdr
]
1588 cmp al, content_pointer
1589 jne .catch_missing_symbol
1591 mov r9
, [r9
+ Cons.cdr
] ; (B C) in R9
1594 and al, content_mask
1595 cmp al, content_pointer
1596 jne .catch_missing_symbol
1598 mov r10
, [r9
+ Cons.car
] ; B in R10
1600 cmp al, maltype_symbol
1601 jne .catch_missing_symbol
1603 mov al, BYTE [r9
+ Cons.typecdr
]
1604 cmp al, content_pointer
1605 jne .catch_missing_form
1606 mov r9
, [r9
+ Cons.cdr
] ; C in R9
1608 ; Now have extracted from (try* A (catch* B C))
1617 ; Set the error handler
1618 mov rsi
, rsp
; Stack pointer
1619 mov rdi
, .catch
; Address to jump to
1620 xor rcx
, rcx
; No data
1621 call error_handler_push
1623 ; Evaluate the form in R8
1625 call incref_object
; Env released by eval
1626 mov rdi
, r15
; Env in RDI
1628 mov rsi
, r8
; The form to evaluate (A)
1630 call incref_object
; AST released by eval
1634 mov r8
, rax
; Result in R8
1636 pop r15
; Environment
1638 ;add rsi, 8 ; pop R10 and R9
1642 ; Remove error handler
1643 call error_handler_pop
1648 ;; Evaluate the form in R8
1649 push r15
; Environment
1652 call incref_object
; Env released by eval
1653 mov rdi
, r15
; Env in RDI
1655 mov rsi
, r8
; The form to evaluate (A)
1657 call incref_object
; AST released by eval
1659 call eval
; Result in RAX
1661 pop r15
; Environment
1665 ; Jumps here on error
1666 ; Value thrown in RSI
1670 call error_handler_pop
1674 pop r12
; B (symbol to bind)
1675 pop r13
; C (form to evaluate)
1677 ; Check if C is a value or pointer
1680 and cl, content_mask
1681 cmp cl, content_pointer
1684 ; A value, so copy and return
1686 mov [rax
], BYTE cl ; Set type
1687 mov rbx
, [r13
+ Cons.car
]
1688 mov [rax
+ Cons.car
], rbx
; Set value
1694 mov r11
, rsi
; Value thrown in R11
1696 mov rsi
, r15
; Outer env
1697 call env_new
; Increments R15's ref count
1699 mov rsi
, rax
; New environment in RSI
1700 mov rdi
, r12
; key (symbol)
1701 mov rcx
, r11
; value
1704 mov rdi
, rsi
; Env in RDI (will be released)
1705 mov rsi
, [r13
+ Cons.car
] ; Form to evaluate
1706 call incref_object
; will be released
1715 load_static try_missing_catch
1720 .
catch_missing_symbol:
1721 load_static catch_missing_symbol
1726 .
catch_missing_form:
1727 load_static catch_missing_form
1732 ; -----------------------------
1736 mov rdi
, r15
; Environment
1738 call eval_ast
; List of evaluated forms in RAX
1743 ; This point can be called to run a function
1746 ; Inputs: RAX - List with function as first element
1747 ; NOTE: This list is released
1749 ; Check that the first element of the return is a function
1751 and bl, content_mask
1752 cmp bl, content_pointer
1753 jne .list_not_function
1755 mov rbx
, [rax
+ Cons.car
] ; Get the address
1757 cmp cl, maltype_function
1758 jne .list_not_function
1760 ; Check the rest of the args
1761 mov cl, BYTE [rax
+ Cons.typecdr
]
1762 cmp cl, content_pointer
1767 push rbx
; Function object
1769 mov rsi
, rax
; List with function first
1770 call release_object
; Can be freed now
1772 ; Create an empty list for the arguments
1774 mov [rax
], BYTE maltype_empty_list
1778 jmp .list_function_call
1780 mov rsi
, [rax
+ Cons.cdr
] ; Rest of list
1781 .
list_function_call:
1782 ; Call the function with the rest of the list in RSI
1784 mov rdx
, rax
; List to release
1785 mov rdi
, rbx
; Function object in RDI
1787 mov rbx
, [rbx
+ Cons.car
] ; Call function
1789 je apply_fn_jmp
; Jump to user function apply
1791 ; A built-in function, so call (no recursion)
1799 pop rsi
; eval'ed list
1804 jmp .return
; Releases Env
1807 ; Not a function. Probably an error
1813 print_str_mac error_string
1814 print_str_mac eval_list_not_function
1822 ;; Applies a user-defined function
1824 ;; Input: RSI - Arguments to bind
1825 ;; RDI - Function object
1826 ;; RDX - list to release after binding
1827 ;; R15 - Env (will be released)
1828 ;; R13 - AST released before return
1831 ;; Output: Result in RAX
1833 ;; This is jumped to from eval, so if it returns
1834 ;; then it will return to the caller of eval, not to eval
1836 ; This is jumped to from eval with AST on the stack
1840 ; Extract values from the list in RDI
1841 mov rax
, [rdi
+ Cons.cdr
]
1842 mov rax
, [rax
+ Cons.cdr
] ; Meta (don't need)
1843 mov rsi
, [rax
+ Cons.car
] ; Env
1844 mov rax
, [rax
+ Cons.cdr
]
1845 mov rdi
, [rax
+ Cons.car
] ; Binds
1846 mov rax
, [rax
+ Cons.cdr
]
1847 mov rax
, [rax
+ Cons.car
] ; Body
1850 ; Check the type of the body
1852 and bl, block_mask
+ container_mask
1854 ; Just a value (in RAX). No eval needed
1856 mov r14
, rax
; Save return value in R14
1861 ; Release the list passed in RDX
1865 ; Release the environment
1876 ; Create a new environment, binding arguments
1879 mov r14
, r13
; Old AST. R13 used by env_new_bind
1885 mov rdi
, rax
; New environment in RDI
1887 ; Note: Need to increment the reference count
1888 ; of the function body before releasing anything,
1889 ; since if the function was defined in-place (lambda)
1890 ; then the body may be released early
1893 call incref_object
; Will be released by eval
1894 mov r8
, rsi
; Body in R8
1896 ; Release the list passed in RDX
1900 ; Release the environment
1904 ; Release the old AST
1910 jmp eval
; Tail call
1911 ; The new environment (in RDI) will be released by eval
1914 ;; Set ZF if RSI is a non-empty list or vector
1915 ;; Modifies RAX, does not modify RSI
1919 jnz .false
; Not a Cons
1920 cmp al, maltype_empty_list
1921 je .false
; Empty list
1922 cmp al, maltype_empty_vector
1923 je .false
; Empty vector
1925 ; Something non empty
1926 and al, container_mask
1927 cmp al, container_list
1929 cmp al, container_vector
1931 ; Not a list or vector -> false
1935 and ah, 255-64 ; clear zero flag
1940 or ah, 64 ; set zero flag
1944 ;; Called by eval with AST in RSI [ modified ]
1945 ;; Returns new AST in RAX
1947 ; i. Check if AST is an empty list
1951 ; ii. Check if the first element of RSI is the symbol
1955 and al, content_mask
1956 cmp al, content_pointer
1957 jne .not_unquote
; Not a pointer
1959 mov rdi
, [rsi
+ Cons.car
] ; Get the pointer
1961 cmp cl, maltype_symbol
1964 ; Compare against 'unquote'
1968 mov rsi
, unquote_symbol
1969 call compare_char_array
1978 ; iii. Handle splice-unquote
1979 ; RSI -> ( ( splice-unquote ? ) ? )
1981 ; Test if RSI contains a pointer
1983 cmp al, content_pointer
1986 mov rbx
, [rsi
+ Cons.car
] ; Get the object pointer
1988 ; RBX -> ( splice-unquote ? )
1993 jne .not_splice
; First element not a pair
1995 ; Check if this list in RBX starts with 'splice-unquote' symbol
1997 and al, content_mask
1998 cmp al, content_pointer
2002 mov rdi
, [rbx
+ Cons.car
] ; Get the pointer
2004 cmp al, maltype_symbol
2010 ; Compare against 'splice-unquote'
2011 mov rsi
, splice_unquote_symbol
2012 call compare_char_array
2022 ; iv. Cons first and rest of AST in RSI
2024 ; check if pointer or value
2026 and cl, content_mask
2027 cmp cl, content_pointer
2032 or cl, container_list
2033 mov [rax
], BYTE cl ; List + Content
2034 mov rbx
, [rsi
+ Cons.car
]
2035 mov [rax
+ Cons.car
], rbx
2040 ; Get the pointer and call quasiquote
2042 mov rsi
, [rsi
+ Cons.car
]
2048 mov [rax
], BYTE (container_list
+ content_pointer
)
2049 mov [rax
+ Cons.car
], rcx
2053 ; Have Cons with first object in RCX
2055 ; Call quasiquote on the rest of the AST
2056 ; Check if this is the end of the list
2057 mov al, BYTE [rsi
+ Cons.typecdr
]
2058 cmp al, content_pointer
2061 mov rsi
, [rsi
+ Cons.cdr
] ; Rest of the list
2063 call incref_object
; Will release after quasiquote call
2065 jmp .cons_quasiquote_ast
2068 ; End of the AST, so make an empty list
2070 mov [rax
], BYTE maltype_empty_list
2073 .
cons_quasiquote_ast:
2077 mov rdx
, rax
; List in RDX
2080 call release_object
; Release input
2082 pop rcx
; Value in RCX
2085 ; Work from the end of the list to the front
2088 mov [rax
], BYTE (container_list
+ content_pointer
)
2089 mov [rax
+ Cons.car
], rdx
; The rest of AST
2091 ; Link to the RCX Cons
2092 mov [rcx
+ Cons.typecdr
], BYTE content_pointer
2093 mov [rcx
+ Cons.cdr
], rax
2096 call alloc_cons
; Cons for cons symbol
2097 mov [rax
+ Cons.typecdr
], BYTE content_pointer
2098 mov [rax
+ Cons.cdr
], rdx
2101 ; Get the cons symbol
2102 mov rsi
, cons_symbol
2105 mov [rdx
], BYTE (container_list
+ content_pointer
)
2106 mov [rdx
+ Cons.car
], rsi
2112 ; Return (quote RSI)
2114 call incref_object
; RSI reference count
2118 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
2119 mov [rax
+ Cons.car
], rsi
2122 ; Cons for quote symbol
2125 mov [rbx
+ Cons.typecdr
], BYTE content_pointer
2126 mov [rbx
+ Cons.cdr
], rsi
2128 ; Get a quote symbol, incrementing references
2129 mov rsi
, quote_symbol
2132 ; Put into the Cons in RBX
2133 mov [rbx
+ Cons.car
], rsi
2134 mov [rbx
], BYTE (block_cons
+ container_list
+ content_pointer
)
2137 ; -----------------------
2141 ; Got unquote symbol. Return second element of RSI
2142 mov al, BYTE [rsi
+ Cons.typecdr
]
2143 cmp al, content_pointer
2144 jne .empty_list
; No second element
2146 mov rsi
, [rsi
+ Cons.cdr
]
2148 ; Check if it's a value or pointer
2150 and cl, content_mask
2151 cmp cl, content_pointer
2154 ; A value, so need a new Cons
2156 mov [rax
], BYTE cl ; content
2157 mov rbx
, [rsi
+ Cons.car
]
2158 mov [rax
+ Cons.car
], rbx
; Copy content
2162 mov rsi
, [rsi
+ Cons.car
]
2167 ; -----------------------
2169 ; RSI -> ( RBX->( splice-unquote A ) B )
2171 ; RBX Car points to splice-unquote symbol
2173 ; Check if there is anything after the symbol
2174 mov al, BYTE [rbx
+ Cons.typecdr
]
2175 cmp al, content_pointer
2176 jne .splice_unquote_empty
2178 ; Point to the second element of the splice-unquote list
2179 mov rcx
, [rbx
+ Cons.cdr
]
2181 ; Check whether it's a value or pointer
2183 and al, content_mask
2184 cmp al, content_pointer
2185 je .splice_unquote_pointer
2187 ; A value, so change the container to a value
2189 ; Remove pointer from RBX
2190 mov [rbx
+ Cons.typecdr
], BYTE 0
2191 jmp .splice_unquote_first
; Got the value in RCX
2193 .
splice_unquote_pointer:
2194 mov rcx
, [rcx
+ Cons.car
] ; Get the object pointed to
2197 xchg rcx
, rsi
; Object in RCX
2199 .
splice_unquote_first: ; Got the first object in RCX
2201 ; Check if RSI contains anything else
2202 mov al, BYTE [rsi
+ Cons.typecdr
]
2203 cmp al, content_pointer
2204 jne .splice_unquote_notail
2206 mov rsi
, [rsi
+ Cons.cdr
]
2209 ; ( ( splice-unquote A ) B )
2211 ; Need to call quasiquote on the rest of the list
2216 ; Need to concat rcx and rdx
2217 ; Work from the end of the list to the front
2220 mov [rax
], BYTE (container_list
+ content_pointer
)
2221 mov [rax
+ Cons.car
], rdx
; The rest of AST
2222 mov rdx
, rax
; Push list into RDX
2225 mov [rax
], BYTE (container_list
+ content_pointer
)
2226 mov [rax
+ Cons.car
], rcx
; The splice-unquote object
2227 mov [rax
+ Cons.typecdr
], BYTE content_pointer
2228 mov [rax
+ Cons.cdr
], rdx
2231 call alloc_cons
; Cons for concat symbol
2232 mov [rax
+ Cons.typecdr
], BYTE content_pointer
2233 mov [rax
+ Cons.cdr
], rdx
2236 ; Get the concat symbol
2237 mov rsi
, concat_symbol
2240 mov [rdx
], BYTE (container_list
+ content_pointer
)
2241 mov [rdx
+ Cons.car
], rsi
2246 .
splice_unquote_notail:
2247 ; Just return the object in RCX
2248 ; since nothing to concatenate with
2252 .
splice_unquote_empty:
2253 ; Nothing in the (splice-unquote) list, so ignore
2254 ; Just call quasiquote on the rest of RSI
2256 mov al, BYTE [rsi
+ Cons.typecdr
]
2257 cmp al, content_pointer
2258 jne .empty_list
; Nothing else
2260 mov rsi
, [rsi
+ Cons.cdr
]
2261 jmp quasiquote
; Tail call
2264 ; Return an empty list
2266 mov [rax
], BYTE maltype_empty_list
2271 ;; Tests if an AST in RSI is a list containing
2272 ;; a macro defined in the ENV in R15
2274 ;; Inputs: AST in RSI (not modified)
2275 ;; ENV in R15 (not modified)
2277 ;; Returns: Sets ZF if macro call. If set (true),
2278 ;; then the macro object is in RAX
2288 ; Test if RSI is a list which contains a pointer
2290 cmp al, (block_cons
+ container_list
+ content_pointer
)
2293 ; Test if this is a symbol
2294 mov rbx
, [rsi
+ Cons.car
]
2296 cmp al, maltype_symbol
2299 ; Look up symbol in Env
2302 mov rdi
, rbx
; symbol in RDI
2303 mov rsi
, r15
; Environment in RSI
2307 jne .false
; Not in environment
2310 ; If this is not a macro then needs to be released
2313 cmp dl, maltype_macro
2316 ; Not a macro, so release
2324 and ah, 255-64 ; clear zero flag
2328 mov rbx
, rax
; Returning Macro object
2330 or ah, 64 ; set zero flag
2335 ;; Expands macro calls
2337 ;; Input: AST in RSI (released and replaced)
2338 ;; Env in R15 (not modified)
2340 ;; Result: New AST in RSI
2349 mov rdi
, rax
; Macro in RDI
2351 ; Check the rest of the args
2352 mov cl, BYTE [rsi
+ Cons.typecdr
]
2353 cmp cl, content_pointer
2356 ; No arguments. Create an empty list
2358 mov [rax
], BYTE maltype_empty_list
2361 mov rsi
, rdx
; Arguments (empty list)
2365 mov rsi
, [rsi
+ Cons.cdr
] ; Rest of list
2367 mov rdx
, rsi
; Released
2371 ; RDI - Macro object
2372 ; RDX - List to release
2376 ; Increment reference for Environment
2377 ; since this will be released by apply_fn
2384 mov rsi
, rax
; Result in RSI
2400 mov rsi
, rax
; Form to evaluate
2401 mov rdi
, [repl_env
] ; Environment
2404 call incref_object
; Environment increment refs
2405 xchg rsi
, rdi
; since it will be decremented by eval
2407 jmp eval
; This releases Env and Form/AST
2410 ;; Read-Eval-Print in sequence
2412 ;; Input string in RSI
2420 mov rsi
, rax
; Form to evaluate
2421 mov rdi
, [repl_env
] ; Environment
2424 call incref_object
; Environment increment refs
2425 xchg rsi
, rdi
; since it will be decremented by eval
2427 call eval
; This releases Env and Form/AST
2428 push rax
; Save result of eval
2433 mov rsi
, rax
; Output of eval into input of print
2434 mov rdi
, 1 ; print readably
2435 call pr_str
; String in RAX
2437 mov r8
, rax
; Save output
2439 pop rsi
; Result from eval
2447 ; Create and print the core environment
2448 call core_environment
; Environment in RAX
2450 mov [repl_env
], rax
; store in memory
2452 ; Set the error handler
2453 mov rsi
, rsp
; Stack pointer
2454 mov rdi
, .catch
; Address to jump to
2455 xor rcx
, rcx
; No data
2456 call error_handler_push
2458 ; Evaluate the startup string
2460 mov rsi
, mal_startup_string
2461 mov edx, mal_startup_string.len
2462 call raw_to_string
; String in RAX
2466 call read_str
; AST in RAX
2470 call release_array
; string
2471 pop rdi
; AST in RDI
2473 mov rsi
, [repl_env
] ; Environment in RSI
2475 call incref_object
; Environment increment refs
2476 xchg rsi
, rdi
; Env in RDI, AST in RSI
2481 call release_object
; Return from eval
2483 ; -----------------------------
2484 ; Check command-line arguments
2486 pop rax
; Number of arguments
2487 cmp rax
, 1 ; Always have at least one, the path to executable
2490 ; No extra arguments, so just set *ARGV* to an empty list
2491 call alloc_cons
; in RAX
2492 mov [rax
], BYTE maltype_empty_list
2493 mov rcx
, rax
; value (empty list)
2494 mov rdi
, argv_symbol
; symbol (*ARGV*)
2495 mov rsi
, [repl_env
] ; environment
2498 ; -----------------------------
2501 load_static mal_startup_header
2506 call read_eval
; no print ('nil')
2508 call release_object
; Release result of eval
2510 ; Release the input string
2514 ; -----------------------------
2519 print_str_mac prompt_string
2523 ; Check if we have a zero-length string
2524 cmp DWORD [rax
+Array.
length], 0
2527 push rax
; Save address of the string
2530 call rep_seq
; Read-Eval-Print
2532 push rax
; Save returned string
2534 mov rsi
, rax
; Put into input of print_string
2537 ; Release string from rep_seq
2541 ; Release the input string
2551 ; Jumps here on error
2553 ; Check if an object was thrown
2555 je .catch_done_print
; nothing to print
2558 print_str_mac error_string
; print 'Error: '
2566 jmp .mainLoop
; Go back to the prompt
2571 ; Called with number of command-line arguments in RAX
2573 pop rbx
; executable
2576 pop rsi
; Address of first arg
2577 call cstring_to_string
; string in RAX
2580 ; get the rest of the args
2585 ; Got some arguments
2587 ; Got an argument left.
2588 pop rsi
; Address of C string
2589 call cstring_to_string
; String in RAX
2592 ;Make a Cons to point to the string
2593 call alloc_cons
; in RAX
2594 mov [rax
], BYTE (block_cons
+ container_list
+ content_pointer
)
2595 mov [rax
+ Cons.car
], r12
2600 ; R10 zero, so first arg
2601 mov r10
, rax
; Head of list
2602 mov r11
, rax
; Tail of list
2605 ; R10 not zero, so append to list tail
2606 mov [r11
+ Cons.cdr
], rax
2607 mov [r11
+ Cons.typecdr
], BYTE content_pointer
2615 ; No arguments. Create an emoty list
2616 call alloc_cons
; in RAX
2617 mov [rax
], BYTE maltype_empty_list
2621 push r9
; File name string
2623 mov rcx
, r10
; value (list)
2624 mov rdi
, argv_symbol
; symbol (*ARGV*)
2625 mov rsi
, [repl_env
] ; environment
2628 mov rsi
, run_script_string
; load-file function
2629 mov edx, run_script_string.len
2630 call raw_to_string
; String in RAX
2633 pop rdx
; File name string
2634 call string_append_string
2637 call string_append_char
2639 call string_append_char
; closing brace
2641 ; Read-Eval "(load-file <file>)"