rubasteme 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 +136 -0
- data/Rakefile +20 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/examples/mini_rus3 +193 -0
- data/examples/mini_sicp_scheme +62 -0
- data/examples/sicp_scheme/environment.rb +103 -0
- data/examples/sicp_scheme/error.rb +5 -0
- data/examples/sicp_scheme/evaluator.rb +312 -0
- data/examples/sicp_scheme/primitives.rb +123 -0
- data/examples/sicp_scheme/printer.rb +12 -0
- data/exe/rubasteme +88 -0
- data/lib/rubasteme.rb +16 -0
- data/lib/rubasteme/ast.rb +93 -0
- data/lib/rubasteme/ast/branch_node.rb +514 -0
- data/lib/rubasteme/ast/leaf_node.rb +62 -0
- data/lib/rubasteme/error.rb +50 -0
- data/lib/rubasteme/parser.rb +608 -0
- data/lib/rubasteme/utils.rb +14 -0
- data/lib/rubasteme/version.rb +6 -0
- data/rubasteme.gemspec +31 -0
- metadata +88 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SicpScheme
|
4
|
+
|
5
|
+
# primitive procedure for SICP Scheme
|
6
|
+
|
7
|
+
module Primitives
|
8
|
+
def cons(obj1, obj2)
|
9
|
+
[obj1, obj2]
|
10
|
+
end
|
11
|
+
|
12
|
+
def pair?(obj)
|
13
|
+
obj.instance_of?(Array)
|
14
|
+
end
|
15
|
+
|
16
|
+
def car(lis)
|
17
|
+
lis[0]
|
18
|
+
end
|
19
|
+
|
20
|
+
def cdr(lis)
|
21
|
+
lis[1..-1]
|
22
|
+
end
|
23
|
+
|
24
|
+
def list(*args)
|
25
|
+
args
|
26
|
+
end
|
27
|
+
|
28
|
+
def append(*args)
|
29
|
+
if args.empty?
|
30
|
+
[]
|
31
|
+
else
|
32
|
+
args[0] + append(*args[1..-1])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def write(obj)
|
37
|
+
print obj
|
38
|
+
end
|
39
|
+
|
40
|
+
def display(obj)
|
41
|
+
write(obj)
|
42
|
+
print "\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
def zero?(obj)
|
46
|
+
eqv?(obj, 0)
|
47
|
+
end
|
48
|
+
|
49
|
+
def a_calc(op, *args)
|
50
|
+
case args.size
|
51
|
+
when 0
|
52
|
+
0
|
53
|
+
when 1
|
54
|
+
args[0]
|
55
|
+
else
|
56
|
+
a_calc(op, args[0].send(op, args[1]), *args[2..-1])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def add(*args)
|
61
|
+
a_calc(:+, *args)
|
62
|
+
end
|
63
|
+
|
64
|
+
def subtract(*args)
|
65
|
+
a_calc(:-, *args)
|
66
|
+
end
|
67
|
+
|
68
|
+
def mul(*args)
|
69
|
+
a_calc(:*, *args)
|
70
|
+
end
|
71
|
+
|
72
|
+
def div(*args)
|
73
|
+
a_calc(:/, *args)
|
74
|
+
end
|
75
|
+
|
76
|
+
def mod(*args)
|
77
|
+
a_calc(:%, *args)
|
78
|
+
end
|
79
|
+
|
80
|
+
def scm_true
|
81
|
+
[:ast_boolean, "#t"]
|
82
|
+
end
|
83
|
+
|
84
|
+
def scm_false
|
85
|
+
[:ast_boolean, "#f"]
|
86
|
+
end
|
87
|
+
|
88
|
+
def c_calc(op, *args)
|
89
|
+
case args.size
|
90
|
+
when 0, 1
|
91
|
+
raise ArgumentError, args.to_s
|
92
|
+
when 2
|
93
|
+
args[0].send(op, args[1]) ? scm_true : scm_false
|
94
|
+
else
|
95
|
+
args[0].send(op, args[1]) and c_calc(op, *args[1..-1]) ? scm_true : scm_false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def lt?(*args)
|
100
|
+
c_calc(:<, *args)
|
101
|
+
end
|
102
|
+
|
103
|
+
def le?(*args)
|
104
|
+
c_calc(:<=, *args)
|
105
|
+
end
|
106
|
+
|
107
|
+
def gt?(*args)
|
108
|
+
c_calc(:>, *args)
|
109
|
+
end
|
110
|
+
|
111
|
+
def ge?(*args)
|
112
|
+
c_calc(:>=, *args)
|
113
|
+
end
|
114
|
+
|
115
|
+
def same_value?(*args)
|
116
|
+
c_calc(:==, *args)
|
117
|
+
end
|
118
|
+
|
119
|
+
def eqv?(obj1, obj2)
|
120
|
+
obj1 == obj2 ? scm_true : scm_false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/exe/rubasteme
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "pp"
|
4
|
+
require "rubasteme"
|
5
|
+
|
6
|
+
def version
|
7
|
+
"Rubasteme version: #{Rubasteme::VERSION} (#{Rubasteme::RELEASE})"
|
8
|
+
end
|
9
|
+
|
10
|
+
def usage
|
11
|
+
puts <<HELP
|
12
|
+
usage:
|
13
|
+
rubasteme [option] [FILE]
|
14
|
+
|
15
|
+
Reads the given files as a sequence of tokens, then print AST nodes.
|
16
|
+
If no FILE is specified, then tries to read from the standard input.
|
17
|
+
|
18
|
+
option:
|
19
|
+
-o, --output-file OUTPUT_FILE : specify the output file
|
20
|
+
-t, --format-type TYPE : specify the output format
|
21
|
+
-d, --debug : specify to run verbosely
|
22
|
+
-v, --version : print version
|
23
|
+
-h, --help : show this message
|
24
|
+
|
25
|
+
input format:
|
26
|
+
Specify one of the following names:
|
27
|
+
ast, array
|
28
|
+
HELP
|
29
|
+
end
|
30
|
+
|
31
|
+
def opt_parse(args, opts = {})
|
32
|
+
files = []
|
33
|
+
while args.size > 0
|
34
|
+
arg = args.shift
|
35
|
+
case arg
|
36
|
+
when "-o", "--output-file"
|
37
|
+
opts[:output_file] = args.shift
|
38
|
+
when "-t", "--format-type"
|
39
|
+
format_type = args.shift
|
40
|
+
raise ArgumentError, "not specified as format type" if format_type.nil?
|
41
|
+
opts[:format_type] = format_type.intern
|
42
|
+
when "-d", "--debug"
|
43
|
+
opts[:verbose] = true
|
44
|
+
when "-v", "--version"
|
45
|
+
puts version
|
46
|
+
exit 0
|
47
|
+
when "-h", "--help"
|
48
|
+
puts usage
|
49
|
+
exit 0
|
50
|
+
else # must be a filename
|
51
|
+
files << arg if arg
|
52
|
+
end
|
53
|
+
end
|
54
|
+
args.concat(files)
|
55
|
+
opts
|
56
|
+
end
|
57
|
+
|
58
|
+
opts = opt_parse(ARGV)
|
59
|
+
if opts[:verbose]
|
60
|
+
puts version
|
61
|
+
format = opts[:format_type] || "default (token)"
|
62
|
+
puts "Input format: #{format}"
|
63
|
+
ofname = opts[:output_file] || "STDOUT"
|
64
|
+
puts "Output file: #{ofname}"
|
65
|
+
end
|
66
|
+
|
67
|
+
lines = []
|
68
|
+
if ARGV.size > 0
|
69
|
+
lines = ARGF.entries.map{|s| s.delete_suffix("\n")}
|
70
|
+
else
|
71
|
+
lines = STDIN.readlines(chomp: true)
|
72
|
+
end
|
73
|
+
|
74
|
+
tokens = lines.map{|e| Kernel.eval(e)}
|
75
|
+
lexer = Rubasteme.lexer(tokens)
|
76
|
+
parser = Rubasteme.parser(lexer)
|
77
|
+
ast = parser.parse
|
78
|
+
|
79
|
+
output = ""
|
80
|
+
PP.pp(ast.to_a, output)
|
81
|
+
|
82
|
+
form = opts[:format_type] || :token
|
83
|
+
of = STDOUT
|
84
|
+
of = File.open(opts[:output_file], "w") if opts[:output_file]
|
85
|
+
|
86
|
+
of.puts(output)
|
87
|
+
|
88
|
+
of.close
|
data/lib/rubasteme.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rbscmlex"
|
4
|
+
|
5
|
+
module Rubasteme
|
6
|
+
|
7
|
+
def self.lexer(obj)
|
8
|
+
Rbscmlex::Lexer.new(obj)
|
9
|
+
end
|
10
|
+
|
11
|
+
require_relative "rubasteme/error"
|
12
|
+
require_relative "rubasteme/utils"
|
13
|
+
require_relative "rubasteme/ast"
|
14
|
+
require_relative "rubasteme/parser"
|
15
|
+
require_relative "rubasteme/version"
|
16
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubasteme
|
4
|
+
|
5
|
+
def self.write(ast_node, of = STDOUT)
|
6
|
+
of.puts ast_node.to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
module AST
|
10
|
+
|
11
|
+
AST_NODE_TYPE = [ # :nodoc:
|
12
|
+
# leaf
|
13
|
+
:ast_empty_list,
|
14
|
+
:ast_boolean,
|
15
|
+
:ast_identifier,
|
16
|
+
:ast_character,
|
17
|
+
:ast_string,
|
18
|
+
:ast_number,
|
19
|
+
:ast_dot,
|
20
|
+
# branch
|
21
|
+
:ast_program,
|
22
|
+
:ast_list,
|
23
|
+
:ast_quotation,
|
24
|
+
:ast_procedure_call,
|
25
|
+
:ast_lambda_expression,
|
26
|
+
:ast_formals,
|
27
|
+
:ast_conditional,
|
28
|
+
:ast_assignment,
|
29
|
+
:ast_identifier_definition,
|
30
|
+
:ast_cond,
|
31
|
+
:ast_cond_clause,
|
32
|
+
:ast_case,
|
33
|
+
:ast_and,
|
34
|
+
:ast_or,
|
35
|
+
:ast_when,
|
36
|
+
:ast_unless,
|
37
|
+
:ast_let,
|
38
|
+
:ast_let_star,
|
39
|
+
:ast_letrec,
|
40
|
+
:ast_letrec_star,
|
41
|
+
:ast_bindings,
|
42
|
+
:ast_bind_spec,
|
43
|
+
:ast_begin,
|
44
|
+
:ast_do,
|
45
|
+
:ast_iteration_bindings,
|
46
|
+
:ast_test_and_do_result,
|
47
|
+
:ast_iteration_spec,
|
48
|
+
# misc.
|
49
|
+
:ast_illegal,
|
50
|
+
]
|
51
|
+
|
52
|
+
def self.instantiate(ast_node_type, literal)
|
53
|
+
type_name = Utils.camel_case(ast_node_type.to_s.delete_prefix("ast_"))
|
54
|
+
klass = AST.const_get("#{type_name}Node")
|
55
|
+
|
56
|
+
if klass.nil? or klass == IllegalNode
|
57
|
+
IllegalNode.new(ast_node_type, literal)
|
58
|
+
else
|
59
|
+
klass.new(literal)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Node
|
64
|
+
def initialize(_literal = nil)
|
65
|
+
end
|
66
|
+
|
67
|
+
def type
|
68
|
+
klass_name = self.class.name.split("::")[-1]
|
69
|
+
type_name = Utils.snake_case(klass_name.delete_suffix("Node"))
|
70
|
+
"ast_#{type_name}".intern
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_a; []; end
|
74
|
+
def to_s; to_a.to_s; end
|
75
|
+
end
|
76
|
+
|
77
|
+
require_relative "ast/leaf_node"
|
78
|
+
require_relative "ast/branch_node"
|
79
|
+
|
80
|
+
class IllegalNode < Node
|
81
|
+
def initialize(type, literal)
|
82
|
+
super(literal)
|
83
|
+
@given_type = type
|
84
|
+
@literal = literal
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_a
|
88
|
+
[type, @given_type, @literal]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,514 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubasteme
|
4
|
+
|
5
|
+
module AST
|
6
|
+
|
7
|
+
class BranchNode < Node
|
8
|
+
def initialize(initial_size = nil)
|
9
|
+
super(nil)
|
10
|
+
@nodes = initial_size.nil? ? [] : Array.new(initial_size)
|
11
|
+
end
|
12
|
+
|
13
|
+
def size
|
14
|
+
@nodes.size
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_a
|
18
|
+
[type].concat(@nodes.map(&:to_a))
|
19
|
+
end
|
20
|
+
|
21
|
+
include Enumerable
|
22
|
+
|
23
|
+
def each(&blk)
|
24
|
+
if block_given?
|
25
|
+
@nodes.each(&blk)
|
26
|
+
self
|
27
|
+
else
|
28
|
+
@nodes.each
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def [](index)
|
33
|
+
@nodes[index]
|
34
|
+
end
|
35
|
+
|
36
|
+
def []=(index, node)
|
37
|
+
@nodes[index] = node
|
38
|
+
end
|
39
|
+
|
40
|
+
def <<(node)
|
41
|
+
@nodes << node
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class ProgramNode < BranchNode
|
46
|
+
def initialize(_ = nil)
|
47
|
+
super(nil)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class VectorNode < BranchNode
|
52
|
+
def initialize(_ = nil)
|
53
|
+
super(nil)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class ListNode < BranchNode
|
58
|
+
def initialize(_ = nil)
|
59
|
+
super(nil)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class QuotationNode < ListNode
|
64
|
+
def initialize(_ = nil)
|
65
|
+
super(nil)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class ProcedureCallNode < ListNode
|
70
|
+
def initialize(_ = nil)
|
71
|
+
# @nodes = [<operator>, <operand>*]
|
72
|
+
super(1)
|
73
|
+
end
|
74
|
+
|
75
|
+
def operator
|
76
|
+
@nodes[0]
|
77
|
+
end
|
78
|
+
|
79
|
+
def operator=(node)
|
80
|
+
@nodes[0] = node
|
81
|
+
end
|
82
|
+
|
83
|
+
def operands
|
84
|
+
@nodes[1..-1]
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_operand(node)
|
88
|
+
@nodes << node
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class LambdaExpressionNode < ListNode
|
93
|
+
def initialize(_ = nil)
|
94
|
+
# @nodes = [<formals>, <body>, ...]
|
95
|
+
super(1)
|
96
|
+
end
|
97
|
+
|
98
|
+
def formals
|
99
|
+
@nodes[0]
|
100
|
+
end
|
101
|
+
|
102
|
+
def formals=(node)
|
103
|
+
@nodes[0] = node
|
104
|
+
end
|
105
|
+
|
106
|
+
def body
|
107
|
+
@nodes[1..-1]
|
108
|
+
end
|
109
|
+
|
110
|
+
def body=(nodes)
|
111
|
+
nodes.each_with_index { |node, i|
|
112
|
+
@nodes[i + 1] = node
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class FormalsNode < ListNode
|
118
|
+
def initialize(_ = nil)
|
119
|
+
# @nodes = [<identifier 1>, <identifier 2>, ... ]
|
120
|
+
super(nil)
|
121
|
+
end
|
122
|
+
|
123
|
+
def add_identifier(node)
|
124
|
+
@nodes << node
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class ConditionalNode < ListNode
|
129
|
+
def initialize(_ = nil)
|
130
|
+
# @nodes = [<test>, <consequent>] or
|
131
|
+
# [<test>, <consequent>, <alternate>]
|
132
|
+
super(1)
|
133
|
+
end
|
134
|
+
|
135
|
+
def test
|
136
|
+
@nodes[0]
|
137
|
+
end
|
138
|
+
|
139
|
+
def test=(node)
|
140
|
+
@nodes[0] = node
|
141
|
+
end
|
142
|
+
|
143
|
+
def consequent
|
144
|
+
@nodes[1]
|
145
|
+
end
|
146
|
+
|
147
|
+
def consequent=(node)
|
148
|
+
@nodes[1] = node
|
149
|
+
end
|
150
|
+
|
151
|
+
def alternate
|
152
|
+
@nodes[2]
|
153
|
+
end
|
154
|
+
|
155
|
+
def alternate=(node)
|
156
|
+
@nodes[2] = node
|
157
|
+
end
|
158
|
+
|
159
|
+
def alternate?
|
160
|
+
!@nodes[2].nil?
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class AssignmentNode < ListNode
|
165
|
+
def initialize(_ = nil)
|
166
|
+
# @nodes = [<identifier>, <expression>]
|
167
|
+
super(2)
|
168
|
+
end
|
169
|
+
|
170
|
+
def identifier
|
171
|
+
@nodes[0]
|
172
|
+
end
|
173
|
+
|
174
|
+
def identifier=(node)
|
175
|
+
@nodes[0] = node
|
176
|
+
end
|
177
|
+
|
178
|
+
def expression
|
179
|
+
@nodes[1]
|
180
|
+
end
|
181
|
+
|
182
|
+
def expression=(node)
|
183
|
+
@nodes[1] = node
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
class IdentifierDefinitionNode < ListNode
|
188
|
+
def initialize(_ = nil)
|
189
|
+
# @nodes = [<identifier>, <expression>]
|
190
|
+
# <expression> might be a lambda expression.
|
191
|
+
super(2)
|
192
|
+
end
|
193
|
+
|
194
|
+
def identifier
|
195
|
+
@nodes[0]
|
196
|
+
end
|
197
|
+
|
198
|
+
def identifier=(node)
|
199
|
+
@nodes[0] = node
|
200
|
+
end
|
201
|
+
|
202
|
+
def expression
|
203
|
+
@nodes[1]
|
204
|
+
end
|
205
|
+
|
206
|
+
def expression=(node)
|
207
|
+
@nodes[1] = node
|
208
|
+
end
|
209
|
+
|
210
|
+
def def_formals
|
211
|
+
lambda? ? expression.formals : nil
|
212
|
+
end
|
213
|
+
|
214
|
+
def body
|
215
|
+
lambda? ? expression.body : nil
|
216
|
+
end
|
217
|
+
|
218
|
+
private
|
219
|
+
|
220
|
+
def lambda?
|
221
|
+
expression.type == :ast_lambda_expression
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
class CondNode < ListNode
|
226
|
+
def initialize(_ = nil)
|
227
|
+
super(nil)
|
228
|
+
end
|
229
|
+
|
230
|
+
def cond_clause
|
231
|
+
@nodes[0..-1]
|
232
|
+
end
|
233
|
+
|
234
|
+
def add_clause(node)
|
235
|
+
@nodes << node
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class CondClauseNode < ListNode
|
240
|
+
# @nodes = [<test>, <sequence>]
|
241
|
+
def initialize(_ = nil)
|
242
|
+
super(nil)
|
243
|
+
end
|
244
|
+
|
245
|
+
def test
|
246
|
+
@nodes[0]
|
247
|
+
end
|
248
|
+
|
249
|
+
def test=(node)
|
250
|
+
@nodes[0] = node
|
251
|
+
end
|
252
|
+
|
253
|
+
def sequence
|
254
|
+
@nodes[1..-1]
|
255
|
+
end
|
256
|
+
|
257
|
+
def add_expression(node)
|
258
|
+
@nodes << node
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
class AndNode < ListNode
|
263
|
+
def initialize(_ = nil)
|
264
|
+
# @nodes = [<test>, ...]
|
265
|
+
super(nil)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
class OrNode < ListNode
|
270
|
+
def initialize(_ = nil)
|
271
|
+
# @nodes = [<test>, ...]
|
272
|
+
super(nil)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
class TestAndSequenceNode < ListNode
|
277
|
+
def initialize(_ = nil)
|
278
|
+
super(1)
|
279
|
+
end
|
280
|
+
|
281
|
+
def test
|
282
|
+
@nodes[0]
|
283
|
+
end
|
284
|
+
|
285
|
+
def test=(node)
|
286
|
+
@nodes[0] = node
|
287
|
+
end
|
288
|
+
|
289
|
+
def sequence
|
290
|
+
@nodes[1..-1]
|
291
|
+
end
|
292
|
+
|
293
|
+
def add_sequence(node)
|
294
|
+
@nodes << node
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
class WhenNode < TestAndSequenceNode
|
299
|
+
end
|
300
|
+
|
301
|
+
class UnlessNode < TestAndSequenceNode
|
302
|
+
end
|
303
|
+
|
304
|
+
class LetNode < ListNode
|
305
|
+
def initialize(_ = nil)
|
306
|
+
# @nodes = [<bindings>, <body>, ...] or
|
307
|
+
# [<identifier>, <bindings>, <body>, ...]
|
308
|
+
super(1)
|
309
|
+
end
|
310
|
+
|
311
|
+
def identifier
|
312
|
+
named_let? ? @nodes[0] : nil
|
313
|
+
end
|
314
|
+
|
315
|
+
def identifier=(node)
|
316
|
+
@nodes.insert(0, node) if node.type == :ast_identifier
|
317
|
+
end
|
318
|
+
|
319
|
+
def bindings
|
320
|
+
named_let? ? @nodes[1] : @nodes[0]
|
321
|
+
end
|
322
|
+
|
323
|
+
def bindings=(node)
|
324
|
+
if named_let?
|
325
|
+
@nodes[1] = node
|
326
|
+
else
|
327
|
+
@nodes[0] = node
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def body
|
332
|
+
named_let? ? @nodes[2..-1] : @nodes[1..-1]
|
333
|
+
end
|
334
|
+
|
335
|
+
def body=(nodes)
|
336
|
+
start_pos = named_let? ? 2 : 1
|
337
|
+
nodes.each_with_index { |node, i|
|
338
|
+
@nodes[start_pos + i] = node
|
339
|
+
}
|
340
|
+
end
|
341
|
+
|
342
|
+
private
|
343
|
+
|
344
|
+
def named_let?
|
345
|
+
@nodes[0].nil? ? false : @nodes[0].type == :ast_identifier
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
class LetBaseNode < ListNode
|
350
|
+
def initialize(_ = nil)
|
351
|
+
# @nodes = [<bindings>, <body>, ...]
|
352
|
+
super(1)
|
353
|
+
end
|
354
|
+
|
355
|
+
def bindings
|
356
|
+
@nodes[0]
|
357
|
+
end
|
358
|
+
|
359
|
+
def bindings=(node)
|
360
|
+
@nodes[0] = node
|
361
|
+
end
|
362
|
+
|
363
|
+
def body
|
364
|
+
@nodes[1..-1]
|
365
|
+
end
|
366
|
+
|
367
|
+
def body=(nodes)
|
368
|
+
nodes.each_with_index { |node, i|
|
369
|
+
@nodes[1 + i] = node
|
370
|
+
}
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
class LetStarNode < LetBaseNode
|
375
|
+
end
|
376
|
+
|
377
|
+
class LetrecNode < LetBaseNode
|
378
|
+
end
|
379
|
+
|
380
|
+
class LetrecStarNode < LetBaseNode
|
381
|
+
end
|
382
|
+
|
383
|
+
class BindingsNode < ListNode
|
384
|
+
def initialize(_ = nil)
|
385
|
+
# @nodes = [<bind spec 1>, <bind spec 2> , ...]
|
386
|
+
super(nil)
|
387
|
+
end
|
388
|
+
|
389
|
+
def add_bind_spec(node)
|
390
|
+
@nodes << node
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
class BindSpecNode < ListNode
|
395
|
+
def initialize(_ = nil)
|
396
|
+
super(2)
|
397
|
+
end
|
398
|
+
|
399
|
+
def identifier
|
400
|
+
@nodes[0]
|
401
|
+
end
|
402
|
+
|
403
|
+
def identifier=(node)
|
404
|
+
@nodes[0] = node
|
405
|
+
end
|
406
|
+
|
407
|
+
def expression
|
408
|
+
@nodes[1]
|
409
|
+
end
|
410
|
+
|
411
|
+
def expression=(node)
|
412
|
+
@nodes[1] = node
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
class BeginNode < ListNode
|
417
|
+
def initialize(_ = nil)
|
418
|
+
super(nil)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
class DoNode < ListNode
|
423
|
+
def initialize(_ = nil)
|
424
|
+
# @nodes = [<iteration bindings>, <test and do result>, <command>, ...]
|
425
|
+
super(2)
|
426
|
+
end
|
427
|
+
|
428
|
+
def iteration_bindings
|
429
|
+
@nodes[0]
|
430
|
+
end
|
431
|
+
|
432
|
+
def iteration_bindings=(node)
|
433
|
+
@nodes[0] = node
|
434
|
+
end
|
435
|
+
|
436
|
+
def test_and_do_result
|
437
|
+
@nodes[1]
|
438
|
+
end
|
439
|
+
|
440
|
+
def test_and_do_result=(node)
|
441
|
+
@nodes[1] = node
|
442
|
+
end
|
443
|
+
|
444
|
+
def commands
|
445
|
+
@nodes[2..-1]
|
446
|
+
end
|
447
|
+
|
448
|
+
def add_command(node)
|
449
|
+
@nodes << node
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
class IterationBindingsNode < ListNode
|
454
|
+
def initialize(_ = nil)
|
455
|
+
super(nil)
|
456
|
+
end
|
457
|
+
|
458
|
+
def add_iteration_spec(node)
|
459
|
+
@nodes << node
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
class TestAndDoResultNode < ListNode
|
464
|
+
def initialize(_ = nil)
|
465
|
+
super(1)
|
466
|
+
end
|
467
|
+
|
468
|
+
def test
|
469
|
+
@nodes[0]
|
470
|
+
end
|
471
|
+
|
472
|
+
def test=(node)
|
473
|
+
@nodes[0] = node
|
474
|
+
end
|
475
|
+
|
476
|
+
def add_expression(node)
|
477
|
+
@nodes << node
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
class IterationSpecNode < ListNode
|
482
|
+
def initialize(_ = nil)
|
483
|
+
# @nodes = [<identifier>, <init>, <step>]
|
484
|
+
super(3)
|
485
|
+
end
|
486
|
+
|
487
|
+
def identifier
|
488
|
+
@nodes[0]
|
489
|
+
end
|
490
|
+
|
491
|
+
def identifier=(node)
|
492
|
+
@nodes[0] = node
|
493
|
+
end
|
494
|
+
|
495
|
+
def init
|
496
|
+
@nodes[1]
|
497
|
+
end
|
498
|
+
|
499
|
+
def init=(node)
|
500
|
+
@nodes[1] = node
|
501
|
+
end
|
502
|
+
|
503
|
+
def step
|
504
|
+
@nodes[2]
|
505
|
+
end
|
506
|
+
|
507
|
+
def step=(node)
|
508
|
+
@nodes[2] = node
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
end # end of AST
|
513
|
+
|
514
|
+
end
|