rbsiev 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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