Commit | Line | Data |
---|---|---|
afdf531e JM |
1 | using System; |
2 | using System.IO; | |
3 | using System.Collections; | |
4 | using System.Collections.Generic; | |
5 | using Mal; | |
6 | using MalVal = Mal.types.MalVal; | |
7 | using MalSymbol = Mal.types.MalSymbol; | |
c3b508af | 8 | using MalInt = Mal.types.MalInt; |
afdf531e JM |
9 | using MalList = Mal.types.MalList; |
10 | using MalVector = Mal.types.MalVector; | |
11 | using MalHashMap = Mal.types.MalHashMap; | |
c3b508af | 12 | using MalFunc = Mal.types.MalFunc; |
afdf531e JM |
13 | using Env = Mal.env.Env; |
14 | ||
15 | namespace Mal { | |
16 | class step4_if_fn_do { | |
17 | // read | |
18 | static MalVal READ(string str) { | |
19 | return reader.read_str(str); | |
20 | } | |
21 | ||
22 | // eval | |
23 | static MalVal eval_ast(MalVal ast, Env env) { | |
24 | if (ast is MalSymbol) { | |
b8ee29b2 | 25 | return env.get((MalSymbol)ast); |
afdf531e JM |
26 | } else if (ast is MalList) { |
27 | MalList old_lst = (MalList)ast; | |
28 | MalList new_lst = ast.list_Q() ? new MalList() | |
29 | : (MalList)new MalVector(); | |
30 | foreach (MalVal mv in old_lst.getValue()) { | |
31 | new_lst.conj_BANG(EVAL(mv, env)); | |
32 | } | |
33 | return new_lst; | |
34 | } else if (ast is MalHashMap) { | |
35 | var new_dict = new Dictionary<string, MalVal>(); | |
36 | foreach (var entry in ((MalHashMap)ast).getValue()) { | |
37 | new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env)); | |
38 | } | |
39 | return new MalHashMap(new_dict); | |
40 | } else { | |
41 | return ast; | |
42 | } | |
43 | } | |
44 | ||
45 | ||
46 | static MalVal EVAL(MalVal orig_ast, Env env) { | |
47 | MalVal a0, a1, a2, a3, res; | |
48 | MalList el; | |
b8ee29b2 | 49 | //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); |
afdf531e JM |
50 | if (!orig_ast.list_Q()) { |
51 | return eval_ast(orig_ast, env); | |
52 | } | |
53 | ||
54 | // apply list | |
55 | MalList ast = (MalList)orig_ast; | |
56 | if (ast.size() == 0) { return ast; } | |
8cb5cda4 | 57 | a0 = ast[0]; |
afdf531e JM |
58 | |
59 | String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName() | |
60 | : "__<*fn*>__"; | |
61 | ||
62 | switch (a0sym) { | |
63 | case "def!": | |
8cb5cda4 JM |
64 | a1 = ast[1]; |
65 | a2 = ast[2]; | |
afdf531e | 66 | res = EVAL(a2, env); |
b8ee29b2 | 67 | env.set((MalSymbol)a1, res); |
afdf531e JM |
68 | return res; |
69 | case "let*": | |
8cb5cda4 JM |
70 | a1 = ast[1]; |
71 | a2 = ast[2]; | |
afdf531e JM |
72 | MalSymbol key; |
73 | MalVal val; | |
74 | Env let_env = new Env(env); | |
75 | for(int i=0; i<((MalList)a1).size(); i+=2) { | |
8cb5cda4 JM |
76 | key = (MalSymbol)((MalList)a1)[i]; |
77 | val = ((MalList)a1)[i+1]; | |
b8ee29b2 | 78 | let_env.set(key, EVAL(val, let_env)); |
afdf531e JM |
79 | } |
80 | return EVAL(a2, let_env); | |
81 | case "do": | |
82 | el = (MalList)eval_ast(ast.rest(), env); | |
8cb5cda4 | 83 | return el[el.size()-1]; |
afdf531e | 84 | case "if": |
8cb5cda4 | 85 | a1 = ast[1]; |
afdf531e JM |
86 | MalVal cond = EVAL(a1, env); |
87 | if (cond == Mal.types.Nil || cond == Mal.types.False) { | |
88 | // eval false slot form | |
89 | if (ast.size() > 3) { | |
8cb5cda4 | 90 | a3 = ast[3]; |
afdf531e JM |
91 | return EVAL(a3, env); |
92 | } else { | |
93 | return Mal.types.Nil; | |
94 | } | |
95 | } else { | |
96 | // eval true slot form | |
8cb5cda4 | 97 | a2 = ast[2]; |
afdf531e JM |
98 | return EVAL(a2, env); |
99 | } | |
100 | case "fn*": | |
8cb5cda4 JM |
101 | MalList a1f = (MalList)ast[1]; |
102 | MalVal a2f = ast[2]; | |
afdf531e | 103 | Env cur_env = env; |
c3b508af | 104 | return new MalFunc( |
afdf531e JM |
105 | args => EVAL(a2f, new Env(cur_env, a1f, args)) ); |
106 | default: | |
107 | el = (MalList)eval_ast(ast, env); | |
c3b508af | 108 | var f = (MalFunc)el[0]; |
afdf531e JM |
109 | return f.apply(el.rest()); |
110 | } | |
111 | } | |
112 | ||
113 | ||
114 | static string PRINT(MalVal exp) { | |
115 | return printer._pr_str(exp, true); | |
116 | } | |
117 | ||
86b689f3 | 118 | // repl |
afdf531e | 119 | static void Main(string[] args) { |
c3b508af JM |
120 | var repl_env = new Mal.env.Env(null); |
121 | Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env); | |
afdf531e | 122 | |
8cb5cda4 | 123 | // core.cs: defined using C# |
8cb5cda4 | 124 | foreach (var entry in core.ns) { |
b8ee29b2 | 125 | repl_env.set(new MalSymbol(entry.Key), entry.Value); |
afdf531e JM |
126 | } |
127 | ||
8cb5cda4 | 128 | // core.mal: defined using the language itself |
c3b508af | 129 | RE("(def! not (fn* (a) (if a false true)))"); |
afdf531e JM |
130 | |
131 | if (args.Length > 0 && args[0] == "--raw") { | |
132 | Mal.readline.mode = Mal.readline.Mode.Raw; | |
133 | } | |
c3b508af | 134 | |
86b689f3 | 135 | // repl loop |
afdf531e JM |
136 | while (true) { |
137 | string line; | |
138 | try { | |
c3b508af | 139 | line = Mal.readline.Readline("user> "); |
afdf531e | 140 | if (line == null) { break; } |
c3b508af | 141 | if (line == "") { continue; } |
afdf531e JM |
142 | } catch (IOException e) { |
143 | Console.WriteLine("IOException: " + e.Message); | |
144 | break; | |
145 | } | |
146 | try { | |
c3b508af | 147 | Console.WriteLine(PRINT(RE(line))); |
afdf531e JM |
148 | } catch (Mal.types.MalContinue) { |
149 | continue; | |
afdf531e JM |
150 | } catch (Exception e) { |
151 | Console.WriteLine("Error: " + e.Message); | |
8cb5cda4 | 152 | Console.WriteLine(e.StackTrace); |
afdf531e JM |
153 | continue; |
154 | } | |
155 | } | |
156 | } | |
157 | } | |
158 | } |