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.
- 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
|