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