Merge pull request #337 from AndreaCrotti/upgrade-libraries
[jackhill/mal.git] / ruby / step7_quote.rb
CommitLineData
107d9694
IJ
1require_relative "mal_readline"
2require_relative "types"
3require_relative "reader"
4require_relative "printer"
5require_relative "env"
6require_relative "core"
01e25489
JM
7
8# read
9def READ(str)
10 return read_str(str)
11end
12
13# eval
14def pair?(x)
15 return sequential?(x) && x.size > 0
16end
17
18def quasiquote(ast)
19 if not pair?(ast)
20 return List.new [:quote, ast]
21 elsif ast[0] == :unquote
22 return ast[1]
23 elsif pair?(ast[0]) && ast[0][0] == :"splice-unquote"
24 return List.new [:concat, ast[0][1], quasiquote(ast.drop(1))]
25 else
26 return List.new [:cons, quasiquote(ast[0]), quasiquote(ast.drop(1))]
27 end
28end
29
30def eval_ast(ast, env)
31 return case ast
32 when Symbol
33 env.get(ast)
34 when List
35 List.new ast.map{|a| EVAL(a, env)}
36 when Vector
37 Vector.new ast.map{|a| EVAL(a, env)}
3e8a088f
JM
38 when Hash
39 new_hm = {}
40 ast.each{|k,v| new_hm[EVAL(k,env)] = EVAL(v, env)}
41 new_hm
01e25489
JM
42 else
43 ast
44 end
45end
46
47def EVAL(ast, env)
48 while true
49
3e8a088f
JM
50 #puts "EVAL: #{_pr_str(ast, true)}"
51
01e25489
JM
52 if not ast.is_a? List
53 return eval_ast(ast, env)
54 end
f53183a3
DM
55 if ast.empty?
56 return ast
57 end
01e25489
JM
58
59 # apply list
60 a0,a1,a2,a3 = ast
61 case a0
62 when :def!
63 return env.set(a1, EVAL(a2, env))
64 when :"let*"
65 let_env = Env.new(env)
66 a1.each_slice(2) do |a,e|
67 let_env.set(a, EVAL(e, let_env))
68 end
6301e0b6
JM
69 env = let_env
70 ast = a2 # Continue loop (TCO)
01e25489
JM
71 when :quote
72 return a1
73 when :quasiquote
6301e0b6 74 ast = quasiquote(a1); # Continue loop (TCO)
01e25489
JM
75 when :do
76 eval_ast(ast[1..-2], env)
6301e0b6 77 ast = ast.last # Continue loop (TCO)
01e25489
JM
78 when :if
79 cond = EVAL(a1, env)
80 if not cond
81 return nil if a3 == nil
6301e0b6 82 ast = a3 # Continue loop (TCO)
01e25489 83 else
6301e0b6 84 ast = a2 # Continue loop (TCO)
01e25489
JM
85 end
86 when :"fn*"
87 return Function.new(a2, env, a1) {|*args|
79859c62 88 EVAL(a2, Env.new(env, a1, List.new(args)))
01e25489
JM
89 }
90 else
91 el = eval_ast(ast, env)
92 f = el[0]
93 if f.class == Function
94 ast = f.ast
6301e0b6 95 env = f.gen_env(el.drop(1)) # Continue loop (TCO)
01e25489
JM
96 else
97 return f[*el.drop(1)]
98 end
99 end
100
101 end
102end
103
104# print
105def PRINT(exp)
106 return _pr_str(exp, true)
107end
108
109# repl
110repl_env = Env.new
111RE = lambda {|str| EVAL(READ(str), repl_env) }
112REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }
01e25489 113
8cb5cda4
JM
114# core.rb: defined using ruby
115$core_ns.each do |k,v| repl_env.set(k,v) end
116repl_env.set(:eval, lambda {|ast| EVAL(ast, repl_env)})
86b689f3 117repl_env.set(:"*ARGV*", List.new(ARGV.slice(1,ARGV.length) || []))
01e25489 118
8cb5cda4 119# core.mal: defined using the language itself
01e25489
JM
120RE["(def! not (fn* (a) (if a false true)))"]
121RE["(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"]
122
01e25489 123if ARGV.size > 0
86b689f3 124 RE["(load-file \"" + ARGV[0] + "\")"]
01e25489
JM
125 exit 0
126end
86b689f3
JM
127
128# repl loop
718887c3 129while line = _readline("user> ")
01e25489
JM
130 begin
131 puts REP[line]
132 rescue Exception => e
133 puts "Error: #{e}"
134 puts "\t#{e.backtrace.join("\n\t")}"
135 end
136end