Merge pull request #378 from asarhaddon/test-macro-not-changing-function
[jackhill/mal.git] / logo / stepA_mal.lg
CommitLineData
4eb88ef2
DM
1load "../logo/readline.lg
2load "../logo/reader.lg
3load "../logo/printer.lg
4load "../logo/types.lg
5load "../logo/env.lg
6load "../logo/core.lg
7
8to _read :str
9output read_str :str
10end
11
12to pairp :obj
13output and sequentialp :obj ((_count :obj) > 0)
14end
15
16to quasiquote :ast
17if not pairp :ast [output (mal_list symbol_new "quote :ast)]
18localmake "a0 nth :ast 0
19if symbolnamedp "unquote :a0 [output nth :ast 1]
20if pairp :a0 [
21 localmake "a00 nth :a0 0
22 if symbolnamedp "splice-unquote :a00 [
23 localmake "a01 nth :a0 1
24 output (mal_list symbol_new "concat :a01 (mal_list symbol_new "quasiquote rest :ast))
25 ]
26]
27output (mal_list symbol_new "cons (mal_list symbol_new "quasiquote :a0) (mal_list symbol_new "quasiquote rest :ast))
28end
29
30to macrocallp :ast :env
31if (obj_type :ast) = "list [
32 if (_count :ast) > 0 [
33 localmake "a0 nth :ast 0
34 if (obj_type :a0) = "symbol [
35 if not emptyp env_find :env :a0 [
36 localmake "f env_get :env :a0
37 if (obj_type :f) = "fn [
38 output fn_is_macro :f
39 ]
40 ]
41 ]
42 ]
43]
44output "false
45end
46
47to _macroexpand :ast :env
48if not macrocallp :ast :env [output :ast]
49localmake "a0 nth :ast 0
50localmake "f env_get :env :a0
51output _macroexpand invoke_fn :f rest :ast :env
52end
53
54to eval_ast :ast :env
55output case (obj_type :ast) [
56 [[symbol] env_get :env :ast]
57 [[list] obj_new "list map [_eval ? :env] obj_val :ast]
58 [[vector] obj_new "vector map [_eval ? :env] obj_val :ast]
59 [[hashmap] obj_new "hashmap map [_eval ? :env] obj_val :ast]
60 [else :ast]
61]
62end
63
64to _eval :a_ast :a_env
65localmake "ast :a_ast
66localmake "env :a_env
67forever [
68 if (obj_type :ast) <> "list [output eval_ast :ast :env]
69 make "ast _macroexpand :ast :env
70 if (obj_type :ast) <> "list [output eval_ast :ast :env]
71 if emptyp obj_val :ast [output :ast]
72 localmake "a0 nth :ast 0
73 case list obj_type :a0 obj_val :a0 [
74 [[[symbol def!]]
75 localmake "a1 nth :ast 1
76 localmake "a2 nth :ast 2
77 output env_set :env :a1 _eval :a2 :env ]
78
79 [[[symbol let*]]
80 localmake "a1 nth :ast 1
81 localmake "letenv env_new :env [] []
82 localmake "i 0
83 while [:i < _count :a1] [
84 ignore env_set :letenv nth :a1 :i _eval nth :a1 (:i + 1) :letenv
85 make "i (:i + 2)
86 ]
87 make "env :letenv
88 make "ast nth :ast 2 ] ; TCO
89
90 [[[symbol quote]]
91 output nth :ast 1 ]
92
93 [[[symbol quasiquote]]
94 make "ast quasiquote nth :ast 1 ] ; TCO
95
96 [[[symbol defmacro!]]
97 localmake "a1 nth :ast 1
98 localmake "a2 nth :ast 2
99 localmake "macro_fn _eval :a2 :env
100 fn_set_macro :macro_fn
101 output env_set :env :a1 :macro_fn ]
102
103 [[[symbol macroexpand]]
104 output _macroexpand nth :ast 1 :env ]
105
106 [[[symbol try*]]
e64dbbd4
DM
107 localmake "a1 nth :ast 1
108 if (_count :ast) < 3 [
109 output _eval :a1 :env
110 ]
4eb88ef2 111 localmake "result nil_new
e64dbbd4 112 catch "error [make "result _eval :a1 :env]
4eb88ef2 113 localmake "exception error
e64dbbd4 114 ifelse emptyp :exception [
4eb88ef2
DM
115 output :result
116 ] [
117 localmake "e first butfirst :exception
118 localmake "exception_obj ifelse :e = "_mal_exception_ [:global_exception] [obj_new "string :e]
119 localmake "a2 nth :ast 2
120 localmake "catchenv env_new :env [] []
121 ignore env_set :catchenv nth :a2 1 :exception_obj
122 output _eval nth :a2 2 :catchenv
123 ] ]
124
125 [[[symbol do]]
126 localmake "i 1
127 while [:i < ((_count :ast) - 1)] [
128 ignore _eval nth :ast :i :env
129 make "i (:i + 1)
130 ]
131 make "ast last obj_val :ast ] ; TCO
132
133 [[[symbol if]]
134 localmake "a1 nth :ast 1
135 localmake "cond _eval :a1 :env
136 case obj_type :cond [
137 [[nil false] ifelse (_count :ast) > 3 [
138 make "ast nth :ast 3 ; TCO
139 ] [
140 output nil_new
141 ]]
142 [else make "ast nth :ast 2] ; TCO
143 ]]
144
145 [[[symbol fn*]]
146 output fn_new nth :ast 1 :env nth :ast 2 ]
147
148 [else
149 localmake "el eval_ast :ast :env
150 localmake "f nth :el 0
151 case obj_type :f [
152 [[nativefn]
153 output apply obj_val :f butfirst obj_val :el ]
154 [[fn]
155 make "env env_new fn_env :f fn_args :f rest :el
156 make "ast fn_body :f ] ; TCO
157 [else
158 (throw "error [Wrong type for apply])]
159 ] ]
160 ]
161]
162end
163
164to _print :exp
165output pr_str :exp "true
166end
167
168to re :str
169output _eval _read :str :repl_env
170end
171
172to rep :str
173output _print re :str
174end
175
176to print_exception :exception
177if not emptyp :exception [
178 localmake "e first butfirst :exception
179 ifelse :e = "_mal_exception_ [
180 (print "Error: pr_str :global_exception "false)
181 ] [
182 (print "Error: :e)
183 ]
184]
185end
186
187to repl
188localmake "running "true
189while [:running] [
190 localmake "line readline word "user> :space_char
191 ifelse :line=[] [
192 print "
193 make "running "false
194 ] [
195 if not emptyp :line [
196 catch "error [print rep :line]
197 print_exception error
198 ]
199 ]
200]
201end
202
203to mal_eval :a
204output _eval :a :repl_env
205end
206
207to argv_list
208localmake "argv ifelse emptyp :command.line [[]] [butfirst :command.line]
209output obj_new "list map [obj_new "string ?] :argv
210end
211
212make "repl_env env_new [] [] []
213foreach :core_ns [
214 ignore env_set :repl_env first ? first butfirst ?
215]
216ignore env_set :repl_env [symbol eval] [nativefn mal_eval]
217ignore env_set :repl_env [symbol *ARGV*] argv_list
218
219; core.mal: defined using the language itself
220ignore re "|(def! *host-language* "logo")|
221ignore re "|(def! not (fn* (a) (if a false true)))|
222ignore re "|(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))|
223ignore re "|(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw "odd number of forms to cond")) (cons 'cond (rest (rest xs)))))))|
14ab099c
NB
224ignore re "|(def! inc (fn* [x] (+ x 1)))|
225ignore re "|(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc))))))|
4eb88ef2
DM
226ignore re "|(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))|
227
228if not emptyp :command.line [
229 catch "error [ignore re (word "\(load-file :space_char "\" first :command.line "\"\) )]
230 print_exception error
231 bye
232]
233
234ignore re "|(println (str "Mal [" *host-language* "]"))|
235repl
236bye