apricot 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +1 -0
  5. data/Gemfile.lock +229 -11
  6. data/README.md +46 -29
  7. data/Rakefile +1 -1
  8. data/apricot.gemspec +7 -3
  9. data/benchmarks/factorial.rb +51 -0
  10. data/benchmarks/interpolate.rb +20 -0
  11. data/bin/apricot +5 -23
  12. data/examples/bot.apr +1 -4
  13. data/examples/cinch-bot.apr +3 -3
  14. data/examples/sinatra.apr +9 -0
  15. data/kernel/core.apr +124 -75
  16. data/kernel/repl.apr +37 -0
  17. data/lib/apricot.rb +7 -26
  18. data/lib/apricot/boot.rb +24 -0
  19. data/lib/apricot/code_loader.rb +108 -0
  20. data/lib/apricot/compiler.rb +265 -32
  21. data/lib/apricot/generator.rb +10 -3
  22. data/lib/apricot/identifier.rb +25 -10
  23. data/lib/apricot/list.rb +28 -41
  24. data/lib/apricot/macroexpand.rb +14 -8
  25. data/lib/apricot/misc.rb +2 -1
  26. data/lib/apricot/namespace.rb +20 -3
  27. data/lib/apricot/{parser.rb → reader.rb} +221 -194
  28. data/lib/apricot/repl.rb +67 -24
  29. data/lib/apricot/ruby_ext.rb +27 -16
  30. data/lib/apricot/scopes.rb +159 -0
  31. data/lib/apricot/seq.rb +43 -1
  32. data/lib/apricot/special_forms.rb +16 -695
  33. data/lib/apricot/special_forms/def.rb +32 -0
  34. data/lib/apricot/special_forms/do.rb +23 -0
  35. data/lib/apricot/special_forms/dot.rb +112 -0
  36. data/lib/apricot/special_forms/fn.rb +342 -0
  37. data/lib/apricot/special_forms/if.rb +31 -0
  38. data/lib/apricot/special_forms/let.rb +8 -0
  39. data/lib/apricot/special_forms/loop.rb +10 -0
  40. data/lib/apricot/special_forms/quote.rb +9 -0
  41. data/lib/apricot/special_forms/recur.rb +26 -0
  42. data/lib/apricot/special_forms/try.rb +146 -0
  43. data/lib/apricot/variables.rb +65 -0
  44. data/lib/apricot/version.rb +1 -1
  45. data/spec/compiler_spec.rb +53 -450
  46. data/spec/fn_spec.rb +206 -0
  47. data/spec/list_spec.rb +1 -1
  48. data/spec/reader_spec.rb +349 -0
  49. data/spec/spec_helper.rb +40 -4
  50. data/spec/special_forms_spec.rb +203 -0
  51. metadata +99 -133
  52. data/lib/apricot/ast.rb +0 -3
  53. data/lib/apricot/ast/identifier.rb +0 -111
  54. data/lib/apricot/ast/list.rb +0 -99
  55. data/lib/apricot/ast/literals.rb +0 -240
  56. data/lib/apricot/ast/node.rb +0 -45
  57. data/lib/apricot/ast/scopes.rb +0 -147
  58. data/lib/apricot/ast/toplevel.rb +0 -66
  59. data/lib/apricot/ast/variables.rb +0 -64
  60. data/lib/apricot/printers.rb +0 -12
  61. data/lib/apricot/stages.rb +0 -60
  62. data/spec/parser_spec.rb +0 -312
@@ -1,18 +1,17 @@
1
1
  module Apricot
2
2
  class SpecialForm
3
- Specials = {}
3
+ SPECIAL_FORMS = {}
4
4
 
5
5
  def self.[](name)
6
- Specials[name.to_sym]
6
+ SPECIAL_FORMS[name]
7
7
  end
8
8
 
9
9
  def self.define(name, &block)
10
- name = name.to_sym
11
- Specials[name] = new(name, block)
10
+ SPECIAL_FORMS[name] = new(name, block)
12
11
  end
13
12
 
14
13
  def initialize(name, block)
15
- @name = name.to_sym
14
+ @name = name
16
15
  @block = block
17
16
  end
18
17
 
@@ -21,193 +20,23 @@ module Apricot
21
20
  end
22
21
  end
23
22
 
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
23
  # Code shared between let and loop. type is :let or :loop
195
24
  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
25
+ g.compile_error "Too few arguments to #{type}" if args.count < 1
26
+ g.compile_error "First argument to #{type} must be an array literal" unless args.first.is_a? Array
198
27
 
199
- bindings = args.shift.elements
28
+ bindings, body = args.first, args.rest
200
29
 
201
30
  g.compile_error "Bindings array for #{type} must contain an even number of forms" if bindings.length.odd?
202
31
 
203
- scope = AST::LetScope.new(g.scope)
32
+ scope = LetScope.new(g.scope)
204
33
  g.scopes << scope
205
34
 
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
35
+ bindings.each_slice(2) do |id, value|
36
+ g.compile_error "Binding targets in #{type} must be identifiers" unless id.is_a? Identifier
208
37
 
209
- value.bytecode(g)
210
- g.set_local scope.new_local(name)
38
+ Compiler.bytecode(g, value)
39
+ g.set_local scope.new_local(id)
211
40
  g.pop
212
41
  end
213
42
 
@@ -216,520 +45,12 @@ module Apricot
216
45
  scope.loop_label.set!
217
46
  end
218
47
 
219
- SpecialForm[:do].bytecode(g, args)
48
+ SpecialForm[:do].bytecode(g, body)
220
49
 
221
50
  g.scopes.pop
222
51
  end
52
+ end
223
53
 
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
54
+ %w[def do dot fn if let loop quote recur try].each do |r|
55
+ require "apricot/special_forms/#{r}"
735
56
  end