Commit | Line | Data |
---|---|---|
4eb88ef2 DM |
1 | load "../logo/readline.lg |
2 | load "../logo/reader.lg | |
3 | load "../logo/printer.lg | |
4 | load "../logo/types.lg | |
5 | load "../logo/env.lg | |
6 | load "../logo/core.lg | |
7 | ||
8 | to _read :str | |
9 | output read_str :str | |
10 | end | |
11 | ||
12 | to pairp :obj | |
13 | output and sequentialp :obj ((_count :obj) > 0) | |
14 | end | |
15 | ||
16 | to quasiquote :ast | |
17 | if not pairp :ast [output (mal_list symbol_new "quote :ast)] | |
18 | localmake "a0 nth :ast 0 | |
19 | if symbolnamedp "unquote :a0 [output nth :ast 1] | |
20 | if 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 | ] | |
27 | output (mal_list symbol_new "cons (mal_list symbol_new "quasiquote :a0) (mal_list symbol_new "quasiquote rest :ast)) | |
28 | end | |
29 | ||
30 | to macrocallp :ast :env | |
31 | if (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 | ] | |
44 | output "false | |
45 | end | |
46 | ||
47 | to _macroexpand :ast :env | |
48 | if not macrocallp :ast :env [output :ast] | |
49 | localmake "a0 nth :ast 0 | |
50 | localmake "f env_get :env :a0 | |
51 | output _macroexpand invoke_fn :f rest :ast :env | |
52 | end | |
53 | ||
54 | to eval_ast :ast :env | |
55 | output 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 | ] | |
62 | end | |
63 | ||
64 | to _eval :a_ast :a_env | |
65 | localmake "ast :a_ast | |
66 | localmake "env :a_env | |
67 | forever [ | |
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 | ] | |
162 | end | |
163 | ||
164 | to _print :exp | |
165 | output pr_str :exp "true | |
166 | end | |
167 | ||
168 | to re :str | |
169 | output _eval _read :str :repl_env | |
170 | end | |
171 | ||
172 | to rep :str | |
173 | output _print re :str | |
174 | end | |
175 | ||
176 | to print_exception :exception | |
177 | if 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 | ] | |
185 | end | |
186 | ||
187 | to repl | |
188 | localmake "running "true | |
189 | while [: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 | ] | |
201 | end | |
202 | ||
203 | to mal_eval :a | |
204 | output _eval :a :repl_env | |
205 | end | |
206 | ||
207 | to argv_list | |
208 | localmake "argv ifelse emptyp :command.line [[]] [butfirst :command.line] | |
209 | output obj_new "list map [obj_new "string ?] :argv | |
210 | end | |
211 | ||
212 | make "repl_env env_new [] [] [] | |
213 | foreach :core_ns [ | |
214 | ignore env_set :repl_env first ? first butfirst ? | |
215 | ] | |
216 | ignore env_set :repl_env [symbol eval] [nativefn mal_eval] | |
217 | ignore env_set :repl_env [symbol *ARGV*] argv_list | |
218 | ||
219 | ; core.mal: defined using the language itself | |
220 | ignore re "|(def! *host-language* "logo")| | |
221 | ignore re "|(def! not (fn* (a) (if a false true)))| | |
222 | ignore re "|(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))| | |
223 | ignore 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 |
224 | ignore re "|(def! inc (fn* [x] (+ x 1)))| |
225 | ignore re "|(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str "G__" (swap! counter inc))))))| | |
4eb88ef2 DM |
226 | ignore 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 | ||
228 | if 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 | ||
234 | ignore re "|(println (str "Mal [" *host-language* "]"))| | |
235 | repl | |
236 | bye |