rbsiev 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbsiev
4
+ class Error < StandardError; end
5
+ end
@@ -0,0 +1,330 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbsiev
4
+ class Evaluator
5
+ include Rubasteme::AST::Misc
6
+
7
+ def self.version
8
+ "(rbsiev.evaluator :version #{VERSION} :release #{RELEASE})"
9
+ end
10
+
11
+ def initialize
12
+ @verbose = false
13
+ end
14
+
15
+ attr_accessor :verbose
16
+
17
+ def eval(ast_node, env)
18
+ self.send(func_map(ast_node.type), ast_node, env)
19
+ end
20
+
21
+ def apply(procedure, arguments)
22
+ case procedure.type
23
+ when :procedure_primitive
24
+ procedure.apply(arguments)
25
+ when :procedure_compound
26
+ apply_compound_procedure(procedure, arguments)
27
+ else
28
+ raise Error, "Unknown procedure type -- APPLY: got=%s" % procedure.type.to_s
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ EV_FUNCTIONS_MAP = {
35
+ ast_empty_list: :eval_empty_list,
36
+ ast_boolean: :eval_boolean,
37
+ ast_identifier: :eval_variable,
38
+ ast_character: :eval_self_evaluating,
39
+ ast_string: :eval_self_evaluating,
40
+ ast_number: :eval_self_evaluating,
41
+ ast_program: :eval_program,
42
+ ast_quotation: :eval_quoted,
43
+ ast_procedure_call: :eval_combination,
44
+ ast_lambda_expression: :eval_lambda,
45
+ ast_conditional: :eval_if,
46
+ ast_assignment: :eval_assignment,
47
+ ast_identifier_definition: :eval_definition,
48
+ ast_cond: :eval_cond,
49
+ ast_and: :eval_and,
50
+ ast_or: :eval_or,
51
+ ast_when: :eval_when,
52
+ ast_unless: :eval_unless,
53
+ ast_let: :eval_let,
54
+ ast_let_star: :eval_let_star,
55
+ ast_letrec: :eval_letrec,
56
+ ast_letrec_star: :eval_letrec_star,
57
+ ast_begin: :eval_begin,
58
+ ast_do: :eval_do,
59
+ }
60
+
61
+ def func_map(ast_node_type)
62
+ func = EV_FUNCTIONS_MAP[ast_node_type]
63
+ if func.nil?
64
+ raise Error, "Unknown expression type -- EVAL: got=%s" % ast_node_type
65
+ end
66
+ func
67
+ end
68
+
69
+ def eval_empty_list(_ast_node, _env)
70
+ SCM_EMPTY_LIST
71
+ end
72
+
73
+ def eval_boolean(ast_node, _)
74
+ case ast_node.literal
75
+ when /\A#f(alse)?\Z/
76
+ false
77
+ when /\A#t(rue)?\Z/
78
+ true
79
+ else
80
+ raise Error, "Invalid boolean literal -- EVAL: got=%s" % exp[1]
81
+ end
82
+ end
83
+
84
+ def eval_variable(ast_node, env)
85
+ env.lookup_variable_value(identifier(ast_node))
86
+ end
87
+
88
+ def eval_self_evaluating(ast_node, env)
89
+ if ast_node.type == :ast_number and /([^\/]+)\/([^\/]+)/ === ast_node.literal
90
+ md = Regexp.last_match
91
+ Kernel.eval("Rational(#{md[1]}, #{md[2]})")
92
+ else
93
+ Kernel.eval(ast_node.literal)
94
+ end
95
+ end
96
+
97
+ def eval_program(ast_node, env)
98
+ result = nil
99
+ ast_node.each { |node|
100
+ result = self.eval(node, env)
101
+ }
102
+ result
103
+ end
104
+
105
+ def eval_quoted(ast_node, _env)
106
+ text_of_quotation(ast_node)
107
+ end
108
+
109
+ def eval_combination(ast_node, env)
110
+ apply(self.eval(ast_node.operator, env),
111
+ list_of_values(ast_node.operands, env))
112
+ end
113
+
114
+ def eval_lambda(ast_node, env)
115
+ parameters = ast_node.formals.map{|e| identifier(e)}
116
+ dummy_arguments = Array.new(parameters.size, :ev_unassigned)
117
+ extended_env = env.extend(parameters, dummy_arguments)
118
+
119
+ definitions = ast_node.body.definitions
120
+ unless definitions.empty?
121
+ names = []
122
+ procedures = []
123
+ definitions.each { |definition|
124
+ name = identifier(definition.identifier)
125
+ extended_env.define_variable(name, :ev_unassigned)
126
+
127
+ self.eval(definition, extended_env)
128
+
129
+ names << name
130
+ procedures << extended_env.lookup_variable_value(name)
131
+ }
132
+
133
+ target_frame = extended_env.first_frame
134
+ names.zip(procedures).each { |name, procedure|
135
+ target_frame.set(name, procedure)
136
+ }
137
+ end
138
+
139
+ make_procedure(ast_node.formals, ast_node.body.sequence, extended_env)
140
+ end
141
+
142
+ def eval_if(ast_node, env)
143
+ test_result = self.eval(ast_node.test, env)
144
+ if true?(test_result)
145
+ self.eval(ast_node.consequent, env)
146
+ else
147
+ self.eval(ast_node.alternate, env) if ast_node.alternate?
148
+ end
149
+ end
150
+
151
+ def eval_assignment(ast_node, env)
152
+ var = identifier(ast_node.identifier)
153
+ val = self.eval(ast_node.expression, env)
154
+ env.set_variable_value(var, val)
155
+ end
156
+
157
+ def eval_definition(ast_node, env)
158
+ var = identifier(ast_node.identifier)
159
+ val = self.eval(ast_node.expression, env)
160
+ env.define_variable(var, val)
161
+ end
162
+
163
+ def eval_cond(ast_node, env)
164
+ eval_if(cond_to_if(ast_node), env)
165
+ end
166
+
167
+ def eval_and(ast_node, env)
168
+ if empty_node?(ast_node)
169
+ SCM_TRUE
170
+ else
171
+ result = self.eval(first_node(ast_node), env)
172
+ if true?(result)
173
+ if last_node?(ast_node)
174
+ result
175
+ else
176
+ self.eval_and(rest_nodes(ast_node), env)
177
+ end
178
+ else
179
+ SCM_FALSE
180
+ end
181
+ end
182
+ end
183
+
184
+ def eval_or(ast_node, env)
185
+ if empty_node?(ast_node)
186
+ SCM_FALSE
187
+ else
188
+ result = self.eval(first_node(ast_node), env)
189
+ if true?(result)
190
+ result
191
+ else
192
+ if last_node?(ast_node)
193
+ SCM_FALSE
194
+ else
195
+ self.eval_or(rest_nodes(ast_node), env)
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ def eval_when(ast_node, env)
202
+ test_result = self.eval(ast_node.test, env)
203
+ if true?(test_result)
204
+ self.eval_sequence(ast_node.sequence, env)
205
+ end
206
+ end
207
+
208
+ def eval_unless(ast_node, env)
209
+ test_result = self.eval(ast_node.test, env)
210
+ unless true?(test_result)
211
+ self.eval_sequence(ast_node.sequence, env)
212
+ end
213
+ end
214
+
215
+ def eval_let(ast_node, env)
216
+ combination = let_to_combination(ast_node)
217
+
218
+ # named let
219
+ if ast_node.identifier
220
+ name = identifier(ast_node.identifier)
221
+ extended_env = env.extend([name], [:ev_unassigned])
222
+ procedure = self.eval(combination.operator, extended_env)
223
+ extended_env.set_variable_value(name, procedure)
224
+ env = extended_env
225
+ combination.operator = make_identifier(name)
226
+ end
227
+
228
+ self.eval(combination, env)
229
+ end
230
+
231
+ def eval_let_star(ast_node, env)
232
+ nested_lets = let_star_to_nested_lets(ast_node.bindings,
233
+ ast_node.body)
234
+ self.eval(nested_lets, env)
235
+ end
236
+
237
+ def eval_letrec(ast_node, env)
238
+ combination = let_to_combination(ast_node)
239
+
240
+ params = parameters(combination.operator.formals)
241
+ ext_env = env.extend(params, Array.new(params.size, :ev_unassigned))
242
+
243
+ target_frame = ext_env.first_frame
244
+
245
+ operands = combination.operands.map{|e| self.eval(e, ext_env)}
246
+ params.zip(operands).each { |parameter, arg|
247
+ target_frame.set(parameter, arg)
248
+ }
249
+
250
+ self.eval(combination, ext_env)
251
+ end
252
+
253
+ def eval_letrec_star(ast_node, env)
254
+ combination = let_to_combination(ast_node)
255
+
256
+ params = parameters(combination.operator.formals)
257
+ ext_env = env.extend(params, Array.new(params.size, :ev_unassigned))
258
+
259
+ target_frame = ext_env.first_frame
260
+
261
+ params.zip(combination.operands).each { |parameter, operand|
262
+ arg = self.eval(operand, ext_env)
263
+ target_frame.set(parameter, arg)
264
+ }
265
+
266
+ self.eval(combination, ext_env)
267
+ end
268
+
269
+ def eval_begin(ast_node, env)
270
+ eval_sequence(ast_node.sequence, env)
271
+ end
272
+
273
+ def eval_sequence(ast_node, env)
274
+ value = nil
275
+ ast_node.each{|exp| value = self.eval(exp, env)}
276
+ value
277
+ end
278
+
279
+ EV_DO_LOOP_NAME = "ev_do_loop"
280
+
281
+ def eval_do(ast_node, env)
282
+ let_node = do_to_named_let(ast_node, EV_DO_LOOP_NAME)
283
+ self.eval(let_node, env)
284
+ end
285
+
286
+ def text_of_quotation(ast_node)
287
+ "not implemented yet"
288
+ end
289
+
290
+ def true?(obj)
291
+ case obj
292
+ when Scmo::Object
293
+ obj.to_rb == false ? false : true
294
+ when Rubasteme::AST::BooleanNode
295
+ self.eval_boolean(obj, nil)
296
+ when FalseClass, NilClass
297
+ false
298
+ when TrueClass
299
+ true
300
+ else
301
+ true
302
+ end
303
+ end
304
+
305
+ def make_procedure(parameters, seq, env)
306
+ Procedure.make_procedure(parameters, seq, env)
307
+ end
308
+
309
+ def list_of_values(ast_nodes, env)
310
+ ast_nodes.map{|node| self.eval(node, env)}
311
+ end
312
+
313
+ def apply_compound_procedure(procedure, arguments)
314
+ env = procedure.env
315
+ procedure_parameters(procedure).zip(arguments).each { |var, val|
316
+ env.set_variable_value(var, val)
317
+ }
318
+ eval_sequence(procedure_body(procedure), env)
319
+ end
320
+
321
+ def procedure_parameters(procedure)
322
+ procedure.parameters.map{|node| identifier(node)}
323
+ end
324
+
325
+ def procedure_body(procedure)
326
+ procedure.body
327
+ end
328
+
329
+ end
330
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbsiev
4
+
5
+ PRIMITIVE_NAMES_MAP = {
6
+ # Some primitive procedure names in Scheme must be defined with
7
+ # different names in Ruby.
8
+ "+" => :add,
9
+ "-" => :subtract,
10
+ "*" => :mul,
11
+ "/" => :div,
12
+ "%" => :mod,
13
+ "<" => :lt?,
14
+ "<=" => :le?,
15
+ ">" => :gt?,
16
+ ">=" => :ge?,
17
+ "=" => :same_value?,
18
+ }
19
+
20
+ module Primitives
21
+
22
+ require_relative "primitives/empty_list"
23
+ require_relative "primitives/comparison"
24
+ require_relative "primitives/arithmetic"
25
+
26
+ include EmptyList
27
+ include Comparison
28
+ include Arithmetic
29
+
30
+ def cons(scm_obj1, scm_obj2)
31
+ [scm_obj1, scm_obj2]
32
+ end
33
+
34
+ def pair?(scm_obj)
35
+ scm_obj.instance_of?(Array)
36
+ end
37
+
38
+ def list?(scm_obj)
39
+ scm_obj.instance_of?(Array)
40
+ end
41
+
42
+ def car(scm_list)
43
+ scm_list[0]
44
+ end
45
+
46
+ def cdr(scm_list)
47
+ scm_list[1..-1]
48
+ end
49
+
50
+ def list(*scm_objs)
51
+ scm_objs
52
+ end
53
+
54
+ def append(*scm_lists)
55
+ if scm_lists.empty?
56
+ SCM_EMPTY_LIST
57
+ else
58
+ scm_lists[0] + append(*scm_lists[1..-1])
59
+ end
60
+ end
61
+
62
+ def write(scm_obj)
63
+ print scm_obj
64
+ end
65
+
66
+ def display(scm_obj)
67
+ write(scm_obj)
68
+ print "\n"
69
+ end
70
+
71
+ def number?(scm_obj)
72
+ scm_obj.kind_of?(Numeric) ? SCM_TRUE : SCM_FALSE
73
+ end
74
+
75
+ # :stopdoc:
76
+
77
+ # Registers primitive procedure names into the name map, those
78
+ # names are identical in Scheme and Ruby.
79
+ instance_methods(true).each { |sym|
80
+ name = sym.to_s
81
+ unless PRIMITIVE_NAMES_MAP.key?(name)
82
+ PRIMITIVE_NAMES_MAP[name] = sym
83
+ end
84
+ }
85
+ # :startdoc:
86
+ end
87
+
88
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbsiev
4
+
5
+ module Primitives
6
+
7
+ module Arithmetic
8
+
9
+ def zero?(obj)
10
+ if number?(obj) == SCM_TRUE
11
+ same_value?(obj, 0)
12
+ else
13
+ SCM_FALSE
14
+ end
15
+ end
16
+
17
+ def add(*args)
18
+ a_calc(:+, *args)
19
+ end
20
+
21
+ def subtract(*args)
22
+ a_calc(:-, *args)
23
+ end
24
+
25
+ def mul(*args)
26
+ a_calc(:*, *args)
27
+ end
28
+
29
+ def div(*args)
30
+ a_calc(:/, *args)
31
+ end
32
+
33
+ def mod(*args)
34
+ a_calc(:%, *args)
35
+ end
36
+
37
+ private
38
+
39
+ def a_calc(op, *args)
40
+ case args.size
41
+ when 0
42
+ 0
43
+ when 1
44
+ args[0]
45
+ else
46
+ a_calc(op, args[0].send(op, args[1]), *args[2..-1])
47
+ end
48
+ end
49
+
50
+ end # end of Arithmetic
51
+
52
+ end # end of Primitives
53
+
54
+ end