Python: metadata on builtin funcs. Support python3.
[jackhill/mal.git] / cs / stepA_more.cs
CommitLineData
faee4d12
JM
1using System;
2using System.IO;
3using System.Collections;
4using System.Collections.Generic;
5using Mal;
6using MalException = Mal.types.MalException;
7using MalVal = Mal.types.MalVal;
8using MalString = Mal.types.MalString;
9using MalSymbol = Mal.types.MalSymbol;
10using MalInteger = Mal.types.MalInteger;
11using MalList = Mal.types.MalList;
12using MalVector = Mal.types.MalVector;
13using MalHashMap = Mal.types.MalHashMap;
14using MalFunction = Mal.types.MalFunction;
15using Env = Mal.env.Env;
16
17namespace Mal {
18 class stepA_more {
19 // read
20 static MalVal READ(string str) {
21 return reader.read_str(str);
22 }
23
24 // eval
25 public static bool is_pair(MalVal x) {
26 return x is MalList && ((MalList)x).size() > 0;
27 }
28
29 public static MalVal quasiquote(MalVal ast) {
30 if (!is_pair(ast)) {
31 return new MalList(new MalSymbol("quote"), ast);
32 } else {
33 MalVal a0 = ((MalList)ast)[0];
34 if ((a0 is MalSymbol) &&
35 (((MalSymbol)a0).getName() == "unquote")) {
36 return ((MalList)ast)[1];
37 } else if (is_pair(a0)) {
38 MalVal a00 = ((MalList)a0)[0];
39 if ((a00 is MalSymbol) &&
40 (((MalSymbol)a00).getName() == "splice-unquote")) {
41 return new MalList(new MalSymbol("concat"),
42 ((MalList)a0)[1],
43 quasiquote(((MalList)ast).rest()));
44 }
45 }
46 return new MalList(new MalSymbol("cons"),
47 quasiquote(a0),
48 quasiquote(((MalList)ast).rest()));
49 }
50 }
51
52 public static bool is_macro_call(MalVal ast, Env env) {
53 if (ast is MalList) {
54 MalVal a0 = ((MalList)ast)[0];
55 if (a0 is MalSymbol &&
56 env.find(((MalSymbol)a0).getName()) != null) {
57 MalVal mac = env.get(((MalSymbol)a0).getName());
58 if (mac is MalFunction &&
59 ((MalFunction)mac).isMacro()) {
60 return true;
61 }
62 }
63 }
64 return false;
65 }
66
67 public static MalVal macroexpand(MalVal ast, Env env) {
68 while (is_macro_call(ast, env)) {
69 MalSymbol a0 = (MalSymbol)((MalList)ast)[0];
70 MalFunction mac = (MalFunction) env.get(a0.getName());
71 ast = mac.apply(((MalList)ast).rest());
72 }
73 return ast;
74 }
75
76 static MalVal eval_ast(MalVal ast, Env env) {
77 if (ast is MalSymbol) {
78 MalSymbol sym = (MalSymbol)ast;
79 return env.get(sym.getName());
80 } else if (ast is MalList) {
81 MalList old_lst = (MalList)ast;
82 MalList new_lst = ast.list_Q() ? new MalList()
83 : (MalList)new MalVector();
84 foreach (MalVal mv in old_lst.getValue()) {
85 new_lst.conj_BANG(EVAL(mv, env));
86 }
87 return new_lst;
88 } else if (ast is MalHashMap) {
89 var new_dict = new Dictionary<string, MalVal>();
90 foreach (var entry in ((MalHashMap)ast).getValue()) {
91 new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));
92 }
93 return new MalHashMap(new_dict);
94 } else {
95 return ast;
96 }
97 }
98
99
100 static MalVal EVAL(MalVal orig_ast, Env env) {
101 MalVal a0, a1, a2, res;
102 MalList el;
103
104 while (true) {
105
106 //System.out.println("EVAL: " + printer._pr_str(orig_ast, true));
107 if (!orig_ast.list_Q()) {
108 return eval_ast(orig_ast, env);
109 }
110
111 // apply list
112 MalVal expanded = macroexpand(orig_ast, env);
113 if (!expanded.list_Q()) { return expanded; }
114 MalList ast = (MalList) expanded;
115
116 if (ast.size() == 0) { return ast; }
117 a0 = ast[0];
118
119 String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()
120 : "__<*fn*>__";
121
122 switch (a0sym) {
123 case "def!":
124 a1 = ast[1];
125 a2 = ast[2];
126 res = EVAL(a2, env);
127 env.set(((MalSymbol)a1).getName(), res);
128 return res;
129 case "let*":
130 a1 = ast[1];
131 a2 = ast[2];
132 MalSymbol key;
133 MalVal val;
134 Env let_env = new Env(env);
135 for(int i=0; i<((MalList)a1).size(); i+=2) {
136 key = (MalSymbol)((MalList)a1)[i];
137 val = ((MalList)a1)[i+1];
138 let_env.set(key.getName(), EVAL(val, let_env));
139 }
140 return EVAL(a2, let_env);
141 case "quote":
142 return ast[1];
143 case "quasiquote":
144 return EVAL(quasiquote(ast[1]), env);
145 case "defmacro!":
146 a1 = ast[1];
147 a2 = ast[2];
148 res = EVAL(a2, env);
149 ((MalFunction)res).setMacro();
150 env.set(((MalSymbol)a1).getName(), res);
151 return res;
152 case "macroexpand":
153 a1 = ast[1];
154 return macroexpand(a1, env);
155 case "try*":
156 try {
157 return EVAL(ast[1], env);
158 } catch (Exception e) {
159 if (ast.size() > 2) {
160 MalVal exc;
161 a2 = ast[2];
162 MalVal a20 = ((MalList)a2)[0];
163 if (((MalSymbol)a20).getName() == "catch*") {
164 if (e is MalException) {
165 exc = ((MalException)e).getValue();
166 } else {
167 exc = new MalString(e.StackTrace);
168 }
169 return EVAL(((MalList)a2)[2],
170 new Env(env, ((MalList)a2).slice(1,2),
171 new MalList(exc)));
172 }
173 }
174 throw e;
175 }
176 case "do":
177 eval_ast(ast.slice(1, ast.size()-1), env);
178 orig_ast = ast[ast.size()-1];
179 break;
180 case "if":
181 a1 = ast[1];
182 MalVal cond = EVAL(a1, env);
183 if (cond == types.Nil || cond == types.False) {
184 // eval false slot form
185 if (ast.size() > 3) {
186 orig_ast = ast[3];
187 } else {
188 return types.Nil;
189 }
190 } else {
191 // eval true slot form
192 orig_ast = ast[2];
193 }
194 break;
195 case "fn*":
196 MalList a1f = (MalList)ast[1];
197 MalVal a2f = ast[2];
198 Env cur_env = env;
199 return new MalFunction(a2f, env, a1f,
200 args => EVAL(a2f, new Env(cur_env, a1f, args)) );
201 default:
202 el = (MalList)eval_ast(ast, env);
203 var f = (MalFunction)el[0];
204 MalVal fnast = f.getAst();
205 if (fnast != null) {
206 orig_ast = fnast;
207 env = f.genEnv(el.rest());
208 } else {
209 return f.apply(el.rest());
210 }
211 break;
212 }
213
214 }
215 }
216
217 // print
218 static string PRINT(MalVal exp) {
219 return printer._pr_str(exp, true);
220 }
221
222 // REPL
223 static MalVal RE(Env env, string str) {
224 return EVAL(READ(str), env);
225 }
226 public static Env _ref(Env env, string name, MalVal mv) {
227 return env.set(name, mv);
228 }
229
230
231 static void Main(string[] args) {
232 string prompt = "user> ";
233
234 var repl_env = new env.Env(null);
235 foreach (var entry in core.ns) {
236 _ref(repl_env, entry.Key, entry.Value);
237 }
238 _ref(repl_env, "readline", new MalFunction(
239 a => {
240 var line = readline.Readline(((MalString)a[0]).getValue());
241 if (line == null) { return types.Nil; }
242 else { return new MalString(line); }
243 }));
244 _ref(repl_env, "read-string", new MalFunction(
245 a => reader.read_str(((MalString)a[0]).getValue())));
246 _ref(repl_env, "eval", new MalFunction(
247 a => EVAL(a[0], repl_env)));
248 _ref(repl_env, "slurp", new MalFunction(
249 a => new MalString(File.ReadAllText(
250 ((MalString)a[0]).getValue()))));
251
252 RE(repl_env, "(def! not (fn* (a) (if a false true)))");
253 RE(repl_env, "(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)))))))");
254 RE(repl_env, "(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))))))))");
255 RE(repl_env, "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
256
257 int fileIdx = 0;
258 if (args.Length > 0 && args[0] == "--raw") {
259 readline.mode = readline.Mode.Raw;
260 fileIdx = 1;
261 }
262 if (args.Length > fileIdx) {
263 for(int i=fileIdx; i<args.Length; i++) {
264 RE(repl_env, "(load-file \"" + args[i] + "\")");
265 }
266 return;
267 }
268 while (true) {
269 string line;
270 try {
271 line = readline.Readline(prompt);
272 if (line == null) { break; }
273 } catch (IOException e) {
274 Console.WriteLine("IOException: " + e.Message);
275 break;
276 }
277 try {
278 Console.WriteLine(PRINT(RE(repl_env, line)));
279 } catch (types.MalContinue) {
280 continue;
281 } catch (reader.ParseError e) {
282 Console.WriteLine(e.Message);
283 continue;
284 } catch (MalException e) {
285 Console.WriteLine("Error: " +
286 printer._pr_str(e.getValue(), false));
287 continue;
288 } catch (Exception e) {
289 Console.WriteLine("Error: " + e.Message);
290 Console.WriteLine(e.StackTrace);
291 continue;
292 }
293 }
294 }
295 }
296}