| 1 | --- step9_try ----------------------------------- |
| 2 | import types, reader, printer, env, core |
| 3 | |
| 4 | READ(str): return reader.read_str(str) |
| 5 | |
| 6 | quasiquote(ast): return ... // quasiquote |
| 7 | |
| 8 | macro?(ast, env): return ... // true if macro call |
| 9 | macroexpand(ast, env): return ... // recursive macro expansion |
| 10 | |
| 11 | eval_ast(ast,env): |
| 12 | switch type(ast): |
| 13 | symbol: return env.get(ast) |
| 14 | list,vector: return ast.map((x) -> EVAL(x,env)) |
| 15 | hash: return ast.map((k,v) -> list(k, EVAL(v,env))) |
| 16 | _default_: return ast |
| 17 | |
| 18 | EVAL(ast,env): |
| 19 | while true: |
| 20 | if not list?(ast): return eval_ast(ast, env) |
| 21 | |
| 22 | ast = macroexpand(ast, env) |
| 23 | if not list?(ast): return eval_ast(ast, env) |
| 24 | if empty?(ast): return ast |
| 25 | |
| 26 | switch ast[0]: |
| 27 | 'def!: return env.set(ast[1], EVAL(ast[2], env)) |
| 28 | 'let*: env = ...; ast = ast[2] // TCO |
| 29 | 'quote: return ast[1] |
| 30 | 'quasiquote: ast = quasiquote(ast[1]) // TCO |
| 31 | 'defmacro!: return ... // like def!, but set macro property |
| 32 | 'macroexpand: return macroexpand(ast[1], env) |
| 33 | 'try*: return ... // try/catch native and malval exceptions |
| 34 | 'do: ast = eval_ast(ast[1..-1], env)[-1] // TCO |
| 35 | 'if: EVAL(ast[1], env) ? ast = ast[2] : ast = ast[3] // TCO |
| 36 | 'fn*: return new MalFunc(...) |
| 37 | _default_: f, args = eval_ast(ast, env) |
| 38 | if malfunc?(f): ast = f.fn; env = ... // TCO |
| 39 | else: return apply(f, args) |
| 40 | |
| 41 | PRINT(exp): return printer.pr_str(exp) |
| 42 | |
| 43 | repl_env = new Env() |
| 44 | rep(str): return PRINT(EVAL(READ(str),repl_env)) |
| 45 | |
| 46 | ;; core.EXT: defined using Racket |
| 47 | core.ns.map((k,v) -> (repl_env.set(k, v))) |
| 48 | repl_env.set('eval, (ast) -> EVAL(ast, repl-env)) |
| 49 | repl_env.set('*ARGV*, cmdline_args[1..]) |
| 50 | |
| 51 | ;; core.mal: defined using the language itself |
| 52 | rep("(def! not (fn* (a) (if a false true)))") |
| 53 | rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))") |
| 54 | 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)))))))"); |
| 55 | |
| 56 | if cmdline_args: rep("(load-file \"" + args[0] + "\")"); exit 0 |
| 57 | |
| 58 | main loop: |
| 59 | try: println(rep(readline("user> "))) |
| 60 | catch e: println("Error: ", e) |
| 61 | |
| 62 | --- env module ---------------------------------- |
| 63 | class Env (outer=null,binds=[],exprs=[]) |
| 64 | data = hash_map() |
| 65 | foreach b, i in binds: |
| 66 | if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break |
| 67 | else: data[binds[i]] = exprs[i] |
| 68 | set(k,v): return data.set(k,v) |
| 69 | find(k): return data.has(k) ? this : (if outer ? find(outer) : null) |
| 70 | get(k): return data.find(k).get(k) OR raise "'" + k + "' not found" |
| 71 | |
| 72 | --- core module --------------------------------- |
| 73 | ns = {'=: equal?, |
| 74 | 'throw: throw, |
| 75 | |
| 76 | 'nil?: nil?, |
| 77 | 'true?: true?, |
| 78 | 'false?: false?, |
| 79 | 'symbol: symbol, |
| 80 | 'symbol?: symbol?, |
| 81 | 'keyword: keyword, |
| 82 | 'keyword?: keyword?, |
| 83 | |
| 84 | 'pr-str: (a) -> a.map(|s| pr_str(e,true)).join(" ")), |
| 85 | 'str: (a) -> a.map(|s| pr_str(e,false)).join("")), |
| 86 | 'prn: (a) -> println(a.map(|s| pr_str(e,true)).join(" ")), |
| 87 | 'println: (a) -> println(a.map(|s| pr_str(e,false)).join(" ")), |
| 88 | 'read-string: read_str, |
| 89 | 'slurp read-file, |
| 90 | |
| 91 | '<: lt, |
| 92 | '<=: lte, |
| 93 | '>: gt, |
| 94 | '>=: gte, |
| 95 | '+: add, |
| 96 | '-: sub, |
| 97 | '*: mult, |
| 98 | '/: div, |
| 99 | |
| 100 | 'list: list, |
| 101 | 'list?: list?, |
| 102 | 'vector: vector, |
| 103 | 'vector?: vector?, |
| 104 | 'hash-map: hash_map, |
| 105 | 'map?: hash_map?, |
| 106 | 'assoc: assoc, |
| 107 | 'dissoc: dissoc, |
| 108 | 'get: get, |
| 109 | 'contains?: contains?, |
| 110 | 'keys: keys, |
| 111 | 'vals: vals, |
| 112 | |
| 113 | 'sequential? sequential?, |
| 114 | 'cons: (a) -> concat([a[0]], a[1]), |
| 115 | 'concat: (a) -> reduce(concat, [], a), |
| 116 | 'vec: (l) -> l converted to vector, |
| 117 | 'nth: (a) -> a[0][a[1]] OR raise "nth: index out of range", |
| 118 | 'first: (a) -> a[0][0] OR nil, |
| 119 | 'rest: (a) -> a[0][1..] OR list(), |
| 120 | 'empty?: empty?, |
| 121 | 'count: count, |
| 122 | 'apply: apply, |
| 123 | 'map: map, |
| 124 | |
| 125 | 'atom: (a) -> new Atom(a[0]), |
| 126 | 'atom?: (a) -> type(a[0]) == "atom", |
| 127 | 'deref: (a) -> a[0].val, |
| 128 | 'reset!: (a) -> a[0].val = a[1], |
| 129 | 'swap!: swap!} |