Merge pull request #378 from asarhaddon/test-macro-not-changing-function
[jackhill/mal.git] / cs / step7_quote.cs
CommitLineData
5a159ae7
JM
1using System;
2using System.IO;
3using System.Collections;
4using System.Collections.Generic;
5using Mal;
6using MalVal = Mal.types.MalVal;
7using MalString = Mal.types.MalString;
8using MalSymbol = Mal.types.MalSymbol;
c3b508af 9using MalInt = Mal.types.MalInt;
5a159ae7
JM
10using MalList = Mal.types.MalList;
11using MalVector = Mal.types.MalVector;
12using MalHashMap = Mal.types.MalHashMap;
c3b508af 13using MalFunc = Mal.types.MalFunc;
5a159ae7
JM
14using Env = Mal.env.Env;
15
16namespace Mal {
8cb5cda4 17 class step7_quote {
5a159ae7
JM
18 // read
19 static MalVal READ(string str) {
20 return reader.read_str(str);
21 }
22
23 // eval
24 public static bool is_pair(MalVal x) {
25 return x is MalList && ((MalList)x).size() > 0;
26 }
27
28 public static MalVal quasiquote(MalVal ast) {
29 if (!is_pair(ast)) {
30 return new MalList(new MalSymbol("quote"), ast);
31 } else {
32 MalVal a0 = ((MalList)ast)[0];
33 if ((a0 is MalSymbol) &&
34 (((MalSymbol)a0).getName() == "unquote")) {
35 return ((MalList)ast)[1];
36 } else if (is_pair(a0)) {
37 MalVal a00 = ((MalList)a0)[0];
38 if ((a00 is MalSymbol) &&
39 (((MalSymbol)a00).getName() == "splice-unquote")) {
40 return new MalList(new MalSymbol("concat"),
41 ((MalList)a0)[1],
42 quasiquote(((MalList)ast).rest()));
43 }
44 }
45 return new MalList(new MalSymbol("cons"),
46 quasiquote(a0),
47 quasiquote(((MalList)ast).rest()));
48 }
49 }
50
51 static MalVal eval_ast(MalVal ast, Env env) {
52 if (ast is MalSymbol) {
b8ee29b2 53 return env.get((MalSymbol)ast);
5a159ae7
JM
54 } else if (ast is MalList) {
55 MalList old_lst = (MalList)ast;
56 MalList new_lst = ast.list_Q() ? new MalList()
57 : (MalList)new MalVector();
58 foreach (MalVal mv in old_lst.getValue()) {
59 new_lst.conj_BANG(EVAL(mv, env));
60 }
61 return new_lst;
62 } else if (ast is MalHashMap) {
63 var new_dict = new Dictionary<string, MalVal>();
64 foreach (var entry in ((MalHashMap)ast).getValue()) {
65 new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env));
66 }
67 return new MalHashMap(new_dict);
68 } else {
69 return ast;
70 }
71 }
72
73
74 static MalVal EVAL(MalVal orig_ast, Env env) {
75 MalVal a0, a1, a2, res;
76 MalList el;
77
78 while (true) {
79
b8ee29b2 80 //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true));
5a159ae7
JM
81 if (!orig_ast.list_Q()) {
82 return eval_ast(orig_ast, env);
83 }
84
85 // apply list
86 MalList ast = (MalList)orig_ast;
87 if (ast.size() == 0) { return ast; }
88 a0 = ast[0];
89
90 String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()
91 : "__<*fn*>__";
92
93 switch (a0sym) {
94 case "def!":
95 a1 = ast[1];
96 a2 = ast[2];
97 res = EVAL(a2, env);
b8ee29b2 98 env.set((MalSymbol)a1, res);
5a159ae7
JM
99 return res;
100 case "let*":
101 a1 = ast[1];
102 a2 = ast[2];
103 MalSymbol key;
104 MalVal val;
105 Env let_env = new Env(env);
106 for(int i=0; i<((MalList)a1).size(); i+=2) {
107 key = (MalSymbol)((MalList)a1)[i];
108 val = ((MalList)a1)[i+1];
b8ee29b2 109 let_env.set(key, EVAL(val, let_env));
5a159ae7 110 }
6301e0b6
JM
111 orig_ast = a2;
112 env = let_env;
113 break;
5a159ae7
JM
114 case "quote":
115 return ast[1];
116 case "quasiquote":
6301e0b6
JM
117 orig_ast = quasiquote(ast[1]);
118 break;
5a159ae7
JM
119 case "do":
120 eval_ast(ast.slice(1, ast.size()-1), env);
121 orig_ast = ast[ast.size()-1];
122 break;
123 case "if":
124 a1 = ast[1];
125 MalVal cond = EVAL(a1, env);
126 if (cond == Mal.types.Nil || cond == Mal.types.False) {
127 // eval false slot form
128 if (ast.size() > 3) {
129 orig_ast = ast[3];
130 } else {
131 return Mal.types.Nil;
132 }
133 } else {
134 // eval true slot form
135 orig_ast = ast[2];
136 }
137 break;
138 case "fn*":
139 MalList a1f = (MalList)ast[1];
140 MalVal a2f = ast[2];
141 Env cur_env = env;
c3b508af 142 return new MalFunc(a2f, env, a1f,
5a159ae7
JM
143 args => EVAL(a2f, new Env(cur_env, a1f, args)) );
144 default:
145 el = (MalList)eval_ast(ast, env);
c3b508af 146 var f = (MalFunc)el[0];
5a159ae7
JM
147 MalVal fnast = f.getAst();
148 if (fnast != null) {
149 orig_ast = fnast;
150 env = f.genEnv(el.rest());
151 } else {
152 return f.apply(el.rest());
153 }
154 break;
155 }
156
157 }
158 }
159
160 // print
161 static string PRINT(MalVal exp) {
162 return printer._pr_str(exp, true);
163 }
164
86b689f3 165 // repl
5a159ae7 166 static void Main(string[] args) {
c3b508af
JM
167 var repl_env = new Mal.env.Env(null);
168 Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);
5a159ae7 169
8cb5cda4 170 // core.cs: defined using C#
8cb5cda4 171 foreach (var entry in core.ns) {
b8ee29b2 172 repl_env.set(new MalSymbol(entry.Key), entry.Value);
5a159ae7 173 }
b8ee29b2
JM
174 repl_env.set(new MalSymbol("eval"), new MalFunc(
175 a => EVAL(a[0], repl_env)));
dbbac62f 176 int fileIdx = 0;
aaba2493
JM
177 if (args.Length > 0 && args[0] == "--raw") {
178 Mal.readline.mode = Mal.readline.Mode.Raw;
dbbac62f 179 fileIdx = 1;
aaba2493 180 }
86b689f3 181 MalList _argv = new MalList();
dbbac62f 182 for (int i=fileIdx+1; i < args.Length; i++) {
86b689f3
JM
183 _argv.conj_BANG(new MalString(args[i]));
184 }
b8ee29b2 185 repl_env.set(new MalSymbol("*ARGV*"), _argv);
5a159ae7 186
8cb5cda4 187 // core.mal: defined using the language itself
c3b508af
JM
188 RE("(def! not (fn* (a) (if a false true)))");
189 RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
5a159ae7 190
5a159ae7 191 if (args.Length > fileIdx) {
c3b508af 192 RE("(load-file \"" + args[fileIdx] + "\")");
5a159ae7
JM
193 return;
194 }
c3b508af 195
86b689f3 196 // repl loop
5a159ae7
JM
197 while (true) {
198 string line;
199 try {
c3b508af 200 line = Mal.readline.Readline("user> ");
5a159ae7 201 if (line == null) { break; }
c3b508af 202 if (line == "") { continue; }
5a159ae7
JM
203 } catch (IOException e) {
204 Console.WriteLine("IOException: " + e.Message);
205 break;
206 }
207 try {
c3b508af 208 Console.WriteLine(PRINT(RE(line)));
5a159ae7
JM
209 } catch (Mal.types.MalContinue) {
210 continue;
5a159ae7
JM
211 } catch (Exception e) {
212 Console.WriteLine("Error: " + e.Message);
213 Console.WriteLine(e.StackTrace);
214 continue;
215 }
216 }
217 }
218 }
219}