matlab: support Octave 4.0.0
[jackhill/mal.git] / d / step6_file.d
1 module main;
2
3 import std.algorithm;
4 import std.array;
5 import std.range;
6 import std.stdio;
7 import std.string;
8 import std.c.process;
9 import env;
10 import mal_core;
11 import readline;
12 import reader;
13 import printer;
14 import types;
15
16 MalType READ(string str)
17 {
18 return read_str(str);
19 }
20
21 MalType eval_ast(MalType ast, Env env)
22 {
23 if (typeid(ast) == typeid(MalSymbol))
24 {
25 auto sym = verify_cast!MalSymbol(ast);
26 return env.get(sym);
27 }
28 else if (typeid(ast) == typeid(MalList))
29 {
30 auto lst = verify_cast!MalList(ast);
31 auto el = array(lst.elements.map!(e => EVAL(e, env)));
32 return new MalList(el);
33 }
34 else if (typeid(ast) == typeid(MalVector))
35 {
36 auto lst = verify_cast!MalVector(ast);
37 auto el = array(lst.elements.map!(e => EVAL(e, env)));
38 return new MalVector(el);
39 }
40 else if (typeid(ast) == typeid(MalHashmap))
41 {
42 auto hm = verify_cast!MalHashmap(ast);
43 typeof(hm.data) new_data;
44 foreach (string k, MalType v; hm.data)
45 {
46 new_data[k] = EVAL(v, env);
47 }
48 return new MalHashmap(new_data);
49 }
50 else
51 {
52 return ast;
53 }
54 }
55
56 MalType EVAL(MalType ast, Env env)
57 {
58 for (;;)
59 {
60 MalList ast_list = cast(MalList) ast;
61 if (ast_list is null)
62 {
63 return eval_ast(ast, env);
64 }
65
66 auto aste = ast_list.elements;
67 auto a0_sym = cast(MalSymbol) aste[0];
68 auto sym_name = a0_sym is null ? "" : a0_sym.name;
69 switch (sym_name)
70 {
71 case "def!":
72 auto a1 = verify_cast!MalSymbol(aste[1]);
73 return env.set(a1, EVAL(aste[2], env));
74
75 case "let*":
76 auto a1 = verify_cast!MalSequential(aste[1]);
77 auto let_env = new Env(env);
78 foreach (kv; chunks(a1.elements, 2))
79 {
80 if (kv.length < 2) throw new Exception("let* requires even number of elements");
81 auto var_name = verify_cast!MalSymbol(kv[0]);
82 let_env.set(var_name, EVAL(kv[1], let_env));
83 }
84 ast = aste[2];
85 env = let_env;
86 continue; // TCO
87
88 case "do":
89 auto all_but_last = new MalList(aste[1..$-1]);
90 eval_ast(all_but_last, env);
91 ast = aste[$-1];
92 continue; // TCO
93
94 case "if":
95 auto cond = EVAL(aste[1], env);
96 if (cond.is_truthy())
97 {
98 ast = aste[2];
99 continue; // TCO
100 }
101 else
102 if (aste.length > 3)
103 {
104 ast = aste[3];
105 continue; // TCO
106 }
107 else
108 {
109 return mal_nil;
110 }
111
112 case "fn*":
113 auto args_list = verify_cast!MalSequential(aste[1]);
114 return new MalFunc(args_list.elements, aste[2], env);
115
116 default:
117 auto el = verify_cast!MalList(eval_ast(ast, env));
118 if (el.elements.length == 0)
119 {
120 throw new Exception("Expected a non-empty list");
121 }
122 auto first = el.elements[0];
123 auto rest = el.elements[1..$];
124 if (typeid(first) == typeid(MalFunc))
125 {
126 auto funcobj = verify_cast!MalFunc(first);
127 auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);
128 ast = funcobj.func_body;
129 env = callenv;
130 continue; // TCO
131 }
132 else if (typeid(first) == typeid(MalBuiltinFunc))
133 {
134 auto builtinfuncobj = verify_cast!MalBuiltinFunc(first);
135 return builtinfuncobj.fn(rest);
136 }
137 else
138 {
139 throw new Exception("Expected a function");
140 }
141 }
142 }
143 }
144
145 string PRINT(MalType ast)
146 {
147 return pr_str(ast);
148 }
149
150 MalType re(string str, Env env)
151 {
152 return EVAL(READ(str), env);
153 }
154
155 string rep(string str, Env env)
156 {
157 return PRINT(re(str, env));
158 }
159
160 static MalList create_argv_list(string[] args)
161 {
162 if (args.length <= 2) return new MalList([]);
163 return new MalList(array(args[2..$].map!(s => cast(MalType)(new MalString(s)))));
164 }
165
166 void main(string[] args)
167 {
168 Env repl_env = new Env(null);
169 foreach (string sym_name, BuiltinStaticFuncType f; core_ns)
170 {
171 repl_env.set(new MalSymbol(sym_name), new MalBuiltinFunc(f, sym_name));
172 }
173
174 BuiltinFuncType eval_func = (a ...) {
175 verify_args_count(a, 1);
176 return EVAL(a[0], repl_env);
177 };
178 repl_env.set(new MalSymbol("eval"), new MalBuiltinFunc(eval_func, "eval"));
179 repl_env.set(new MalSymbol("*ARGV*"), create_argv_list(args));
180
181 // core.mal: defined using the language itself
182 re("(def! not (fn* (a) (if a false true)))", repl_env);
183 re("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env);
184
185 if (args.length > 1)
186 {
187 try
188 {
189 rep("(load-file \"" ~ args[1] ~ "\")", repl_env);
190 return;
191 }
192 catch (Exception e)
193 {
194 writeln("Error: ", e.msg);
195 std.c.process.exit(1);
196 }
197 }
198
199 for (;;)
200 {
201 string line = _readline("user> ");
202 if (line is null) break;
203 if (line.length == 0) continue;
204 try
205 {
206 writeln(rep(line, repl_env));
207 }
208 catch (Exception e)
209 {
210 writeln("Error: ", e.msg);
211 }
212 }
213 writeln("");
214 }