Merge pull request #156 from omarrayward/explain-regexp-tokenizer
[jackhill/mal.git] / cs / step4_if_fn_do.cs
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;
8 using MalInt = Mal.types.MalInt;
9 using MalList = Mal.types.MalList;
10 using MalVector = Mal.types.MalVector;
11 using MalHashMap = Mal.types.MalHashMap;
12 using MalFunc = Mal.types.MalFunc;
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) {
25 return env.get((MalSymbol)ast);
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;
49 //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true));
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; }
57 a0 = ast[0];
58
59 String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName()
60 : "__<*fn*>__";
61
62 switch (a0sym) {
63 case "def!":
64 a1 = ast[1];
65 a2 = ast[2];
66 res = EVAL(a2, env);
67 env.set((MalSymbol)a1, res);
68 return res;
69 case "let*":
70 a1 = ast[1];
71 a2 = ast[2];
72 MalSymbol key;
73 MalVal val;
74 Env let_env = new Env(env);
75 for(int i=0; i<((MalList)a1).size(); i+=2) {
76 key = (MalSymbol)((MalList)a1)[i];
77 val = ((MalList)a1)[i+1];
78 let_env.set(key, EVAL(val, let_env));
79 }
80 return EVAL(a2, let_env);
81 case "do":
82 el = (MalList)eval_ast(ast.rest(), env);
83 return el[el.size()-1];
84 case "if":
85 a1 = ast[1];
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) {
90 a3 = ast[3];
91 return EVAL(a3, env);
92 } else {
93 return Mal.types.Nil;
94 }
95 } else {
96 // eval true slot form
97 a2 = ast[2];
98 return EVAL(a2, env);
99 }
100 case "fn*":
101 MalList a1f = (MalList)ast[1];
102 MalVal a2f = ast[2];
103 Env cur_env = env;
104 return new MalFunc(
105 args => EVAL(a2f, new Env(cur_env, a1f, args)) );
106 default:
107 el = (MalList)eval_ast(ast, env);
108 var f = (MalFunc)el[0];
109 return f.apply(el.rest());
110 }
111 }
112
113 // print
114 static string PRINT(MalVal exp) {
115 return printer._pr_str(exp, true);
116 }
117
118 // repl
119 static void Main(string[] args) {
120 var repl_env = new Mal.env.Env(null);
121 Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);
122
123 // core.cs: defined using C#
124 foreach (var entry in core.ns) {
125 repl_env.set(new MalSymbol(entry.Key), entry.Value);
126 }
127
128 // core.mal: defined using the language itself
129 RE("(def! not (fn* (a) (if a false true)))");
130
131 if (args.Length > 0 && args[0] == "--raw") {
132 Mal.readline.mode = Mal.readline.Mode.Raw;
133 }
134
135 // repl loop
136 while (true) {
137 string line;
138 try {
139 line = Mal.readline.Readline("user> ");
140 if (line == null) { break; }
141 if (line == "") { continue; }
142 } catch (IOException e) {
143 Console.WriteLine("IOException: " + e.Message);
144 break;
145 }
146 try {
147 Console.WriteLine(PRINT(RE(line)));
148 } catch (Mal.types.MalContinue) {
149 continue;
150 } catch (Exception e) {
151 Console.WriteLine("Error: " + e.Message);
152 Console.WriteLine(e.StackTrace);
153 continue;
154 }
155 }
156 }
157 }
158 }