apricot 0.0.1
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.
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +26 -0
- data/README.md +90 -0
- data/Rakefile +9 -0
- data/apricot.gemspec +22 -0
- data/bin/apricot +58 -0
- data/examples/bot.apr +23 -0
- data/examples/cinch-bot.apr +12 -0
- data/examples/hanoi.apr +10 -0
- data/examples/hello.apr +1 -0
- data/examples/plot.apr +28 -0
- data/examples/quine.apr +1 -0
- data/kernel/core.apr +928 -0
- data/lib/apricot/ast/identifier.rb +111 -0
- data/lib/apricot/ast/list.rb +99 -0
- data/lib/apricot/ast/literals.rb +240 -0
- data/lib/apricot/ast/node.rb +45 -0
- data/lib/apricot/ast/scopes.rb +147 -0
- data/lib/apricot/ast/toplevel.rb +66 -0
- data/lib/apricot/ast/variables.rb +64 -0
- data/lib/apricot/ast.rb +3 -0
- data/lib/apricot/compiler.rb +55 -0
- data/lib/apricot/cons.rb +27 -0
- data/lib/apricot/errors.rb +38 -0
- data/lib/apricot/generator.rb +15 -0
- data/lib/apricot/identifier.rb +91 -0
- data/lib/apricot/list.rb +96 -0
- data/lib/apricot/macroexpand.rb +47 -0
- data/lib/apricot/misc.rb +11 -0
- data/lib/apricot/namespace.rb +59 -0
- data/lib/apricot/parser.rb +541 -0
- data/lib/apricot/printers.rb +12 -0
- data/lib/apricot/repl.rb +254 -0
- data/lib/apricot/ruby_ext.rb +254 -0
- data/lib/apricot/seq.rb +44 -0
- data/lib/apricot/special_forms.rb +735 -0
- data/lib/apricot/stages.rb +60 -0
- data/lib/apricot/version.rb +3 -0
- data/lib/apricot.rb +30 -0
- data/spec/compiler_spec.rb +499 -0
- data/spec/identifier_spec.rb +58 -0
- data/spec/list_spec.rb +96 -0
- data/spec/parser_spec.rb +312 -0
- data/spec/spec_helper.rb +10 -0
- metadata +188 -0
@@ -0,0 +1,735 @@
|
|
1
|
+
module Apricot
|
2
|
+
class SpecialForm
|
3
|
+
Specials = {}
|
4
|
+
|
5
|
+
def self.[](name)
|
6
|
+
Specials[name.to_sym]
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.define(name, &block)
|
10
|
+
name = name.to_sym
|
11
|
+
Specials[name] = new(name, block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(name, block)
|
15
|
+
@name = name.to_sym
|
16
|
+
@block = block
|
17
|
+
end
|
18
|
+
|
19
|
+
def bytecode(g, args)
|
20
|
+
@block.call(g, args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# (. receiver method args*)
|
25
|
+
# (. receiver method args* & rest)
|
26
|
+
# (. receiver method args* | block)
|
27
|
+
# (. receiver method args* & rest | block)
|
28
|
+
# (. receiver (method args*))
|
29
|
+
# (. receiver (method args* & rest))
|
30
|
+
# (. receiver (method args* | block))
|
31
|
+
# (. receiver (method args* & rest | block))
|
32
|
+
SpecialForm.define(:'.') do |g, args|
|
33
|
+
g.compile_error "Too few arguments to send expression, expecting (. receiver method ...)" if args.length < 2
|
34
|
+
|
35
|
+
receiver, method_or_list = args.shift(2)
|
36
|
+
|
37
|
+
# Handle the (. receiver (method args*)) form
|
38
|
+
if method_or_list.is_a? AST::List
|
39
|
+
method = method_or_list.elements.shift
|
40
|
+
|
41
|
+
g.compile_error "Invalid send expression, expecting (. receiver (method ...))" unless args.empty?
|
42
|
+
|
43
|
+
args = method_or_list.elements
|
44
|
+
else
|
45
|
+
method = method_or_list
|
46
|
+
end
|
47
|
+
|
48
|
+
g.compile_error "Method in send expression must be an identifier" unless method.is_a? AST::Identifier
|
49
|
+
|
50
|
+
block_arg = nil
|
51
|
+
splat_arg = nil
|
52
|
+
|
53
|
+
if args[-2].is_a?(AST::Identifier) && args[-2].name == :|
|
54
|
+
block_arg = args.last
|
55
|
+
args.pop(2)
|
56
|
+
end
|
57
|
+
|
58
|
+
if args[-2].is_a?(AST::Identifier) && args[-2].name == :&
|
59
|
+
splat_arg = args.last
|
60
|
+
args.pop(2)
|
61
|
+
end
|
62
|
+
|
63
|
+
args.each do |arg|
|
64
|
+
next unless arg.is_a?(AST::Identifier)
|
65
|
+
g.compile_error "Incorrect use of & in send expression" if arg.name == :&
|
66
|
+
g.compile_error "Incorrect use of | in send expression" if arg.name == :|
|
67
|
+
end
|
68
|
+
|
69
|
+
receiver.bytecode(g)
|
70
|
+
|
71
|
+
if block_arg || splat_arg
|
72
|
+
args.each {|a| a.bytecode(g) }
|
73
|
+
|
74
|
+
if splat_arg
|
75
|
+
splat_arg.bytecode(g)
|
76
|
+
g.cast_array unless splat_arg.is_a?(AST::ArrayLiteral)
|
77
|
+
end
|
78
|
+
|
79
|
+
if block_arg
|
80
|
+
nil_block = g.new_label
|
81
|
+
block_arg.bytecode(g)
|
82
|
+
g.dup
|
83
|
+
g.is_nil
|
84
|
+
g.git nil_block
|
85
|
+
|
86
|
+
g.push_const :Proc
|
87
|
+
|
88
|
+
g.swap
|
89
|
+
g.send :__from_block__, 1
|
90
|
+
|
91
|
+
nil_block.set!
|
92
|
+
else
|
93
|
+
g.push_nil
|
94
|
+
end
|
95
|
+
|
96
|
+
if splat_arg
|
97
|
+
g.send_with_splat method.name, args.length
|
98
|
+
else
|
99
|
+
g.send_with_block method.name, args.length
|
100
|
+
end
|
101
|
+
|
102
|
+
elsif method.name == :new
|
103
|
+
slow = g.new_label
|
104
|
+
done = g.new_label
|
105
|
+
|
106
|
+
g.dup # dup the receiver
|
107
|
+
g.check_serial :new, Rubinius::CompiledMethod::KernelMethodSerial
|
108
|
+
g.gif slow
|
109
|
+
|
110
|
+
# fast path
|
111
|
+
g.send :allocate, 0, true
|
112
|
+
g.dup
|
113
|
+
args.each {|a| a.bytecode(g) }
|
114
|
+
g.send :initialize, args.length, true
|
115
|
+
g.pop
|
116
|
+
|
117
|
+
g.goto done
|
118
|
+
|
119
|
+
# slow path
|
120
|
+
slow.set!
|
121
|
+
args.each {|a| a.bytecode(g) }
|
122
|
+
g.send :new, args.length
|
123
|
+
|
124
|
+
done.set!
|
125
|
+
|
126
|
+
else
|
127
|
+
args.each {|a| a.bytecode(g) }
|
128
|
+
g.send method.name, args.length
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# (def name value?)
|
133
|
+
SpecialForm.define(:def) do |g, args|
|
134
|
+
g.compile_error "Too few arguments to def" if args.length < 1
|
135
|
+
g.compile_error "Too many arguments to def" if args.length > 2
|
136
|
+
|
137
|
+
target, value = *args
|
138
|
+
|
139
|
+
value ||= AST::Literal.new(0, :nil)
|
140
|
+
|
141
|
+
case target
|
142
|
+
when AST::Identifier
|
143
|
+
target.assign_bytecode(g, value)
|
144
|
+
else
|
145
|
+
g.compile_error "First argument to def must be an identifier"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# (if cond body else_body?)
|
150
|
+
SpecialForm.define(:if) do |g, args|
|
151
|
+
g.compile_error "Too few arguments to if" if args.length < 2
|
152
|
+
g.compile_error "Too many arguments to if" if args.length > 3
|
153
|
+
|
154
|
+
cond, body, else_body = args
|
155
|
+
else_label, end_label = g.new_label, g.new_label
|
156
|
+
|
157
|
+
cond.bytecode(g)
|
158
|
+
g.gif else_label
|
159
|
+
|
160
|
+
body.bytecode(g)
|
161
|
+
g.goto end_label
|
162
|
+
|
163
|
+
else_label.set!
|
164
|
+
if else_body
|
165
|
+
else_body.bytecode(g)
|
166
|
+
else
|
167
|
+
g.push_nil
|
168
|
+
end
|
169
|
+
|
170
|
+
end_label.set!
|
171
|
+
end
|
172
|
+
|
173
|
+
# (do body*)
|
174
|
+
SpecialForm.define(:do) do |g, args|
|
175
|
+
if args.empty?
|
176
|
+
g.push_nil
|
177
|
+
else
|
178
|
+
args[0..-2].each do |a|
|
179
|
+
a.bytecode(g)
|
180
|
+
g.pop
|
181
|
+
end
|
182
|
+
args.last.bytecode(g)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# (quote form)
|
187
|
+
SpecialForm.define(:quote) do |g, args|
|
188
|
+
g.compile_error "Too few arguments to quote" if args.length < 1
|
189
|
+
g.compile_error "Too many arguments to quote" if args.length > 1
|
190
|
+
|
191
|
+
args.first.quote_bytecode(g)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Code shared between let and loop. type is :let or :loop
|
195
|
+
def self.let(g, args, type)
|
196
|
+
g.compile_error "Too few arguments to #{type}" if args.length < 1
|
197
|
+
g.compile_error "First argument to #{type} must be an array literal" unless args.first.is_a? AST::ArrayLiteral
|
198
|
+
|
199
|
+
bindings = args.shift.elements
|
200
|
+
|
201
|
+
g.compile_error "Bindings array for #{type} must contain an even number of forms" if bindings.length.odd?
|
202
|
+
|
203
|
+
scope = AST::LetScope.new(g.scope)
|
204
|
+
g.scopes << scope
|
205
|
+
|
206
|
+
bindings.each_slice(2) do |name, value|
|
207
|
+
g.compile_error "Binding targets in let must be identifiers" unless name.is_a? AST::Identifier
|
208
|
+
|
209
|
+
value.bytecode(g)
|
210
|
+
g.set_local scope.new_local(name)
|
211
|
+
g.pop
|
212
|
+
end
|
213
|
+
|
214
|
+
if type == :loop
|
215
|
+
scope.loop_label = g.new_label
|
216
|
+
scope.loop_label.set!
|
217
|
+
end
|
218
|
+
|
219
|
+
SpecialForm[:do].bytecode(g, args)
|
220
|
+
|
221
|
+
g.scopes.pop
|
222
|
+
end
|
223
|
+
|
224
|
+
# (let [binding*] body*) where binding is an identifier followed by a value
|
225
|
+
SpecialForm.define(:let) do |g, args|
|
226
|
+
let(g, args, :let)
|
227
|
+
end
|
228
|
+
|
229
|
+
# (loop [binding*] body*) where binding is an identifier followed by a value
|
230
|
+
# Just like let but also introduces a loop target for (recur ...)
|
231
|
+
SpecialForm.define(:loop) do |g, args|
|
232
|
+
let(g, args, :loop)
|
233
|
+
end
|
234
|
+
|
235
|
+
# (recur args*)
|
236
|
+
# Rebinds the arguments of the nearest enclosing loop or fn and jumps to the
|
237
|
+
# top of the loop/fn. Argument rebinding is done in parallel (rebinding a
|
238
|
+
# variable in a recur will not affect uses of that variable in the other
|
239
|
+
# recur bindings.)
|
240
|
+
SpecialForm.define(:recur) do |g, args|
|
241
|
+
target = g.scope.find_recur_target
|
242
|
+
g.compile_error "No recursion target found for recur" unless target
|
243
|
+
vars = target.variables.values
|
244
|
+
|
245
|
+
# If there is a block arg, ignore it.
|
246
|
+
if target.is_a?(AST::OverloadScope) && target.block_arg
|
247
|
+
vars.pop
|
248
|
+
end
|
249
|
+
|
250
|
+
g.compile_error "Arity of recur does not match enclosing loop or fn" unless vars.length == args.length
|
251
|
+
|
252
|
+
args.each {|arg| arg.bytecode(g) }
|
253
|
+
|
254
|
+
# If the recur is in a variadic overload scope, and there is another
|
255
|
+
# overload with the same number of total arguments (not counting the
|
256
|
+
# variadic argument), then we must jump to that other overload if the
|
257
|
+
# list passed for the variadic argument list is empty.
|
258
|
+
variadic_with_secondary =
|
259
|
+
target.is_a?(AST::OverloadScope) &&
|
260
|
+
target.splat? &&
|
261
|
+
target.secondary_loop_label
|
262
|
+
|
263
|
+
if variadic_with_secondary
|
264
|
+
g.dup_top
|
265
|
+
g.move_down args.length # Save the variadic list behind the other args.
|
266
|
+
end
|
267
|
+
|
268
|
+
vars.reverse_each do |var|
|
269
|
+
g.set_local var
|
270
|
+
g.pop
|
271
|
+
end
|
272
|
+
|
273
|
+
if variadic_with_secondary
|
274
|
+
g.send :empty?, 0 # Check if the variadic list is empty.
|
275
|
+
g.goto_if_true target.secondary_loop_label
|
276
|
+
end
|
277
|
+
|
278
|
+
g.check_interrupts
|
279
|
+
g.goto target.loop_label
|
280
|
+
end
|
281
|
+
|
282
|
+
class ArgList
|
283
|
+
attr_reader :required_args, :optional_args, :rest_arg, :block_arg,
|
284
|
+
:num_required, :num_optional, :num_total
|
285
|
+
|
286
|
+
def initialize(args, g)
|
287
|
+
@required_args = []
|
288
|
+
@optional_args = []
|
289
|
+
@rest_arg = nil
|
290
|
+
@block_arg = nil
|
291
|
+
|
292
|
+
next_is_rest = false
|
293
|
+
next_is_block = false
|
294
|
+
|
295
|
+
args.each do |arg|
|
296
|
+
g.compile_error "Unexpected arguments after block argument" if @block_arg
|
297
|
+
|
298
|
+
case arg
|
299
|
+
when AST::ArrayLiteral
|
300
|
+
g.compile_error "Arguments in fn form must be identifiers" unless arg[0].is_a? AST::Identifier
|
301
|
+
g.compile_error "Arguments in fn form can have only one optional value" unless arg.elements.length == 2
|
302
|
+
|
303
|
+
optional_args << [arg[0].name, arg[1]]
|
304
|
+
when AST::Identifier
|
305
|
+
if arg.name == :& && !@block_arg
|
306
|
+
g.compile_error "Can't have two rest arguments in one overload" if @rest_arg
|
307
|
+
next_is_rest = true
|
308
|
+
elsif arg.name == :|
|
309
|
+
g.compile_error "Can't have two block arguments in one overload" if @block_arg
|
310
|
+
next_is_block = true
|
311
|
+
elsif next_is_rest
|
312
|
+
@rest_arg = arg.name
|
313
|
+
next_is_rest = false
|
314
|
+
elsif next_is_block
|
315
|
+
@block_arg = arg.name
|
316
|
+
next_is_block = false
|
317
|
+
else
|
318
|
+
g.compile_error "Unexpected arguments after rest argument" if @rest_arg
|
319
|
+
g.compile_error "Optional arguments in fn form must be last" if @optional_args.any?
|
320
|
+
@required_args << arg.name
|
321
|
+
end
|
322
|
+
else
|
323
|
+
g.compile_error "Arguments in fn form must be identifiers or 2-element arrays"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
g.compile_error "Expected identifier following & in argument list" if next_is_rest
|
328
|
+
g.compile_error "Expected identifier following | in argument list" if next_is_block
|
329
|
+
|
330
|
+
@num_required = @required_args.length
|
331
|
+
@num_optional = @optional_args.length
|
332
|
+
@num_total = @num_required + @num_optional
|
333
|
+
end
|
334
|
+
|
335
|
+
def to_array
|
336
|
+
args = @required_args.map {|id| Identifier.intern(id) }
|
337
|
+
args += @optional_args.map {|name, val| [Identifier.intern(name), val.to_value] }
|
338
|
+
args += [Identifier.intern(:|), Identifier.intern(@block_arg)] if @block_arg
|
339
|
+
args += [Identifier.intern(:&), Identifier.intern(@rest_arg)] if @rest_arg
|
340
|
+
args
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
Overload = Struct.new(:arglist, :body)
|
345
|
+
|
346
|
+
# (fn name? [args*] body*)
|
347
|
+
# (fn name? [args* | block] body*)
|
348
|
+
# (fn name? [args* & rest] body*)
|
349
|
+
# (fn name? [args* & rest | block] body*)
|
350
|
+
# (fn name? ([args*] body*) ... ([args*] body*))
|
351
|
+
SpecialForm.define(:fn) do |g, args|
|
352
|
+
fn_name = args.shift.name if args.first.is_a? AST::Identifier
|
353
|
+
doc_string = args.shift if args.first.is_a? AST::StringLiteral
|
354
|
+
|
355
|
+
overloads = []
|
356
|
+
# The overload that a (recur ...) in a variadic overload must jump to if
|
357
|
+
# the variadic argument list passed is empty, and a matching non-variadic
|
358
|
+
# overload exists.
|
359
|
+
secondary_recur_overload = nil
|
360
|
+
|
361
|
+
case args.first
|
362
|
+
when AST::List
|
363
|
+
# This is the multi-arity form (fn name? ([args*] body*) ... ([args*] body*))
|
364
|
+
args.each do |overload|
|
365
|
+
# Each overload is of the form ([args*] body*)
|
366
|
+
g.compile_error "Expected an arity overload (a list)" unless overload.is_a? AST::List
|
367
|
+
arglist, *body = overload.elements
|
368
|
+
g.compile_error "Argument list in overload must be an array literal" unless arglist.is_a? AST::ArrayLiteral
|
369
|
+
arglist = ArgList.new(arglist.elements, g)
|
370
|
+
overloads << Overload.new(arglist, body)
|
371
|
+
end
|
372
|
+
when AST::ArrayLiteral
|
373
|
+
# This is the single-arity form (fn name? [args*] body*)
|
374
|
+
arglist, *body = args
|
375
|
+
arglist = ArgList.new(arglist.elements, g)
|
376
|
+
overloads << Overload.new(arglist, body)
|
377
|
+
else
|
378
|
+
# Didn't match any of the legal forms.
|
379
|
+
g.compile_error "Expected argument list or arity overload in fn definition"
|
380
|
+
end
|
381
|
+
|
382
|
+
# Check that the overloads do not conflict with each other.
|
383
|
+
if overloads.length > 1
|
384
|
+
variadic, normals = overloads.partition {|overload| overload.arglist.rest_arg }
|
385
|
+
|
386
|
+
g.compile_error "Can't have more than one variadic overload" if variadic.length > 1
|
387
|
+
|
388
|
+
# Sort the non-variadic overloads by ascending number of required arguments.
|
389
|
+
normals.sort_by! {|overload| overload.arglist.num_required }
|
390
|
+
|
391
|
+
if variadic.length == 1
|
392
|
+
# If there is a variadic overload, it should have at least as many
|
393
|
+
# required arguments as the next highest overload.
|
394
|
+
variadic_arglist = variadic.first.arglist
|
395
|
+
if variadic_arglist.num_required < normals.last.arglist.num_required
|
396
|
+
g.compile_error "Can't have a fixed arity overload with more params than a variadic overload"
|
397
|
+
end
|
398
|
+
|
399
|
+
# Can't have two overloads with same number of required args unless
|
400
|
+
# they have no optional args and one of them is the variadic overload.
|
401
|
+
if variadic_arglist.num_required == normals.last.arglist.num_required &&
|
402
|
+
(variadic_arglist.num_optional != 0 || normals.last.arglist.num_optional != 0)
|
403
|
+
g.compile_error "Can't have two overloads with the same arity"
|
404
|
+
elsif normals.last.arglist.num_total > variadic_arglist.num_required
|
405
|
+
g.compile_error "Can't have an overload with more total (required + optional) arguments than the variadic overload has required argument"
|
406
|
+
end
|
407
|
+
|
408
|
+
# If there is a normal overload with the same number of total
|
409
|
+
# arguments as the variadic overload, then a (recur ...) in the
|
410
|
+
# variadic overload may need to jump to the non-variadic overload.
|
411
|
+
if variadic_arglist.num_total == normals.last.arglist.num_total
|
412
|
+
secondary_recur_overload = normals.length - 1
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
# Compare each consecutive two non-variadic overloads.
|
417
|
+
normals.each_cons(2) do |o1, o2|
|
418
|
+
arglist1 = o1.arglist
|
419
|
+
arglist2 = o2.arglist
|
420
|
+
if arglist1.num_required == arglist2.num_required
|
421
|
+
g.compile_error "Can't have two overloads with the same arity"
|
422
|
+
elsif arglist1.num_total >= arglist2.num_required
|
423
|
+
g.compile_error "Can't have an overload with as many total (required + optional) arguments as another overload has required arguments"
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
overloads = normals + variadic
|
428
|
+
end
|
429
|
+
|
430
|
+
fn = g.class.new
|
431
|
+
fn.name = fn_name || :__fn__
|
432
|
+
fn.file = g.file
|
433
|
+
|
434
|
+
fn.definition_line g.line
|
435
|
+
fn.set_line g.line
|
436
|
+
|
437
|
+
fn.total_args = 0
|
438
|
+
fn.required_args = 0
|
439
|
+
fn.local_count = 0
|
440
|
+
fn.local_names = []
|
441
|
+
|
442
|
+
fn_scope = AST::FnScope.new(g.scope, fn_name)
|
443
|
+
|
444
|
+
# Generate the code that selects and jumps to the correct overload based
|
445
|
+
# on the number of arguments passed.
|
446
|
+
if overloads.length > 1
|
447
|
+
overload_labels = overloads.map { fn.new_label }
|
448
|
+
nomatch_possible = false # Is it possible to match no overload?
|
449
|
+
nomatch = fn.new_label
|
450
|
+
|
451
|
+
last_args = overloads.last.arglist
|
452
|
+
if last_args.rest_arg
|
453
|
+
if overloads[-2].arglist.num_required == last_args.num_required
|
454
|
+
fn.passed_arg last_args.num_required
|
455
|
+
else
|
456
|
+
fn.passed_arg last_args.num_required - 1
|
457
|
+
end
|
458
|
+
fn.git overload_labels.last
|
459
|
+
else
|
460
|
+
fn.passed_arg last_args.num_required - 1
|
461
|
+
fn.git overload_labels.last
|
462
|
+
end
|
463
|
+
|
464
|
+
prev_num_required = last_args.num_required
|
465
|
+
|
466
|
+
(overloads.length - 2).downto(0) do |i|
|
467
|
+
arglist = overloads[i].arglist
|
468
|
+
|
469
|
+
jump = prev_num_required - arglist.num_total
|
470
|
+
if jump > 1
|
471
|
+
nomatch_possible = true
|
472
|
+
fn.passed_arg arglist.num_total
|
473
|
+
fn.git nomatch
|
474
|
+
end
|
475
|
+
|
476
|
+
if arglist.num_required == 0
|
477
|
+
if nomatch_possible
|
478
|
+
fn.goto overload_labels[i]
|
479
|
+
end
|
480
|
+
else
|
481
|
+
fn.passed_arg arglist.num_required - 1
|
482
|
+
fn.git overload_labels[i]
|
483
|
+
end
|
484
|
+
|
485
|
+
prev_num_required = arglist.num_required
|
486
|
+
end
|
487
|
+
|
488
|
+
if nomatch_possible
|
489
|
+
nomatch.set!
|
490
|
+
fn.push_const :ArgumentError
|
491
|
+
fn.push_literal "No matching overload"
|
492
|
+
fn.string_dup
|
493
|
+
fn.send :new, 1
|
494
|
+
fn.raise_exc
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
overloads.each_with_index do |overload, i|
|
499
|
+
arglist, body = overload.arglist, overload.body
|
500
|
+
overload_scope = AST::OverloadScope.new(fn_scope)
|
501
|
+
fn.scopes << overload_scope
|
502
|
+
|
503
|
+
# Check if there are any duplicate names in the argument list.
|
504
|
+
argnames = arglist.required_args + arglist.optional_args.map(&:first)
|
505
|
+
argnames << arglist.rest_arg if arglist.rest_arg
|
506
|
+
argnames << arglist.block_arg if arglist.block_arg
|
507
|
+
dup_name = argnames.detect {|name| argnames.count(name) > 1 }
|
508
|
+
g.compile_error "Duplicate argument name '#{dup_name}'" if dup_name
|
509
|
+
|
510
|
+
overload_labels[i].set! if overloads.length > 1
|
511
|
+
|
512
|
+
# Allocate slots for the required arguments
|
513
|
+
arglist.required_args.each {|arg| overload_scope.new_local(arg) }
|
514
|
+
|
515
|
+
next_optional = fn.new_label
|
516
|
+
|
517
|
+
arglist.optional_args.each_with_index do |(name, value), i|
|
518
|
+
# Calculate the position of this optional arg, off the end of the
|
519
|
+
# required args
|
520
|
+
arg_index = arglist.num_required + i
|
521
|
+
|
522
|
+
# Allocate a slot for this optional argument
|
523
|
+
overload_scope.new_local(name)
|
524
|
+
|
525
|
+
fn.passed_arg arg_index
|
526
|
+
fn.git next_optional
|
527
|
+
|
528
|
+
value.bytecode(fn)
|
529
|
+
fn.set_local arg_index
|
530
|
+
fn.pop
|
531
|
+
|
532
|
+
next_optional.set!
|
533
|
+
next_optional = fn.new_label
|
534
|
+
end
|
535
|
+
|
536
|
+
if arglist.rest_arg
|
537
|
+
# Allocate the slot for the rest argument
|
538
|
+
overload_scope.new_local(arglist.rest_arg)
|
539
|
+
overload_scope.splat = true
|
540
|
+
|
541
|
+
# If there is another overload with the same number of arguments,
|
542
|
+
# excluding the variadic argument, a (recur ...) in this variadic
|
543
|
+
# overload may need to jump to that one (if recur is given an empty
|
544
|
+
# list for the variadic argument). Store the other overload's label in
|
545
|
+
# this variadic overload scope so (recur ...) can find it.
|
546
|
+
if secondary_recur_overload
|
547
|
+
overload_scope.secondary_loop_label =
|
548
|
+
overload_labels[secondary_recur_overload]
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
overload_scope.loop_label = next_optional
|
553
|
+
overload_scope.loop_label.set!
|
554
|
+
|
555
|
+
# Allocate the slot for the block argument
|
556
|
+
if arglist.block_arg
|
557
|
+
overload_scope.new_local(arglist.block_arg)
|
558
|
+
overload_scope.block_arg = arglist.block_arg
|
559
|
+
fn.push_proc
|
560
|
+
fn.set_local overload_scope.find_var(arglist.block_arg).slot
|
561
|
+
fn.pop
|
562
|
+
end
|
563
|
+
|
564
|
+
SpecialForm[:do].bytecode(fn, body)
|
565
|
+
fn.ret
|
566
|
+
|
567
|
+
# TODO: does this make any sense with overloads?
|
568
|
+
fn.local_count += overload_scope.local_count
|
569
|
+
fn.local_names += overload_scope.local_names
|
570
|
+
|
571
|
+
# Pop the overload scope
|
572
|
+
fn.scopes.pop
|
573
|
+
end
|
574
|
+
|
575
|
+
fn.close
|
576
|
+
|
577
|
+
# Use the maximum total args
|
578
|
+
fn.total_args = overloads.last.arglist.num_total
|
579
|
+
# Use the minimum required args
|
580
|
+
fn.required_args = overloads.first.arglist.num_required
|
581
|
+
|
582
|
+
# If there is a rest arg, it will appear after all the required and
|
583
|
+
# optional arguments.
|
584
|
+
fn.splat_index = overloads.last.arglist.num_total if overloads.last.arglist.rest_arg
|
585
|
+
|
586
|
+
g.push_const :Kernel
|
587
|
+
g.create_block fn
|
588
|
+
g.send_with_block :lambda, 0
|
589
|
+
g.set_local fn_scope.self_reference.slot if fn_name
|
590
|
+
end
|
591
|
+
|
592
|
+
# (try body* (rescue name|[name condition*] body*)* (ensure body*)?)
|
593
|
+
SpecialForm.define(:try) do |g, args|
|
594
|
+
body = []
|
595
|
+
rescue_clauses = []
|
596
|
+
ensure_clause = nil
|
597
|
+
|
598
|
+
if args.last.is_a?(AST::List) && args.last[0].is_a?(AST::Identifier) && args.last[0].name == :ensure
|
599
|
+
ensure_clause = args.pop[1..-1] # Chop off the ensure identifier
|
600
|
+
end
|
601
|
+
|
602
|
+
args.each do |arg|
|
603
|
+
if arg.is_a?(AST::List) && arg[0].is_a?(AST::Identifier) && arg[0].name == :rescue
|
604
|
+
rescue_clauses << arg[1..-1] # Chop off the rescue identifier
|
605
|
+
else
|
606
|
+
g.compile_error "Unexpected form after rescue clause" unless rescue_clauses.empty?
|
607
|
+
body << arg
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
# Set up ensure
|
612
|
+
if ensure_clause
|
613
|
+
ensure_ex = g.new_label
|
614
|
+
ensure_ok = g.new_label
|
615
|
+
g.setup_unwind ensure_ex, 1
|
616
|
+
end
|
617
|
+
|
618
|
+
ex = g.new_label
|
619
|
+
done = g.new_label
|
620
|
+
|
621
|
+
g.push_exception_state
|
622
|
+
g.set_stack_local(ex_state = g.new_stack_local)
|
623
|
+
g.pop
|
624
|
+
|
625
|
+
# Evaluate body
|
626
|
+
g.setup_unwind ex, 0
|
627
|
+
SpecialForm[:do].bytecode(g, body)
|
628
|
+
g.pop_unwind
|
629
|
+
g.goto done
|
630
|
+
|
631
|
+
# Body raised an exception
|
632
|
+
ex.set!
|
633
|
+
|
634
|
+
# Save exception state for re-raise
|
635
|
+
g.push_exception_state
|
636
|
+
g.set_stack_local(raised_ex_state = g.new_stack_local)
|
637
|
+
g.pop
|
638
|
+
|
639
|
+
# Push exception for rescue conditions
|
640
|
+
g.push_current_exception
|
641
|
+
|
642
|
+
rescue_clauses.each do |clause|
|
643
|
+
# Parse either (rescue e body) or (rescue [e Exception] body)
|
644
|
+
if clause[0].is_a?(AST::Identifier)
|
645
|
+
name = clause.shift
|
646
|
+
conditions = []
|
647
|
+
elsif clause[0].is_a?(AST::ArrayLiteral)
|
648
|
+
conditions = clause.shift.elements
|
649
|
+
name = conditions.first
|
650
|
+
conditions = conditions.drop(1)
|
651
|
+
g.compile_error "Expected identifier as first form of rescue clause binding" unless name.is_a?(AST::Identifier)
|
652
|
+
else
|
653
|
+
g.compile_error "Expected identifier or array as first form of rescue clause"
|
654
|
+
end
|
655
|
+
|
656
|
+
# Default to StandardError for (rescue e body) and (rescue [e] body)
|
657
|
+
conditions << AST::Identifier.new(name.line, :StandardError) if conditions.empty?
|
658
|
+
|
659
|
+
body = g.new_label
|
660
|
+
next_rescue = g.new_label
|
661
|
+
|
662
|
+
conditions.each do |cond|
|
663
|
+
g.dup # The exception
|
664
|
+
cond.bytecode(g)
|
665
|
+
g.swap
|
666
|
+
g.send :===, 1
|
667
|
+
g.git body
|
668
|
+
end
|
669
|
+
g.goto next_rescue
|
670
|
+
|
671
|
+
# This rescue condition matched
|
672
|
+
body.set!
|
673
|
+
|
674
|
+
# Create a new scope to hold the exception
|
675
|
+
scope = AST::LetScope.new(g.scope)
|
676
|
+
g.scopes << scope
|
677
|
+
|
678
|
+
# Exception is still on the stack
|
679
|
+
g.set_local scope.new_local(name)
|
680
|
+
g.pop
|
681
|
+
|
682
|
+
SpecialForm[:do].bytecode(g, clause)
|
683
|
+
|
684
|
+
# Yay!
|
685
|
+
g.clear_exception
|
686
|
+
g.goto done
|
687
|
+
|
688
|
+
g.scopes.pop
|
689
|
+
|
690
|
+
# Rescue condition did not match
|
691
|
+
next_rescue.set!
|
692
|
+
end
|
693
|
+
|
694
|
+
# No rescue conditions matched, re-raise
|
695
|
+
g.pop # The exception
|
696
|
+
|
697
|
+
# Re-raise the original exception
|
698
|
+
g.push_stack_local raised_ex_state
|
699
|
+
g.restore_exception_state
|
700
|
+
g.reraise
|
701
|
+
|
702
|
+
# Body executed without exception or was rescued
|
703
|
+
done.set!
|
704
|
+
|
705
|
+
g.push_stack_local raised_ex_state
|
706
|
+
g.restore_exception_state
|
707
|
+
|
708
|
+
if ensure_clause
|
709
|
+
g.pop_unwind
|
710
|
+
g.goto ensure_ok
|
711
|
+
|
712
|
+
# Body raised an exception
|
713
|
+
ensure_ex.set!
|
714
|
+
|
715
|
+
# Execute ensure clause
|
716
|
+
g.push_exception_state
|
717
|
+
ensure_clause.each do |expr|
|
718
|
+
expr.bytecode(g)
|
719
|
+
g.pop # Ensure cannot return anything
|
720
|
+
end
|
721
|
+
g.restore_exception_state
|
722
|
+
|
723
|
+
g.reraise
|
724
|
+
|
725
|
+
# Body executed without exception or was rescued
|
726
|
+
ensure_ok.set!
|
727
|
+
|
728
|
+
# Execute ensure clause
|
729
|
+
ensure_clause.each do |expr|
|
730
|
+
expr.bytecode(g)
|
731
|
+
g.pop
|
732
|
+
end
|
733
|
+
end
|
734
|
+
end
|
735
|
+
end
|