Commit | Line | Data |
---|---|---|
e6fd6af2 JM |
1 | #!/usr/bin/env julia |
2 | ||
82484631 | 3 | push!(LOAD_PATH, pwd(), "/usr/share/julia/base") |
85110962 | 4 | import readline_mod |
e6fd6af2 JM |
5 | import reader |
6 | import printer | |
7 | using env | |
8 | import core | |
9 | using types | |
10 | ||
11 | # READ | |
12 | function READ(str) | |
13 | reader.read_str(str) | |
14 | end | |
15 | ||
16 | # EVAL | |
17 | function ispair(ast) | |
18 | (isa(ast, Array) || isa(ast, Tuple)) && length(ast) > 0 | |
19 | end | |
20 | ||
21 | function quasiquote(ast) | |
22 | if !ispair(ast) | |
82484631 | 23 | [[:quote]; Any[ast]] |
e6fd6af2 JM |
24 | elseif ast[1] == :unquote |
25 | ast[2] | |
26 | elseif ispair(ast[1]) && ast[1][1] == symbol("splice-unquote") | |
82484631 | 27 | [[:concat]; Any[ast[1][2]]; Any[quasiquote(ast[2:end])]] |
e6fd6af2 | 28 | else |
82484631 | 29 | [[:cons]; Any[quasiquote(ast[1])]; Any[quasiquote(ast[2:end])]] |
e6fd6af2 JM |
30 | end |
31 | end | |
32 | ||
33 | function eval_ast(ast, env) | |
34 | if typeof(ast) == Symbol | |
82484631 | 35 | env_get(env,ast) |
e6fd6af2 JM |
36 | elseif isa(ast, Array) || isa(ast, Tuple) |
37 | map((x) -> EVAL(x,env), ast) | |
7e0bb668 JM |
38 | elseif isa(ast, Dict) |
39 | [EVAL(x[1],env) => EVAL(x[2], env) for x=ast] | |
e6fd6af2 JM |
40 | else |
41 | ast | |
42 | end | |
43 | end | |
44 | ||
45 | function EVAL(ast, env) | |
46 | while true | |
82484631 | 47 | #println("EVAL: $(printer.pr_str(ast,true))") |
2aa39ccd | 48 | if !isa(ast, Array) return eval_ast(ast, env) end |
e6fd6af2 JM |
49 | |
50 | # apply | |
51 | if :def! == ast[1] | |
82484631 | 52 | return env_set(env, ast[2], EVAL(ast[3], env)) |
e6fd6af2 JM |
53 | elseif symbol("let*") == ast[1] |
54 | let_env = Env(env) | |
55 | for i = 1:2:length(ast[2]) | |
82484631 | 56 | env_set(let_env, ast[2][i], EVAL(ast[2][i+1], let_env)) |
e6fd6af2 JM |
57 | end |
58 | env = let_env | |
59 | ast = ast[3] | |
60 | # TCO loop | |
61 | elseif :quote == ast[1] | |
62 | return ast[2] | |
63 | elseif :quasiquote == ast[1] | |
64 | ast = quasiquote(ast[2]) | |
65 | # TCO loop | |
66 | elseif :do == ast[1] | |
67 | eval_ast(ast[2:end-1], env) | |
68 | ast = ast[end] | |
69 | # TCO loop | |
70 | elseif :if == ast[1] | |
71 | cond = EVAL(ast[2], env) | |
72 | if cond === nothing || cond === false | |
73 | if length(ast) >= 4 | |
74 | ast = ast[4] | |
75 | # TCO loop | |
76 | else | |
77 | return nothing | |
78 | end | |
79 | else | |
80 | ast = ast[3] | |
81 | # TCO loop | |
82 | end | |
83 | elseif symbol("fn*") == ast[1] | |
84 | return MalFunc( | |
f98e3ea9 | 85 | (args...) -> EVAL(ast[3], Env(env, ast[2], Any[args...])), |
e6fd6af2 JM |
86 | ast[3], env, ast[2]) |
87 | else | |
88 | el = eval_ast(ast, env) | |
89 | f, args = el[1], el[2:end] | |
90 | if isa(f, MalFunc) | |
91 | ast = f.ast | |
92 | env = Env(f.env, f.params, args) | |
93 | # TCO loop | |
94 | else | |
95 | return f(args...) | |
96 | end | |
97 | end | |
98 | end | |
99 | end | |
100 | ||
101 | ||
102 | function PRINT(exp) | |
103 | printer.pr_str(exp) | |
104 | end | |
105 | ||
106 | # REPL | |
107 | repl_env = nothing | |
108 | function REP(str) | |
109 | return PRINT(EVAL(READ(str), repl_env)) | |
110 | end | |
111 | ||
112 | # core.jl: defined using Julia | |
113 | repl_env = Env(nothing, core.ns) | |
82484631 JM |
114 | env_set(repl_env, :eval, (ast) -> EVAL(ast, repl_env)) |
115 | env_set(repl_env, symbol("*ARGV*"), ARGS[2:end]) | |
e6fd6af2 JM |
116 | |
117 | # core.mal: defined using the language itself | |
118 | REP("(def! not (fn* (a) (if a false true)))") | |
119 | REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") | |
120 | ||
121 | if length(ARGS) > 0 | |
122 | REP("(load-file \"$(ARGS[1])\")") | |
123 | exit(0) | |
124 | end | |
125 | ||
126 | while true | |
85110962 JM |
127 | line = readline_mod.do_readline("user> ") |
128 | if line === nothing break end | |
e6fd6af2 JM |
129 | try |
130 | println(REP(line)) | |
131 | catch e | |
132 | if isa(e, ErrorException) | |
133 | println("Error: $(e.msg)") | |
134 | else | |
135 | println("Error: $(string(e))") | |
136 | end | |
82484631 JM |
137 | # TODO: show at least part of stack |
138 | if !isa(e, StackOverflowError) | |
139 | bt = catch_backtrace() | |
140 | Base.show_backtrace(STDERR, bt) | |
141 | end | |
e6fd6af2 JM |
142 | println() |
143 | end | |
144 | end |