Julia: step A. Add atoms and metadata.
[jackhill/mal.git] / julia / stepA_mal.jl
CommitLineData
85110962
JM
1#!/usr/bin/env julia
2
3import readline_mod
4import reader
5import printer
6using env
7import core
8using types
9
10# READ
11function READ(str)
12 reader.read_str(str)
13end
14
15# EVAL
16function ispair(ast)
17 (isa(ast, Array) || isa(ast, Tuple)) && length(ast) > 0
18end
19
20function quasiquote(ast)
21 if !ispair(ast)
22 [[:quote], Any[ast]]
23 elseif ast[1] == :unquote
24 ast[2]
25 elseif ispair(ast[1]) && ast[1][1] == symbol("splice-unquote")
26 [[:concat], Any[ast[1][2]], Any[quasiquote(ast[2:end])]]
27 else
28 [[:cons], Any[quasiquote(ast[1])], Any[quasiquote(ast[2:end])]]
29 end
30end
31
32function ismacroCall(ast, env)
33 return isa(ast, Array) &&
34 isa(ast[1], Symbol) &&
35 find(env, ast[1]) != nothing &&
36 isa(get(env, ast[1]), MalFunc) &&
37 get(env, ast[1]).ismacro
38end
39
40function macroexpand(ast, env)
41 while ismacroCall(ast, env)
42 mac = get(env, ast[1])
43 ast = mac.fn(ast[2:end]...)
44 end
45 ast
46end
47
48function eval_ast(ast, env)
49 if typeof(ast) == Symbol
50 get(env,ast)
51 elseif isa(ast, Array) || isa(ast, Tuple)
52 map((x) -> EVAL(x,env), ast)
53 else
54 ast
55 end
56end
57
58function EVAL(ast, env)
59 while true
60 #println("EVAL: $(printer.pr_str(ast,true))")
61 if !isa(ast, Array) return eval_ast(ast, env) end
62
63 # apply
64 ast = macroexpand(ast, env)
65 if !isa(ast, Array) return ast end
66
67 if :def! == ast[1]
68 return set(env, ast[2], EVAL(ast[3], env))
69 elseif symbol("let*") == ast[1]
70 let_env = Env(env)
71 for i = 1:2:length(ast[2])
72 set(let_env, ast[2][i], EVAL(ast[2][i+1], let_env))
73 end
74 env = let_env
75 ast = ast[3]
76 # TCO loop
77 elseif :quote == ast[1]
78 return ast[2]
79 elseif :quasiquote == ast[1]
80 ast = quasiquote(ast[2])
81 # TCO loop
82 elseif :defmacro! == ast[1]
83 func = EVAL(ast[3], env)
84 func.ismacro = true
85 return set(env, ast[2], func)
86 elseif :macroexpand == ast[1]
87 return macroexpand(ast[2], env)
88 elseif symbol("try*") == ast[1]
89 try
90 return EVAL(ast[2], env)
91 catch exc
92 e = string(exc)
93 if isa(exc, MalException)
94 e = exc.malval
95 elseif isa(exc, ErrorException)
96 e = exc.msg
97 else
98 e = string(e)
99 end
100 if length(ast) > 2 && ast[3][1] == symbol("catch*")
101 return EVAL(ast[3][3], Env(env, {ast[3][2]}, {e}))
102 else
103 rethrow(exc)
104 end
105 end
106 elseif :do == ast[1]
107 eval_ast(ast[2:end-1], env)
108 ast = ast[end]
109 # TCO loop
110 elseif :if == ast[1]
111 cond = EVAL(ast[2], env)
112 if cond === nothing || cond === false
113 if length(ast) >= 4
114 ast = ast[4]
115 # TCO loop
116 else
117 return nothing
118 end
119 else
120 ast = ast[3]
121 # TCO loop
122 end
123 elseif symbol("fn*") == ast[1]
124 return MalFunc(
125 (args...) -> EVAL(ast[3], Env(env, ast[2], args)),
126 ast[3], env, ast[2])
127 else
128 el = eval_ast(ast, env)
129 f, args = el[1], el[2:end]
130 if isa(f, MalFunc)
131 ast = f.ast
132 env = Env(f.env, f.params, args)
133 # TCO loop
134 else
135 return f(args...)
136 end
137 end
138 end
139end
140
141# PRINT
142function PRINT(exp)
143 printer.pr_str(exp)
144end
145
146# REPL
147repl_env = nothing
148function REP(str)
149 return PRINT(EVAL(READ(str), repl_env))
150end
151
152# core.jl: defined using Julia
153repl_env = Env(nothing, core.ns)
154set(repl_env, :eval, (ast) -> EVAL(ast, repl_env))
155set(repl_env, symbol("*ARGV*"), ARGS[2:end])
156
157# core.mal: defined using the language itself
158REP("(def! *host-language* \"julia\")")
159REP("(def! not (fn* (a) (if a false true)))")
160REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
161REP("(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)))))))")
162REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
163
164
165if length(ARGS) > 0
166 REP("(load-file \"$(ARGS[1])\")")
167 exit(0)
168end
169
170REP("(println (str \"Mal [\" *host-language* \"]\"))")
171while true
172 line = readline_mod.do_readline("user> ")
173 if line === nothing break end
174 try
175 println(REP(line))
176 catch e
177 if isa(e, ErrorException)
178 println("Error: $(e.msg)")
179 else
180 println("Error: $(string(e))")
181 end
182 bt = catch_backtrace()
183 Base.show_backtrace(STDERR, bt)
184 println()
185 end
186end