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