Merge remote-tracking branch 'kanaka/master' into kotlin
[jackhill/mal.git] / cs / step3_env.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 step3_env {
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, 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 if (!(a0 is MalSymbol)) {
59 throw new Mal.types.MalError("attempt to apply on non-symbol '"
60 + Mal.printer._pr_str(a0,true) + "'");
61 }
62
63 switch (((MalSymbol)a0).getName()) {
64 case "def!":
65 a1 = ast[1];
66 a2 = ast[2];
67 res = EVAL(a2, env);
68 env.set((MalSymbol)a1, 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, EVAL(val, let_env));
80 }
81 return EVAL(a2, let_env);
82 default:
83 el = (MalList)eval_ast(ast, env);
84 var f = (MalFunc)el[0];
85 return f.apply(el.rest());
86 }
87 }
88
89 // print
90 static string PRINT(MalVal exp) {
91 return printer._pr_str(exp, true);
92 }
93
94 // repl
95 static void Main(string[] args) {
96 var repl_env = new Mal.env.Env(null);
97 Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env);
98 repl_env.set(new MalSymbol("+"), new MalFunc(
99 a => (MalInt)a[0] + (MalInt)a[1]) );
100 repl_env.set(new MalSymbol("-"), new MalFunc(
101 a => (MalInt)a[0] - (MalInt)a[1]) );
102 repl_env.set(new MalSymbol("*"), new MalFunc(
103 a => (MalInt)a[0] * (MalInt)a[1]) );
104 repl_env.set(new MalSymbol("/"), new MalFunc(
105 a => (MalInt)a[0] / (MalInt)a[1]) );
106
107 if (args.Length > 0 && args[0] == "--raw") {
108 Mal.readline.mode = Mal.readline.Mode.Raw;
109 }
110
111 // repl loop
112 while (true) {
113 string line;
114 try {
115 line = Mal.readline.Readline("user> ");
116 if (line == null) { break; }
117 if (line == "") { continue; }
118 } catch (IOException e) {
119 Console.WriteLine("IOException: " + e.Message);
120 break;
121 }
122 try {
123 Console.WriteLine(PRINT(RE(line)));
124 } catch (Mal.types.MalContinue) {
125 continue;
126 } catch (Exception e) {
127 Console.WriteLine("Error: " + e.Message);
128 Console.WriteLine(e.StackTrace);
129 continue;
130 }
131 }
132 }
133 }
134 }