Merge remote-tracking branch 'kanaka/master' into kotlin
[jackhill/mal.git] / julia / step5_tco.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 eval_ast(ast, env)
18 if typeof(ast) == Symbol
19 env_get(env,ast)
20 elseif isa(ast, Array) || isa(ast, Tuple)
21 map((x) -> EVAL(x,env), ast)
22 elseif isa(ast, Dict)
23 [EVAL(x[1],env) => EVAL(x[2], env) for x=ast]
24 else
25 ast
26 end
27 end
28
29 function EVAL(ast, env)
30 while true
31 #println("EVAL: $(printer.pr_str(ast,true))")
32 if !isa(ast, Array) return eval_ast(ast, env) end
33
34 # apply
35 if :def! == ast[1]
36 return env_set(env, ast[2], EVAL(ast[3], env))
37 elseif symbol("let*") == ast[1]
38 let_env = Env(env)
39 for i = 1:2:length(ast[2])
40 env_set(let_env, ast[2][i], EVAL(ast[2][i+1], let_env))
41 end
42 env = let_env
43 ast = ast[3]
44 # TCO loop
45 elseif :do == ast[1]
46 eval_ast(ast[2:end-1], env)
47 ast = ast[end]
48 # TCO loop
49 elseif :if == ast[1]
50 cond = EVAL(ast[2], env)
51 if cond === nothing || cond === false
52 if length(ast) >= 4
53 ast = ast[4]
54 # TCO loop
55 else
56 return nothing
57 end
58 else
59 ast = ast[3]
60 # TCO loop
61 end
62 elseif symbol("fn*") == ast[1]
63 return MalFunc(
64 (args...) -> EVAL(ast[3], Env(env, ast[2], args)),
65 ast[3], env, ast[2])
66 else
67 el = eval_ast(ast, env)
68 f, args = el[1], el[2:end]
69 if isa(f, MalFunc)
70 ast = f.ast
71 env = Env(f.env, f.params, args)
72 # TCO loop
73 else
74 return f(args...)
75 end
76 end
77 end
78 end
79
80 # PRINT
81 function PRINT(exp)
82 printer.pr_str(exp)
83 end
84
85 # REPL
86 repl_env = nothing
87 function REP(str)
88 return PRINT(EVAL(READ(str), repl_env))
89 end
90
91 # core.jl: defined using Julia
92 repl_env = Env(nothing, core.ns)
93
94 # core.mal: defined using the language itself
95 REP("(def! not (fn* (a) (if a false true)))")
96
97 while true
98 line = readline_mod.do_readline("user> ")
99 if line === nothing break end
100 try
101 println(REP(line))
102 catch e
103 if isa(e, ErrorException)
104 println("Error: $(e.msg)")
105 else
106 println("Error: $(string(e))")
107 end
108 # TODO: show at least part of stack
109 if !isa(e, StackOverflowError)
110 bt = catch_backtrace()
111 Base.show_backtrace(STDERR, bt)
112 end
113 println()
114 end
115 end