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