Commit | Line | Data |
---|---|---|
e10ceff5 JM |
1 | import rl from './node_readline.js' |
2 | const readline = rl.readline | |
a05c086f | 3 | import { _list_Q, _malfunc, _malfunc_Q } from './types' |
4f8c7db9 JM |
4 | import { BlankException, read_str } from './reader' |
5 | import { pr_str } from './printer' | |
6 | import { new_env, env_set, env_get } from './env' | |
7 | import { core_ns } from './core' | |
73bd649f JM |
8 | |
9 | // read | |
a05c086f | 10 | const READ = str => read_str(str) |
73bd649f JM |
11 | |
12 | // eval | |
13 | const eval_ast = (ast, env) => { | |
a05c086f | 14 | if (typeof ast === 'symbol') { |
73bd649f | 15 | return env_get(env, ast) |
a05c086f JM |
16 | } else if (ast instanceof Array) { |
17 | return ast.map(x => EVAL(x, env)) | |
18 | } else if (ast instanceof Map) { | |
4f8c7db9 | 19 | let new_hm = new Map() |
a05c086f | 20 | ast.forEach((v, k) => new_hm.set(EVAL(k, env), EVAL(v, env))) |
4f8c7db9 | 21 | return new_hm |
73bd649f | 22 | } else { |
4f8c7db9 | 23 | return ast |
73bd649f JM |
24 | } |
25 | } | |
26 | ||
27 | const EVAL = (ast, env) => { | |
28 | while (true) { | |
4f8c7db9 | 29 | //console.log('EVAL:', pr_str(ast, true)) |
73bd649f | 30 | if (!_list_Q(ast)) { return eval_ast(ast, env) } |
f8665761 | 31 | if (ast.length === 0) { return ast } |
73bd649f | 32 | |
4f8c7db9 | 33 | const [a0, a1, a2, a3] = ast |
a05c086f JM |
34 | switch (typeof a0 === 'symbol' ? Symbol.keyFor(a0) : Symbol(':default')) { |
35 | case 'def!': | |
4f8c7db9 | 36 | return env_set(env, a1, EVAL(a2, env)) |
73bd649f | 37 | case 'let*': |
4f8c7db9 | 38 | let let_env = new_env(env) |
73bd649f | 39 | for (let i=0; i < a1.length; i+=2) { |
4f8c7db9 | 40 | env_set(let_env, a1[i], EVAL(a1[i+1], let_env)) |
73bd649f | 41 | } |
4f8c7db9 JM |
42 | env = let_env |
43 | ast = a2 | |
a05c086f | 44 | break // continue TCO loop |
4f8c7db9 JM |
45 | case 'do': |
46 | eval_ast(ast.slice(1,-1), env) | |
47 | ast = ast[ast.length-1] | |
a05c086f | 48 | break // continue TCO loop |
4f8c7db9 JM |
49 | case 'if': |
50 | let cond = EVAL(a1, env) | |
73bd649f | 51 | if (cond === null || cond === false) { |
4f8c7db9 | 52 | ast = (typeof a3 !== 'undefined') ? a3 : null |
73bd649f | 53 | } else { |
4f8c7db9 | 54 | ast = a2 |
73bd649f | 55 | } |
a05c086f | 56 | break // continue TCO loop |
4f8c7db9 | 57 | case 'fn*': |
73bd649f | 58 | return _malfunc((...args) => EVAL(a2, new_env(env, a1, args)), |
a05c086f | 59 | a2, env, a1) |
73bd649f | 60 | default: |
4f8c7db9 | 61 | let [f, ...args] = eval_ast(ast, env) |
73bd649f | 62 | if (_malfunc_Q(f)) { |
4f8c7db9 JM |
63 | env = new_env(f.env, f.params, args) |
64 | ast = f.ast | |
a05c086f | 65 | break // continue TCO loop |
73bd649f | 66 | } else { |
4f8c7db9 | 67 | return f(...args) |
73bd649f JM |
68 | } |
69 | } | |
70 | } | |
71 | } | |
72 | ||
73 | ||
a05c086f | 74 | const PRINT = exp => pr_str(exp, true) |
73bd649f JM |
75 | |
76 | // repl | |
4f8c7db9 | 77 | let repl_env = new_env() |
a05c086f | 78 | const REP = str => PRINT(EVAL(READ(str), repl_env)) |
73bd649f JM |
79 | |
80 | // core.EXT: defined using ES6 | |
a05c086f JM |
81 | for (let [k, v] of core_ns) { env_set(repl_env, Symbol.for(k), v) } |
82 | env_set(repl_env, Symbol.for('eval'), a => EVAL(a, repl_env)) | |
83 | env_set(repl_env, Symbol.for('*ARGV*'), []) | |
73bd649f JM |
84 | |
85 | // core.mal: defined using language itself | |
4f8c7db9 | 86 | REP('(def! not (fn* (a) (if a false true)))') |
e6d41de4 | 87 | REP('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\nnil)")))))') |
73bd649f | 88 | |
a05c086f JM |
89 | if (process.argv.length > 2) { |
90 | env_set(repl_env, Symbol.for('*ARGV*'), process.argv.slice(3)) | |
4f8c7db9 JM |
91 | REP(`(load-file "${process.argv[2]}")`) |
92 | process.exit(0) | |
73bd649f JM |
93 | } |
94 | ||
95 | ||
96 | while (true) { | |
4f8c7db9 JM |
97 | let line = readline('user> ') |
98 | if (line == null) break | |
73bd649f | 99 | try { |
a05c086f | 100 | if (line) { console.log(REP(line)) } |
73bd649f | 101 | } catch (exc) { |
a05c086f | 102 | if (exc instanceof BlankException) { continue } |
dd7a4f55 JM |
103 | if (exc instanceof Error) { console.warn(exc.stack) } |
104 | else { console.warn(`Error: ${exc}`) } | |
73bd649f JM |
105 | } |
106 | } |