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