apricot 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +3 -0
  2. data/.rspec +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +26 -0
  7. data/README.md +90 -0
  8. data/Rakefile +9 -0
  9. data/apricot.gemspec +22 -0
  10. data/bin/apricot +58 -0
  11. data/examples/bot.apr +23 -0
  12. data/examples/cinch-bot.apr +12 -0
  13. data/examples/hanoi.apr +10 -0
  14. data/examples/hello.apr +1 -0
  15. data/examples/plot.apr +28 -0
  16. data/examples/quine.apr +1 -0
  17. data/kernel/core.apr +928 -0
  18. data/lib/apricot/ast/identifier.rb +111 -0
  19. data/lib/apricot/ast/list.rb +99 -0
  20. data/lib/apricot/ast/literals.rb +240 -0
  21. data/lib/apricot/ast/node.rb +45 -0
  22. data/lib/apricot/ast/scopes.rb +147 -0
  23. data/lib/apricot/ast/toplevel.rb +66 -0
  24. data/lib/apricot/ast/variables.rb +64 -0
  25. data/lib/apricot/ast.rb +3 -0
  26. data/lib/apricot/compiler.rb +55 -0
  27. data/lib/apricot/cons.rb +27 -0
  28. data/lib/apricot/errors.rb +38 -0
  29. data/lib/apricot/generator.rb +15 -0
  30. data/lib/apricot/identifier.rb +91 -0
  31. data/lib/apricot/list.rb +96 -0
  32. data/lib/apricot/macroexpand.rb +47 -0
  33. data/lib/apricot/misc.rb +11 -0
  34. data/lib/apricot/namespace.rb +59 -0
  35. data/lib/apricot/parser.rb +541 -0
  36. data/lib/apricot/printers.rb +12 -0
  37. data/lib/apricot/repl.rb +254 -0
  38. data/lib/apricot/ruby_ext.rb +254 -0
  39. data/lib/apricot/seq.rb +44 -0
  40. data/lib/apricot/special_forms.rb +735 -0
  41. data/lib/apricot/stages.rb +60 -0
  42. data/lib/apricot/version.rb +3 -0
  43. data/lib/apricot.rb +30 -0
  44. data/spec/compiler_spec.rb +499 -0
  45. data/spec/identifier_spec.rb +58 -0
  46. data/spec/list_spec.rb +96 -0
  47. data/spec/parser_spec.rb +312 -0
  48. data/spec/spec_helper.rb +10 -0
  49. 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