rbsiev 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +18 -0
- data/.gitignore +57 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +10 -0
- data/LICENSE +21 -0
- data/README.md +90 -0
- data/Rakefile +20 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/exe/rbsiev +51 -0
- data/lib/rbsiev.rb +98 -0
- data/lib/rbsiev/environment.rb +110 -0
- data/lib/rbsiev/error.rb +5 -0
- data/lib/rbsiev/evaluator.rb +330 -0
- data/lib/rbsiev/primitives.rb +88 -0
- data/lib/rbsiev/primitives/arithmetic.rb +54 -0
- data/lib/rbsiev/primitives/comparison.rb +46 -0
- data/lib/rbsiev/primitives/empty_list.rb +15 -0
- data/lib/rbsiev/printer.rb +19 -0
- data/lib/rbsiev/procedure.rb +47 -0
- data/lib/rbsiev/repl.rb +112 -0
- data/lib/rbsiev/version.rb +6 -0
- data/lib/rubasteme/ast/misc.rb +313 -0
- data/lib/scmo/object.rb +117 -0
- data/rbsiev.gemspec +31 -0
- metadata +100 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rbsiev
|
4
|
+
|
5
|
+
module Primitives
|
6
|
+
|
7
|
+
module Comparison
|
8
|
+
|
9
|
+
def lt?(*args)
|
10
|
+
c_calc(:<, *args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def le?(*args)
|
14
|
+
c_calc(:<=, *args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def gt?(*args)
|
18
|
+
c_calc(:>, *args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def ge?(*args)
|
22
|
+
c_calc(:>=, *args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def same_value?(*args)
|
26
|
+
c_calc(:==, *args)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def c_calc(op, *args)
|
32
|
+
case args.size
|
33
|
+
when 0, 1
|
34
|
+
raise Error, "Too few arguments: got=%s" % scm_objs
|
35
|
+
when 2
|
36
|
+
args[0].send(op, args[1]) ? SCM_TRUE : SCM_FALSE
|
37
|
+
else
|
38
|
+
args[0].send(op, args[1]) and c_calc(op, *args[1..-1]) ? SCM_TRUE : SCM_FALSE
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end # end of Comparison
|
43
|
+
|
44
|
+
end # end of Primitives
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rbsiev
|
4
|
+
class Procedure
|
5
|
+
def self.make_procedure(parameters, body, env)
|
6
|
+
if body.instance_of?(Symbol)
|
7
|
+
PrimitiveProcedure.new(nil, body, env)
|
8
|
+
else
|
9
|
+
CompoundProcedure.new(parameters, body, env)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def parameters(bare: false)
|
14
|
+
if bare
|
15
|
+
@parameters.map(&:literal)
|
16
|
+
else
|
17
|
+
@parameters
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :body
|
22
|
+
attr_reader :env
|
23
|
+
|
24
|
+
def type; nil; end
|
25
|
+
def apply(arguments); end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def initialize(parameters, body, env)
|
30
|
+
@parameters = parameters
|
31
|
+
@body = body
|
32
|
+
@env = env
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class PrimitiveProcedure < Procedure
|
37
|
+
def type; :procedure_primitive; end
|
38
|
+
def apply(arguments)
|
39
|
+
@env.send(@body, *arguments)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class CompoundProcedure < Procedure
|
44
|
+
def type; :procedure_compound; end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/lib/rbsiev/repl.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "readline"
|
4
|
+
|
5
|
+
module Rbsiev
|
6
|
+
|
7
|
+
class Repl
|
8
|
+
def self.start(prompt: "REPL> ", verbose: false)
|
9
|
+
msg = Repl.new(prompt: prompt, verbose: verbose).loop
|
10
|
+
puts msg if msg
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(prompt:, verbose: false)
|
14
|
+
@verbose = verbose
|
15
|
+
@prompt = prompt
|
16
|
+
@components = init_components
|
17
|
+
end
|
18
|
+
|
19
|
+
def loop
|
20
|
+
second_prompt = "." * (@prompt.length - 1) + " "
|
21
|
+
|
22
|
+
msg = Kernel.loop {
|
23
|
+
begin
|
24
|
+
source = read_source(second_prompt)
|
25
|
+
rescue EOFError => _
|
26
|
+
break "Bye!"
|
27
|
+
else
|
28
|
+
next if source.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
@components.exec(source)
|
33
|
+
rescue Error => e
|
34
|
+
puts e.message
|
35
|
+
next
|
36
|
+
end
|
37
|
+
}
|
38
|
+
msg
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def init_components
|
44
|
+
parser = Rubasteme.parser
|
45
|
+
evaluator = Evaluator.new
|
46
|
+
env = Rbsiev.setup_environment
|
47
|
+
printer = Printer.new
|
48
|
+
|
49
|
+
evaluator.verbose = @verbose
|
50
|
+
printer.verbose = @verbose
|
51
|
+
|
52
|
+
components = Components.new(parser, evaluator, printer, env)
|
53
|
+
|
54
|
+
func_map = {
|
55
|
+
load: lambda{|file| components.load(file)},
|
56
|
+
version: lambda{components.version},
|
57
|
+
}
|
58
|
+
|
59
|
+
procedures = []
|
60
|
+
func_map.each { |sym, func|
|
61
|
+
env.define_singleton_method(sym, func)
|
62
|
+
procedures << Procedure.make_procedure(nil, sym, env)
|
63
|
+
}
|
64
|
+
env = env.extend(func_map.keys.map(&:to_s), procedures)
|
65
|
+
components.env = env
|
66
|
+
|
67
|
+
components
|
68
|
+
end
|
69
|
+
|
70
|
+
def read_source(second_prompt = ">> ")
|
71
|
+
source = Readline::readline(@prompt, true)
|
72
|
+
raise EOFError if source.nil?
|
73
|
+
|
74
|
+
until match_parenthesis(source)
|
75
|
+
more_source = Readline::readline(second_prompt, true)
|
76
|
+
if more_source.nil?
|
77
|
+
source = nil
|
78
|
+
break
|
79
|
+
else
|
80
|
+
source += (more_source + " ")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
source
|
84
|
+
end
|
85
|
+
|
86
|
+
def match_parenthesis(str)
|
87
|
+
count = count_characters(str, ["(", ")"])
|
88
|
+
count["("] == count[")"]
|
89
|
+
end
|
90
|
+
|
91
|
+
def count_characters(str, chars)
|
92
|
+
count = chars.to_h{|ch| [ch, 0]}
|
93
|
+
escaped = false
|
94
|
+
in_string = false
|
95
|
+
str.each_char { |rune|
|
96
|
+
case rune
|
97
|
+
when "\\"
|
98
|
+
escaped = !escaped if in_string
|
99
|
+
when '"'
|
100
|
+
in_string = !in_string unless escaped
|
101
|
+
escaped = false
|
102
|
+
when *chars
|
103
|
+
count[rune] += 1 unless in_string
|
104
|
+
else
|
105
|
+
escaped = false
|
106
|
+
end
|
107
|
+
}
|
108
|
+
count
|
109
|
+
end
|
110
|
+
|
111
|
+
end # end of Repl
|
112
|
+
end
|
@@ -0,0 +1,313 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubasteme
|
4
|
+
module AST
|
5
|
+
|
6
|
+
# Miscellaneous functions to operate AST nodes.
|
7
|
+
|
8
|
+
module Misc
|
9
|
+
|
10
|
+
# For AST::IdentifierNode.
|
11
|
+
|
12
|
+
def identifier(ast_node)
|
13
|
+
if ast_node.respond_to?(:literal)
|
14
|
+
ast_node.literal
|
15
|
+
else
|
16
|
+
raise Error, "wrong type node: got=%s" % ast_node.to_a.to_s
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# For AST::ListNode. They also accept an Array object as their
|
21
|
+
# arugment.
|
22
|
+
|
23
|
+
def empty_node?(obj)
|
24
|
+
check_list_type(obj)
|
25
|
+
obj.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def last_node?(obj)
|
29
|
+
check_list_type(obj)
|
30
|
+
obj.size == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def first_node(obj)
|
34
|
+
check_list_type(obj)
|
35
|
+
obj[0]
|
36
|
+
end
|
37
|
+
|
38
|
+
def rest_nodes(obj)
|
39
|
+
check_list_type(obj)
|
40
|
+
obj[1..-1]
|
41
|
+
end
|
42
|
+
|
43
|
+
def list_elements(obj)
|
44
|
+
case obj
|
45
|
+
when ListNode
|
46
|
+
obj.elements
|
47
|
+
when Array
|
48
|
+
obj
|
49
|
+
else
|
50
|
+
raise Error, "wrong type node: expected=(Array or ListNode), got=%s" % obj.to_a.to_s
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# for AST::FormalsNode
|
55
|
+
|
56
|
+
def parameters(ast_node)
|
57
|
+
check_type(:ast_formals, ast_node)
|
58
|
+
ast_node.map{|e| identifier(e)}
|
59
|
+
end
|
60
|
+
|
61
|
+
# For AST::BindingsNode
|
62
|
+
|
63
|
+
def last_binding?(ast_node)
|
64
|
+
last_node?(list_elements(ast_node))
|
65
|
+
end
|
66
|
+
|
67
|
+
def first_binding(ast_node)
|
68
|
+
first_node(ast_node)
|
69
|
+
end
|
70
|
+
|
71
|
+
def rest_bindings(ast_node)
|
72
|
+
make_bindings(rest_nodes(ast_node))
|
73
|
+
end
|
74
|
+
|
75
|
+
def bindings_specs(ast_node)
|
76
|
+
check_type(:ast_bindings, ast_node)
|
77
|
+
ast_node.elements
|
78
|
+
end
|
79
|
+
|
80
|
+
# Converter function.
|
81
|
+
|
82
|
+
def cond_to_if(ast_node)
|
83
|
+
check_type(:ast_cond, ast_node)
|
84
|
+
expand_clauses(ast_node.cond_clauses)
|
85
|
+
end
|
86
|
+
|
87
|
+
def sequence_to_node(ast_node)
|
88
|
+
check_type(:ast_sequence, ast_node)
|
89
|
+
if ast_node.empty?
|
90
|
+
EMPTY_LIST
|
91
|
+
elsif last_node?(ast_node)
|
92
|
+
first_node(ast_node)
|
93
|
+
else
|
94
|
+
make_begin(ast_node)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def let_to_combination(ast_node)
|
99
|
+
check_types([:ast_let, :ast_letrec, :ast_letrec_star], ast_node)
|
100
|
+
|
101
|
+
identifiers = []
|
102
|
+
operands = []
|
103
|
+
|
104
|
+
ast_node.bindings.each { |bind_spec|
|
105
|
+
identifiers << bind_spec.identifier
|
106
|
+
operands << bind_spec.expression
|
107
|
+
}
|
108
|
+
|
109
|
+
formals = make_formals(identifiers)
|
110
|
+
operator = make_lambda(formals, ast_node.body)
|
111
|
+
|
112
|
+
make_combination(operator, operands)
|
113
|
+
end
|
114
|
+
|
115
|
+
def let_star_to_nested_lets(bindings, body)
|
116
|
+
single_bindings = make_bindings([first_binding(bindings)])
|
117
|
+
if last_binding?(bindings)
|
118
|
+
make_let(single_bindings, body)
|
119
|
+
else
|
120
|
+
seq = make_sequence([let_star_to_nested_lets(rest_bindings(bindings), body)])
|
121
|
+
new_body = make_body(nil, seq)
|
122
|
+
make_let(single_bindings, new_body)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def do_to_named_let(ast_node, loop_identifier)
|
127
|
+
bind_specs = []
|
128
|
+
bind_specs_with_step = []
|
129
|
+
ast_node.iteration_bindings.each { |iter_spec|
|
130
|
+
spec = make_bind_spec(iter_spec.identifier, iter_spec.init)
|
131
|
+
if iter_spec.step
|
132
|
+
bind_specs_with_step << [spec, iter_spec.step]
|
133
|
+
else
|
134
|
+
bind_specs << spec
|
135
|
+
end
|
136
|
+
}
|
137
|
+
|
138
|
+
let_bindings = make_bindings(bind_specs_with_step.map{|e| e[0]})
|
139
|
+
|
140
|
+
test_and_do_result = ast_node.test_and_do_result
|
141
|
+
|
142
|
+
if_predicate = test_and_do_result.test
|
143
|
+
if_consequent = make_begin(rest_nodes(test_and_do_result))
|
144
|
+
|
145
|
+
sequence = []
|
146
|
+
sequence += ast_node.commands
|
147
|
+
sequence << make_combination(make_identifier(loop_identifier),
|
148
|
+
bind_specs_with_step.map{|e| e[1]})
|
149
|
+
if_alternative = make_begin(sequence)
|
150
|
+
|
151
|
+
if_node = make_if(if_predicate, if_consequent, if_alternative)
|
152
|
+
let_body = make_body(nil, make_sequence([if_node]))
|
153
|
+
let_node = make_let(let_bindings, let_body)
|
154
|
+
let_node.identifier = make_identifier(loop_identifier)
|
155
|
+
|
156
|
+
if bind_specs.empty?
|
157
|
+
let_node
|
158
|
+
else
|
159
|
+
let_body = make_body(nil, make_sequence([let_node]))
|
160
|
+
make_let(make_bindings(bind_specs), let_body)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Construcing functions.
|
165
|
+
|
166
|
+
def make_identifier(literal)
|
167
|
+
Rubasteme::AST::instantiate(:ast_identifier, literal)
|
168
|
+
end
|
169
|
+
|
170
|
+
def make_lambda(formals, body)
|
171
|
+
check_type(:ast_body, body)
|
172
|
+
lambda_node = Rubasteme::AST.instantiate(:ast_lambda_expression, nil)
|
173
|
+
lambda_node.formals = formals
|
174
|
+
lambda_node.body = body
|
175
|
+
lambda_node
|
176
|
+
end
|
177
|
+
|
178
|
+
def make_begin(nodes)
|
179
|
+
begin_node = Rubasteme::AST.instantiate(:ast_begin, nil)
|
180
|
+
case nodes
|
181
|
+
when Rubasteme::AST::SequenceNode
|
182
|
+
begin_node.sequence = nodes
|
183
|
+
when Array
|
184
|
+
begin_node.sequence = make_sequence(nodes)
|
185
|
+
else
|
186
|
+
raise Error, "wrong type argument: expected= Array or Rubasteme::AST::SequenceNode, got=%s" % nodes.class
|
187
|
+
end
|
188
|
+
begin_node
|
189
|
+
end
|
190
|
+
|
191
|
+
def make_body(definitions, sequence)
|
192
|
+
body_node = Rubasteme::AST.instantiate(:ast_body, nil)
|
193
|
+
|
194
|
+
if definitions.nil?
|
195
|
+
definitions = Rubasteme::AST.instantiate(:ast_internal_definitions, nil)
|
196
|
+
end
|
197
|
+
if sequence.nil?
|
198
|
+
sequence = Rubasteme::ASt.instantiate(:ast_sequence, nil)
|
199
|
+
end
|
200
|
+
|
201
|
+
body_node.definitions = definitions
|
202
|
+
body_node.sequence = sequence
|
203
|
+
|
204
|
+
body_node
|
205
|
+
end
|
206
|
+
|
207
|
+
def make_sequence(nodes)
|
208
|
+
check_list_type(nodes)
|
209
|
+
seq_node = Rubasteme::AST.instantiate(:ast_sequence, nil)
|
210
|
+
nodes.each{|exp| seq_node.add_expression(exp)}
|
211
|
+
seq_node
|
212
|
+
end
|
213
|
+
|
214
|
+
def make_if(predicate, consequent, alternative)
|
215
|
+
if_node = Rubasteme::AST.instantiate(:ast_conditional, nil)
|
216
|
+
if_node.test = predicate
|
217
|
+
if_node.consequent = consequent
|
218
|
+
if_node.alternate = alternative if alternative
|
219
|
+
if_node
|
220
|
+
end
|
221
|
+
|
222
|
+
def make_formals(identifiers)
|
223
|
+
formals_node = Rubasteme::AST.instantiate(:ast_formals, nil)
|
224
|
+
identifiers.each { |identifier_node|
|
225
|
+
check_type(:ast_identifier, identifier_node)
|
226
|
+
formals_node.add_identifier(identifier_node)
|
227
|
+
}
|
228
|
+
formals_node
|
229
|
+
end
|
230
|
+
|
231
|
+
def make_combination(operator, operands)
|
232
|
+
proc_call_node = Rubasteme::AST.instantiate(:ast_procedure_call, nil)
|
233
|
+
proc_call_node.operator = operator
|
234
|
+
operands.each { |node|
|
235
|
+
proc_call_node.add_operand(node)
|
236
|
+
}
|
237
|
+
proc_call_node
|
238
|
+
end
|
239
|
+
|
240
|
+
def make_let(bindings, body)
|
241
|
+
check_type(:ast_bindings, bindings)
|
242
|
+
check_type(:ast_body, body)
|
243
|
+
let_node = Rubasteme::AST.instantiate(:ast_let, nil)
|
244
|
+
let_node.bindings = bindings
|
245
|
+
let_node.body = body
|
246
|
+
let_node
|
247
|
+
end
|
248
|
+
|
249
|
+
def make_bind_spec(identifier, expression)
|
250
|
+
spec_node = Rubasteme::AST.instantiate(:ast_bind_spec, nil)
|
251
|
+
spec_node.identifier = identifier
|
252
|
+
spec_node.expression = expression
|
253
|
+
spec_node
|
254
|
+
end
|
255
|
+
|
256
|
+
def make_bindings(bind_spec_nodes)
|
257
|
+
bindings_node = Rubasteme::AST.instantiate(:ast_bindings, nil)
|
258
|
+
bind_spec_nodes.each { |spec|
|
259
|
+
check_type(:ast_bind_spec, spec)
|
260
|
+
bindings_node.add_bind_spec(spec)
|
261
|
+
}
|
262
|
+
bindings_node
|
263
|
+
end
|
264
|
+
|
265
|
+
private
|
266
|
+
|
267
|
+
def check_list_type(obj)
|
268
|
+
obj.kind_of?(ListNode) || obj.kind_of?(Array)
|
269
|
+
end
|
270
|
+
|
271
|
+
def check_type(ast_type, ast_node)
|
272
|
+
if ast_node.type != ast_type
|
273
|
+
raise Error,
|
274
|
+
"wrong type node: expected=%s, got=%s" % [ast_type, ast_node.to_a.to_s]
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def check_types(types, ast_node)
|
279
|
+
unless types.include?(ast_node.type)
|
280
|
+
raise Error,
|
281
|
+
"wrong type node: expected=%s, got=%s" % [ast_type, ast_node.to_a.to_s]
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def expand_clauses(clauses)
|
286
|
+
# clauses must be an Array which holds CondClauseNode.
|
287
|
+
if clauses.empty?
|
288
|
+
SCM_FALSE
|
289
|
+
else
|
290
|
+
first = first_node(clauses)
|
291
|
+
rest = rest_nodes(clauses)
|
292
|
+
if cond_else_clause?(first)
|
293
|
+
if rest.empty?
|
294
|
+
sequence_to_node(first.sequence)
|
295
|
+
else
|
296
|
+
raise Error,
|
297
|
+
"ELSE clause isn't last: got=%s" % clauses.to_s
|
298
|
+
end
|
299
|
+
else
|
300
|
+
make_if(first.test,
|
301
|
+
sequence_to_node(first.sequence),
|
302
|
+
expand_clauses(rest))
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def cond_else_clause?(clause_node)
|
308
|
+
clause_node.type == :ast_else_clause
|
309
|
+
end
|
310
|
+
|
311
|
+
end # end of Misc
|
312
|
+
end # end of AST
|
313
|
+
end
|