forked from mame/cookpad-hackarade-minruby
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinterp.rb
More file actions
executable file
·236 lines (198 loc) · 5.83 KB
/
interp.rb
File metadata and controls
executable file
·236 lines (198 loc) · 5.83 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#!/usr/bin/env ruby
require "minruby"
# An implementation of the evaluator
def evaluate(exp, env)
# exp: A current node of AST
# env: An environment (explained later)
pp "exp => #{exp}" and pp "env => #{env}" and pp '--------------------------------------------------' if ARGV.last == "--debug"
case exp[0]
#
## Problem 1: Arithmetics
#
when "lit"
exp[1] # return the immediate value as is
when "+"
evaluate(exp[1], env) + evaluate(exp[2], env)
when "-"
# Subtraction. Please fill in.
# Use the code above for addition as a reference.
# (Almost just copy-and-paste. This is an exercise.)
evaluate(exp[1], env) - evaluate(exp[2], env)
when "*"
evaluate(exp[1], env) * evaluate(exp[2], env)
# ... Implement other operators that you need
when "/"
evaluate(exp[1], env) / evaluate(exp[2], env)
when "%"
evaluate(exp[1], env) % evaluate(exp[2], env)
when ">"
evaluate(exp[1], env) > evaluate(exp[2], env)
when "<"
evaluate(exp[1], env) < evaluate(exp[2], env)
when "=="
evaluate(exp[1], env) == evaluate(exp[2], env)
#
## Problem 2: Statements and variables
#
when "stmts"
# Statements: sequential evaluation of one or more expressions.
#
# Advice 1: Insert `pp(exp)` and observe the AST first.
# Advice 2: Apply `evaluate` to each child of this node.
i = 1
while exp[i] != nil
ret = evaluate(exp[i], env)
i = i + 1
end
ret
# The second argument of this method, `env`, is an "environement" that
# keeps track of the values stored to variables.
# It is a Hash object whose key is a variable name and whose value is a
# value stored to the corresponded variable.
when "var_ref"
# Variable reference: lookup the value corresponded to the variable
#
# Advice: env[???]
env[exp[1]]
when "var_assign"
# Variable assignment: store (or overwrite) the value to the environment
#
# Advice: env[???] = ???
env[exp[1]] = evaluate(exp[2], env)
#
## Problem 3: Branchs and loops
#
when "if"
# Branch. It evaluates either exp[2] or exp[3] depending upon the
# evaluation result of exp[1],
#
# Advice:
# if ???
# ???
# else
# ???
# end
if evaluate(exp[1], env)
evaluate(exp[2], env)
else
evaluate(exp[3], env)
end
when "while"
# Loop.
while evaluate(exp[1], env)
evaluate(exp[2], env)
end
#
## Problem 4: Function calls
#
when "func_call"
# Lookup the function definition by the given function name.
func = $function_definitions[exp[1]]
if func.nil?
# We couldn't find a user-defined function definition;
# it should be a builtin function.
# Dispatch upon the given function name, and do paticular tasks.
case exp[1]
when "p"
# MinRuby's `p` method is implemented by Ruby's `p` method.
p(evaluate(exp[2], env))
# ... Problem 4
when "Integer"
Integer(evaluate(exp[2], env))
when "fizzbuzz"
n = evaluate(exp[2], env)
if n % 3 == 0
if n % 5 == 0
"FizzBuzz"
else
"Fizz"
end
else
if n % 5 == 0
"Buzz"
else
n
end
end
else
raise("unknown builtin function")
end
else
#
## Problem 5: Function definition
#
# (You may want to implement "func_def" first.)
#
# Here, we could find a user-defined function definition.
# The variable `func` should be a value that was stored at "func_def":
# a parameter list and an AST of function body.
#
# A function call evaluates the AST of function body within a new scope.
# You know, you cannot access a varible out of function.
# Therefore, you need to create a new environment, and evaluate the
# function body under the environment.
#
# Note, you can access formal parameters (*1) in function body.
# So, the new environment must be initialized with each parameter.
#
# (*1) formal parameter: a variable as found in the function definition.
# For example, `a`, `b`, and `c` are the formal parameters of
# `def foo(a, b, c)`.
# func: [[1st formal parameter, 2nd formal parameter, ...], function body]
# exp: ["func_call", function_name, 1st actual parameter, 2nd actual parameter, ...]
env2 = {}
i = 0
while func[0][i] != nil
env2[func[0][i]] = evaluate(exp[i + 2], env)
i = i + 1
end
evaluate(func[1], env2)
end
when "func_def"
# Function definition.
#
# Add a new function definition to function definition list.
# The AST of "func_def" contains function name, parameter list, and the
# child AST of function body.
# All you need is store them into $function_definitions.
#
# Advice: $function_definitions[???] = ???
# ['function_name'] = [[1st formal parameter, 2nd formal parameter, ...], function body]
$function_definitions[exp[1]] = exp[2..-1]
#
## Problem 6: Arrays and Hashes
#
# You don't need advices anymore, do you?
when "ary_new"
array = []
i = 1
while exp[i] != nil
array.push(evaluate(exp[i], env))
i = i + 1
end
array
when "ary_ref"
array = evaluate(exp[1], env)
array[ evaluate(exp[2], env) ]
when "ary_assign"
array = evaluate(exp[1], env)
array[ evaluate(exp[2], env) ] = evaluate(exp[3], env)
when "hash_new"
hash = {}
i = 1
while exp[i] != nil
hash[ evaluate(exp[i], env) ] = evaluate(exp[i + 1], env)
i = i + 2
end
hash
else
p("error")
pp(exp)
raise("unknown node")
end
end
$function_definitions = {}
env = {}
# `minruby_load()` == `File.read(ARGV.shift)`
# `minruby_parse(str)` parses a program text given, and returns its AST
evaluate(minruby_parse(minruby_load()), env)