/
compiler.rb
228 lines (180 loc) · 4.52 KB
/
compiler.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
require 'rexpl'
module Noscript
class Compiler
attr_reader :generator, :scope
alias g generator
alias s scope
def initialize(parent=nil)
@generator = Generator.new
parent_scope = parent ? parent.scope : nil
@scope = Scope.new(@generator, parent_scope)
@generator.push_state(@scope)
end
def compile(ast, debugging=false)
ast = Noscript::Parser.new.parse(ast) unless ast.kind_of?(AST::Node)
# require 'pp'
# pp ast
g.name = :call
if ast.respond_to?(:filename) && ast.filename
g.file = ast.filename
else
g.file = :"(noscript)"
end
g.set_line 1
g.required_args = 0
g.total_args = 0
g.splat_index = nil
ast.accept(self)
debug if debugging
g.ret
finalize
g.encode
cm = g.package Rubinius::CompiledMethod
puts cm.decode if $DEBUG
code = Code.new
ss = Rubinius::StaticScope.new Runtime
Rubinius.attach_method g.name, cm, ss, code
code
end
def visit_Script(o)
set_line(o)
o.body.accept(self)
end
def visit_Nodes(o)
set_line(o)
o.expressions.each do |exp|
exp.accept(self)
end
end
def visit_FunctionLiteral(o)
set_line(o)
# Get a new compiler
block = Compiler.new(self)
# Configures the new generator
# TODO Move this to a method on the compiler
block.generator.for_block = true
block.generator.total_args = o.arguments.size
block.generator.required_args = o.arguments.size
block.generator.post_args = o.arguments.size
block.generator.cast_for_multi_block_arg unless o.arguments.empty?
block.generator.set_line o.line if o.line
block.visit_arguments(o.arguments)
o.body.accept(block)
block.generator.ret
g.push_const :Function
# Invoke the create block instruction
# with the generator of the block compiler
g.create_block block.finalize
g.send :new, 1
end
def visit_arguments(args)
args.each_with_index do |a, i|
g.shift_array
s.set_local a
g.pop
end
g.pop unless args.empty?
end
def visit_CallNode(o)
meth = nil
if o.receiver
meth = o.method.is_a?(String) ? o.method : o.method.name
o.receiver.accept(self)
else
meth = :call
visit_Identifier(o.method)
end
o.arguments.each do |argument|
argument.accept(self)
end
g.noscript_send meth, o.arguments.length
end
def visit_Identifier(o)
set_line(o)
if o.constant?
g.push_const o.name.to_sym
return
end
if s.slot_for(o.name)
visit_LocalVariableAccess(o)
else
g.push_nil
end
end
def visit_LocalVariableAssignment(o)
set_line(o)
o.value.accept(self)
s.set_local o.name
end
AST::RubiniusNodes.each do |rbx|
define_method("visit_#{rbx}") do |o|
set_line(o)
o.bytecode(g)
end
end
def visit_LocalVariableAccess(o)
set_line(o)
s.push_variable o.name
end
def visit_SlotGet(o)
set_line(o)
o.receiver.accept(self)
g.push_literal o.name
g.send :get, 1
end
def visit_SlotAssign(o)
set_line(o)
o.receiver.accept(self)
g.push_literal o.name
o.value.accept(self)
g.send :put, 2
end
def visit_IfNode(o)
set_line(o)
done = g.new_label
else_label = g.new_label
o.condition.accept(self)
g.gif else_label
o.body.accept(self)
g.goto done
else_label.set!
o.else_body.accept(self)
g.set_line 0
done.set!
end
def visit_WhileNode(o)
set_line(o)
brk = g.new_label
repeat = g.new_label
repeat.set!
# Evaluate the condition and jump to the end if false
o.condition.accept(self)
g.gif brk
# Otherwise, evaluate the body and jump to the start of the loop again.
o.body.accept(self)
g.pop
g.goto repeat
brk.set!
g.push_nil
end
def finalize
g.local_names = s.variables
g.local_count = s.variables.size
g.close
g
end
def set_line(o)
g.set_line o.line if o.line
end
def debug(gen = self.g)
p '*****'
ip = 0
while instruction = gen.stream[ip]
instruct = Rubinius::InstructionSet[instruction]
ip += instruct.size
puts instruct.name
end
p '**end**'
end
end
end