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