Merge pull request #378 from asarhaddon/test-macro-not-changing-function
[jackhill/mal.git] / cs / step5_tco.cs
CommitLineData
96115d4f
JM
1using System;
2using System.IO;
3using System.Collections;
4using System.Collections.Generic;
5using Mal;
6using MalVal = Mal.types.MalVal;
7using MalSymbol = Mal.types.MalSymbol;
c3b508af 8using MalInt = Mal.types.MalInt;
96115d4f
JM
9using MalList = Mal.types.MalList;
10using MalVector = Mal.types.MalVector;
11using MalHashMap = Mal.types.MalHashMap;
c3b508af 12using MalFunc = Mal.types.MalFunc;
96115d4f
JM
13using Env = Mal.env.Env;
14
15namespace Mal {
8cb5cda4 16 class step5_tco {
96115d4f
JM
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);
96115d4f
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, res;
48 MalList el;
49
50 while (true) {
51
b8ee29b2 52 //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true));
96115d4f
JM
53 if (!orig_ast.list_Q()) {
54 return eval_ast(orig_ast, env);
55 }
56
57 // apply list
58 MalList ast = (MalList)orig_ast;
59 if (ast.size() == 0) { return ast; }
8cb5cda4 60 a0 = ast[0];
96115d4f
JM
61
62 String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()
63 : "__<*fn*>__";
64
65 switch (a0sym) {
66 case "def!":
8cb5cda4
JM
67 a1 = ast[1];
68 a2 = ast[2];
96115d4f 69 res = EVAL(a2, env);
b8ee29b2 70 env.set((MalSymbol)a1, res);
96115d4f
JM
71 return res;
72 case "let*":
8cb5cda4
JM
73 a1 = ast[1];
74 a2 = ast[2];
96115d4f
JM
75 MalSymbol key;
76 MalVal val;
77 Env let_env = new Env(env);
78 for(int i=0; i<((MalList)a1).size(); i+=2) {
8cb5cda4
JM
79 key = (MalSymbol)((MalList)a1)[i];
80 val = ((MalList)a1)[i+1];
b8ee29b2 81 let_env.set(key, EVAL(val, let_env));
96115d4f 82 }
6301e0b6
JM
83 orig_ast = a2;
84 env = let_env;
85 break;
96115d4f
JM
86 case "do":
87 eval_ast(ast.slice(1, ast.size()-1), env);
8cb5cda4 88 orig_ast = ast[ast.size()-1];
96115d4f
JM
89 break;
90 case "if":
8cb5cda4 91 a1 = ast[1];
96115d4f
JM
92 MalVal cond = EVAL(a1, env);
93 if (cond == Mal.types.Nil || cond == Mal.types.False) {
94 // eval false slot form
95 if (ast.size() > 3) {
96 orig_ast = ast[3];
97 } else {
98 return Mal.types.Nil;
99 }
100 } else {
101 // eval true slot form
102 orig_ast = ast[2];
103 }
104 break;
105 case "fn*":
8cb5cda4
JM
106 MalList a1f = (MalList)ast[1];
107 MalVal a2f = ast[2];
96115d4f 108 Env cur_env = env;
c3b508af 109 return new MalFunc(a2f, env, a1f,
96115d4f
JM
110 args => EVAL(a2f, new Env(cur_env, a1f, args)) );
111 default:
112 el = (MalList)eval_ast(ast, env);
c3b508af 113 var f = (MalFunc)el[0];
96115d4f
JM
114 MalVal fnast = f.getAst();
115 if (fnast != null) {
116 orig_ast = fnast;
117 env = f.genEnv(el.rest());
118 } else {
119 return f.apply(el.rest());
120 }
121 break;
122 }
123
124 }
125 }
126
127 // print
128 static string PRINT(MalVal exp) {
129 return printer._pr_str(exp, true);
130 }
131
86b689f3 132 // repl
96115d4f 133 static void Main(string[] args) {
c3b508af
JM
134 var repl_env = new Mal.env.Env(null);
135 Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);
96115d4f 136
8cb5cda4 137 // core.cs: defined using C#
8cb5cda4 138 foreach (var entry in core.ns) {
b8ee29b2 139 repl_env.set(new MalSymbol(entry.Key), entry.Value);
96115d4f
JM
140 }
141
8cb5cda4 142 // core.mal: defined using the language itself
c3b508af 143 RE("(def! not (fn* (a) (if a false true)))");
96115d4f
JM
144
145 if (args.Length > 0 && args[0] == "--raw") {
146 Mal.readline.mode = Mal.readline.Mode.Raw;
147 }
c3b508af 148
86b689f3 149 // repl loop
96115d4f
JM
150 while (true) {
151 string line;
152 try {
c3b508af 153 line = Mal.readline.Readline("user> ");
96115d4f 154 if (line == null) { break; }
c3b508af 155 if (line == "") { continue; }
96115d4f
JM
156 } catch (IOException e) {
157 Console.WriteLine("IOException: " + e.Message);
158 break;
159 }
160 try {
c3b508af 161 Console.WriteLine(PRINT(RE(line)));
96115d4f
JM
162 } catch (Mal.types.MalContinue) {
163 continue;
96115d4f
JM
164 } catch (Exception e) {
165 Console.WriteLine("Error: " + e.Message);
8cb5cda4 166 Console.WriteLine(e.StackTrace);
96115d4f
JM
167 continue;
168 }
169 }
170 }
171 }
172}