-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinterpreter.rb
More file actions
130 lines (106 loc) · 2.68 KB
/
interpreter.rb
File metadata and controls
130 lines (106 loc) · 2.68 KB
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
class Interpreter
module Methods
def define_func type, name, arguments, body
@functions[name] = [arguments.map{|x| x[1]}, body]
end
def define_var type, name, value = nil
assign name, value if value
end
def call name, arguments
evaluated = arguments.map{|x| self.eval(x)}
if function = Interpreter::PRIMITIVE_FUNCTIONS[name]
return function.call(*evaluated)
end
function = root_scope.functions[name]
raise "undefined function #{name}" if function.nil?
formal_params, body = function
new_scope = Scope.new(self)
formal_params.zip(evaluated).each do |name, value|
new_scope.variables[name] = value
end
catch(:return) do
body.each do |sexp|
new_scope.eval(sexp)
end
end
end
def root_scope
scope = self
scope = scope.upper_scope until scope.upper_scope.nil?
scope
end
def get name
@variables[name]
end
def int value
value.to_i
end
def float value
value.to_f
end
def return expression
result = self.eval expression
throw :return, result
end
def if condition, then_body, else_body = []
evaluated = self.eval condition
if evaluated && evaluated != 0
then_body.each{|x| self.eval x }
else
else_body.each{|x| self.eval x }
end
end
def for init, cond, step, body
self.eval init
evaluated = self.eval cond
while evaluated && evaluated != 0
body.each{|x| self.eval x }
self.eval step
evaluated = self.eval cond
end
end
def while cond, body
evaluated = self.eval cond
while evaluated && evaluated != 0
body.each{|x| self.eval x }
evaluated = self.eval cond
end
end
def assign name, value
@variables[name] = self.eval value
end
def inc name
prev = @variables[name]
@variables[name] += 1
prev
end
def dec name
prev = @variables[name]
@variables[name] -= 1
prev
end
def eval sexp
send(*sexp)
end
end
class Scope
attr_reader :functions, :variables, :upper_scope
include Methods
def initialize(upper_scope = nil)
@upper_scope = upper_scope
@variables = {}
@functions = {}
end
def inspect
"#<Interpreter::Scope:#{(object_id << 1).to_s(16)} @variables=#{@variables}>"
end
end
PRIMITIVE_FUNCTIONS = %w{+ - * / < > <= >= == !=}.map{|x| [x, ->(a, b){a.send(x, b)}] }.to_h
def interpret sexp
root_scope = Scope.new
sexp.each do |function|
root_scope.eval function
end
root_scope.call 'main', []
end
end