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