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