-
Notifications
You must be signed in to change notification settings - Fork 0
/
interpreter.rb
130 lines (104 loc) · 3.25 KB
/
interpreter.rb
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
module Schemer
class Interpreter
attr_reader :env
def initialize(ast = nil)
@ast = ast
@env = Environment.new do |env|
env.add_binding(:+, lambda do |a, b|
a + b
end)
env.add_binding(:-, lambda do |a, b|
a - b
end)
env.add_binding(:*, lambda do |a, b|
a * b
end)
env.add_binding(:/, lambda do |a, b|
a / b
end)
env.add_binding(:write, lambda do |value|
$stdout.print value
nil
end)
env.add_binding(:inspect, lambda do |object|
object.inspect
end)
env.add_binding(:define, lambda do |parameters, body|
if parameters.is_a?(AST::Identifier)
# We are declaring a variable. We must eager-evaluate the value.
result = if body.is_a?(AST::Procedure)
body.eval(env)
else
body
end
env.add_binding(parameters.value, result)
return nil
end
original_params = parameters.to_list.to_a
name = original_params.shift
env.add_binding(name.value, lambda do |*args|
# Create a new scope
environment = Environment.new(env)
args.each_with_index do |arg, idx|
environment.add_binding(original_params[idx].value, arg.eval(environment))
end
body.eval(environment)
end)
nil
end)
env.add_binding(:lambda, lambda do |parameters, body|
original_params = parameters.to_list.to_a
lambda do |*args|
environment = Environment.new(env)
args.each_with_index do |arg, idx|
environment.add_binding(original_params[idx].value, arg.eval(environment))
end
body.eval(environment)
end
end)
env.add_binding(:car, lambda do |list|
list.to_list.elements.first
end)
env.add_binding(:cdr, lambda do |list|
list.to_list.elements.last
end)
env.add_binding(:cadr, lambda do |list|
list.to_list.elements.last.elements.first
end)
env.add_binding(:caddr, lambda do |list|
list.to_list.elements.last.elements.last.elements.first
end)
env.add_binding(:list, lambda do |*args|
AST::List.new(args)
end)
env.add_binding(:null?, lambda do |object|
(object.respond_to?(:empty?) && object.empty?) || object.nil?
end)
env.add_binding("=", lambda do |one, another|
one == another
end)
env.add_binding(:eqv?, lambda do |one, another|
one == another
end)
env.add_binding(">", lambda do |one, another|
one > another
end)
env.add_binding("<", lambda do |one, another|
one < another
end)
env.add_binding(:cond, lambda do |*conditions|
conditions.map!(&:to_list).map!(&:to_a)
conditions.each do |condition, result|
return result.eval(env) if condition.eval(env)
end
nil
end)
end
end
def walk
@ast.map do |node|
node.eval @env
end.last
end
end
end