bash, c, clojure, coffee, es6, js: add seq/string?
[jackhill/mal.git] / js / stepA_mal.js
1 if (typeof module !== 'undefined') {
2 var types = require('./types');
3 var readline = require('./node_readline');
4 var reader = require('./reader');
5 var printer = require('./printer');
6 var Env = require('./env').Env;
7 var core = require('./core');
8 var interop = require('./interop');
9 }
10
11 // read
12 function READ(str) {
13 return reader.read_str(str);
14 }
15
16 // eval
17 function is_pair(x) {
18 return types._sequential_Q(x) && x.length > 0;
19 }
20
21 function quasiquote(ast) {
22 if (!is_pair(ast)) {
23 return [types._symbol("quote"), ast];
24 } else if (types._symbol_Q(ast[0]) && ast[0].value === 'unquote') {
25 return ast[1];
26 } else if (is_pair(ast[0]) && ast[0][0].value === 'splice-unquote') {
27 return [types._symbol("concat"),
28 ast[0][1],
29 quasiquote(ast.slice(1))];
30 } else {
31 return [types._symbol("cons"),
32 quasiquote(ast[0]),
33 quasiquote(ast.slice(1))];
34 }
35 }
36
37 function is_macro_call(ast, env) {
38 return types._list_Q(ast) &&
39 types._symbol_Q(ast[0]) &&
40 env.find(ast[0]) &&
41 env.get(ast[0])._ismacro_;
42 }
43
44 function macroexpand(ast, env) {
45 while (is_macro_call(ast, env)) {
46 var mac = env.get(ast[0]);
47 ast = mac.apply(mac, ast.slice(1));
48 }
49 return ast;
50 }
51
52 function eval_ast(ast, env) {
53 if (types._symbol_Q(ast)) {
54 return env.get(ast);
55 } else if (types._list_Q(ast)) {
56 return ast.map(function(a) { return EVAL(a, env); });
57 } else if (types._vector_Q(ast)) {
58 var v = ast.map(function(a) { return EVAL(a, env); });
59 v.__isvector__ = true;
60 return v;
61 } else if (types._hash_map_Q(ast)) {
62 var new_hm = {};
63 for (k in ast) {
64 new_hm[EVAL(k, env)] = EVAL(ast[k], env);
65 }
66 return new_hm;
67 } else {
68 return ast;
69 }
70 }
71
72 function _EVAL(ast, env) {
73 while (true) {
74
75 //printer.println("EVAL:", printer._pr_str(ast, true));
76 if (!types._list_Q(ast)) {
77 return eval_ast(ast, env);
78 }
79
80 // apply list
81 ast = macroexpand(ast, env);
82 if (!types._list_Q(ast)) {
83 return eval_ast(ast, env);
84 }
85
86 var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3];
87 switch (a0.value) {
88 case "def!":
89 var res = EVAL(a2, env);
90 return env.set(a1, res);
91 case "let*":
92 var let_env = new Env(env);
93 for (var i=0; i < a1.length; i+=2) {
94 let_env.set(a1[i], EVAL(a1[i+1], let_env));
95 }
96 ast = a2;
97 env = let_env;
98 break;
99 case "quote":
100 return a1;
101 case "quasiquote":
102 ast = quasiquote(a1);
103 break;
104 case 'defmacro!':
105 var func = EVAL(a2, env);
106 func._ismacro_ = true;
107 return env.set(a1, func);
108 case 'macroexpand':
109 return macroexpand(a1, env);
110 case "js*":
111 return eval(a1.toString());
112 case ".":
113 var el = eval_ast(ast.slice(2), env),
114 r = interop.resolve_js(a1.toString()),
115 obj = r[0], f = r[1];
116 var res = f.apply(obj, el);
117 console.log("DEBUG3:", res);
118 return interop.js_to_mal(res);
119 case "try*":
120 try {
121 return EVAL(a1, env);
122 } catch (exc) {
123 if (a2 && a2[0].value === "catch*") {
124 if (exc instanceof Error) { exc = exc.message; }
125 return EVAL(a2[2], new Env(env, [a2[1]], [exc]));
126 } else {
127 throw exc;
128 }
129 }
130 case "do":
131 eval_ast(ast.slice(1, -1), env);
132 ast = ast[ast.length-1];
133 break;
134 case "if":
135 var cond = EVAL(a1, env);
136 if (cond === null || cond === false) {
137 ast = (typeof a3 !== "undefined") ? a3 : null;
138 } else {
139 ast = a2;
140 }
141 break;
142 case "fn*":
143 return types._function(EVAL, Env, a2, env, a1);
144 default:
145 var el = eval_ast(ast, env), f = el[0];
146 if (f.__ast__) {
147 ast = f.__ast__;
148 env = f.__gen_env__(el.slice(1));
149 } else {
150 return f.apply(f, el.slice(1));
151 }
152 }
153
154 }
155 }
156
157 function EVAL(ast, env) {
158 var result = _EVAL(ast, env);
159 return (typeof result !== "undefined") ? result : null;
160 }
161
162 // print
163 function PRINT(exp) {
164 return printer._pr_str(exp, true);
165 }
166
167 // repl
168 var repl_env = new Env();
169 var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };
170
171 // core.js: defined using javascript
172 for (var n in core.ns) { repl_env.set(types._symbol(n), core.ns[n]); }
173 repl_env.set(types._symbol('eval'), function(ast) {
174 return EVAL(ast, repl_env); });
175 repl_env.set(types._symbol('*ARGV*'), []);
176
177 // core.mal: defined using the language itself
178 rep("(def! *host-language* \"javascript\")")
179 rep("(def! not (fn* (a) (if a false true)))");
180 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
181 rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))");
182 rep("(def! *gensym-counter* (atom 0))");
183 rep("(def! gensym (fn* [] (symbol (str \"G__\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))");
184 rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))");
185
186 if (typeof process !== 'undefined' && process.argv.length > 2) {
187 repl_env.set(types._symbol('*ARGV*'), process.argv.slice(3));
188 rep('(load-file "' + process.argv[2] + '")');
189 process.exit(0);
190 }
191
192 // repl loop
193 if (typeof require !== 'undefined' && require.main === module) {
194 // Synchronous node.js commandline mode
195 rep("(println (str \"Mal [\" *host-language* \"]\"))");
196 while (true) {
197 var line = readline.readline("user> ");
198 if (line === null) { break; }
199 try {
200 if (line) { printer.println(rep(line)); }
201 } catch (exc) {
202 if (exc instanceof reader.BlankException) { continue; }
203 if (exc.stack) { printer.println(exc.stack); }
204 else { printer.println(exc); }
205 }
206 }
207 }