apricot 0.0.1 → 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/.travis.yml +1 -0
- data/Gemfile.lock +229 -11
- data/README.md +46 -29
- data/Rakefile +1 -1
- data/apricot.gemspec +7 -3
- data/benchmarks/factorial.rb +51 -0
- data/benchmarks/interpolate.rb +20 -0
- data/bin/apricot +5 -23
- data/examples/bot.apr +1 -4
- data/examples/cinch-bot.apr +3 -3
- data/examples/sinatra.apr +9 -0
- data/kernel/core.apr +124 -75
- data/kernel/repl.apr +37 -0
- data/lib/apricot.rb +7 -26
- data/lib/apricot/boot.rb +24 -0
- data/lib/apricot/code_loader.rb +108 -0
- data/lib/apricot/compiler.rb +265 -32
- data/lib/apricot/generator.rb +10 -3
- data/lib/apricot/identifier.rb +25 -10
- data/lib/apricot/list.rb +28 -41
- data/lib/apricot/macroexpand.rb +14 -8
- data/lib/apricot/misc.rb +2 -1
- data/lib/apricot/namespace.rb +20 -3
- data/lib/apricot/{parser.rb → reader.rb} +221 -194
- data/lib/apricot/repl.rb +67 -24
- data/lib/apricot/ruby_ext.rb +27 -16
- data/lib/apricot/scopes.rb +159 -0
- data/lib/apricot/seq.rb +43 -1
- data/lib/apricot/special_forms.rb +16 -695
- data/lib/apricot/special_forms/def.rb +32 -0
- data/lib/apricot/special_forms/do.rb +23 -0
- data/lib/apricot/special_forms/dot.rb +112 -0
- data/lib/apricot/special_forms/fn.rb +342 -0
- data/lib/apricot/special_forms/if.rb +31 -0
- data/lib/apricot/special_forms/let.rb +8 -0
- data/lib/apricot/special_forms/loop.rb +10 -0
- data/lib/apricot/special_forms/quote.rb +9 -0
- data/lib/apricot/special_forms/recur.rb +26 -0
- data/lib/apricot/special_forms/try.rb +146 -0
- data/lib/apricot/variables.rb +65 -0
- data/lib/apricot/version.rb +1 -1
- data/spec/compiler_spec.rb +53 -450
- data/spec/fn_spec.rb +206 -0
- data/spec/list_spec.rb +1 -1
- data/spec/reader_spec.rb +349 -0
- data/spec/spec_helper.rb +40 -4
- data/spec/special_forms_spec.rb +203 -0
- metadata +99 -133
- data/lib/apricot/ast.rb +0 -3
- data/lib/apricot/ast/identifier.rb +0 -111
- data/lib/apricot/ast/list.rb +0 -99
- data/lib/apricot/ast/literals.rb +0 -240
- data/lib/apricot/ast/node.rb +0 -45
- data/lib/apricot/ast/scopes.rb +0 -147
- data/lib/apricot/ast/toplevel.rb +0 -66
- data/lib/apricot/ast/variables.rb +0 -64
- data/lib/apricot/printers.rb +0 -12
- data/lib/apricot/stages.rb +0 -60
- data/spec/parser_spec.rb +0 -312
@@ -0,0 +1,32 @@
|
|
1
|
+
module Apricot
|
2
|
+
# (def name value?)
|
3
|
+
SpecialForm.define(:def) do |g, args|
|
4
|
+
g.compile_error "Too few arguments to def" if args.count < 1
|
5
|
+
g.compile_error "Too many arguments to def" if args.count > 2
|
6
|
+
|
7
|
+
id, value = *args
|
8
|
+
|
9
|
+
g.compile_error "First argument to def must be an identifier" unless id.is_a? Identifier
|
10
|
+
|
11
|
+
if id.constant?
|
12
|
+
if id.const_names.length == 1
|
13
|
+
g.push_scope
|
14
|
+
else
|
15
|
+
g.push_const id.const_names[0]
|
16
|
+
id.const_names[1..-2].each {|n| g.find_const n }
|
17
|
+
end
|
18
|
+
|
19
|
+
g.push_literal id.const_names.last
|
20
|
+
Compiler.bytecode(g, value)
|
21
|
+
g.send :const_set, 2
|
22
|
+
else
|
23
|
+
g.compile_error "Can't change the value of self" if id.name == :self
|
24
|
+
|
25
|
+
g.push_const :Apricot
|
26
|
+
g.send :current_namespace, 0
|
27
|
+
g.push_literal id.name
|
28
|
+
Compiler.bytecode(g, value)
|
29
|
+
g.send :set_var, 2
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Apricot
|
2
|
+
# (do body*)
|
3
|
+
SpecialForm.define(:do) do |g, args|
|
4
|
+
if args.empty?
|
5
|
+
g.push_nil
|
6
|
+
else
|
7
|
+
tail_position = g.tail_position?
|
8
|
+
last_index = args.count - 1
|
9
|
+
|
10
|
+
args.each_with_index do |a, i|
|
11
|
+
g.pop unless i == 0
|
12
|
+
|
13
|
+
if i == last_index && tail_position
|
14
|
+
g.tail_position = true
|
15
|
+
else
|
16
|
+
g.tail_position = false
|
17
|
+
end
|
18
|
+
|
19
|
+
Compiler.bytecode(g, a)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Apricot
|
2
|
+
# (. receiver method args*)
|
3
|
+
# (. receiver method args* & rest)
|
4
|
+
# (. receiver method args* | block)
|
5
|
+
# (. receiver method args* & rest | block)
|
6
|
+
# (. receiver (method args*))
|
7
|
+
# (. receiver (method args* & rest))
|
8
|
+
# (. receiver (method args* | block))
|
9
|
+
# (. receiver (method args* & rest | block))
|
10
|
+
SpecialForm.define(:'.') do |g, args|
|
11
|
+
g.compile_error "Too few arguments to send expression, expecting (. receiver method ...)" if args.count < 2
|
12
|
+
|
13
|
+
g.tail_position = false
|
14
|
+
|
15
|
+
# TODO: Don't convert to an array, just deal with the List.
|
16
|
+
args = args.to_a
|
17
|
+
|
18
|
+
receiver, method_or_list = args.shift(2)
|
19
|
+
|
20
|
+
# Handle the (. receiver (method args*)) form
|
21
|
+
if method_or_list.is_a? Seq
|
22
|
+
g.compile_error "Invalid send expression, expecting (. receiver (method ...))" unless args.empty?
|
23
|
+
|
24
|
+
method, args = method_or_list.first, method_or_list.rest.to_a
|
25
|
+
else
|
26
|
+
method = method_or_list
|
27
|
+
end
|
28
|
+
|
29
|
+
g.compile_error "Method in send expression must be an identifier" unless method.is_a? Identifier
|
30
|
+
|
31
|
+
block_arg = nil
|
32
|
+
splat_arg = nil
|
33
|
+
|
34
|
+
if args[-2].is_a?(Identifier) && args[-2].name == :|
|
35
|
+
block_arg = args.last
|
36
|
+
args.pop(2)
|
37
|
+
end
|
38
|
+
|
39
|
+
if args[-2].is_a?(Identifier) && args[-2].name == :&
|
40
|
+
splat_arg = args.last
|
41
|
+
args.pop(2)
|
42
|
+
end
|
43
|
+
|
44
|
+
args.each do |arg|
|
45
|
+
next unless arg.is_a?(Identifier)
|
46
|
+
g.compile_error "Incorrect use of & in send expression" if arg.name == :&
|
47
|
+
g.compile_error "Incorrect use of | in send expression" if arg.name == :|
|
48
|
+
end
|
49
|
+
|
50
|
+
Compiler.bytecode(g, receiver)
|
51
|
+
|
52
|
+
if block_arg || splat_arg
|
53
|
+
args.each {|a| Compiler.bytecode(g, a) }
|
54
|
+
|
55
|
+
if splat_arg
|
56
|
+
Compiler.bytecode(g, splat_arg)
|
57
|
+
g.cast_array unless splat_arg.is_a?(Array)
|
58
|
+
end
|
59
|
+
|
60
|
+
if block_arg
|
61
|
+
nil_block = g.new_label
|
62
|
+
Compiler.bytecode(g, block_arg)
|
63
|
+
g.dup
|
64
|
+
g.is_nil
|
65
|
+
g.git nil_block
|
66
|
+
|
67
|
+
g.push_const :Proc
|
68
|
+
|
69
|
+
g.swap
|
70
|
+
g.send :__from_block__, 1
|
71
|
+
|
72
|
+
nil_block.set!
|
73
|
+
else
|
74
|
+
g.push_nil
|
75
|
+
end
|
76
|
+
|
77
|
+
if splat_arg
|
78
|
+
g.send_with_splat method.name, args.length
|
79
|
+
else
|
80
|
+
g.send_with_block method.name, args.length
|
81
|
+
end
|
82
|
+
|
83
|
+
elsif method.name == :new
|
84
|
+
slow = g.new_label
|
85
|
+
done = g.new_label
|
86
|
+
|
87
|
+
g.dup # dup the receiver
|
88
|
+
g.check_serial :new, Rubinius::CompiledMethod::KernelMethodSerial
|
89
|
+
g.gif slow
|
90
|
+
|
91
|
+
# fast path
|
92
|
+
g.send :allocate, 0, true
|
93
|
+
g.dup
|
94
|
+
args.each {|a| Compiler.bytecode(g, a) }
|
95
|
+
g.send :initialize, args.length, true
|
96
|
+
g.pop
|
97
|
+
|
98
|
+
g.goto done
|
99
|
+
|
100
|
+
# slow path
|
101
|
+
slow.set!
|
102
|
+
args.each {|a| Compiler.bytecode(g, a) }
|
103
|
+
g.send :new, args.length
|
104
|
+
|
105
|
+
done.set!
|
106
|
+
|
107
|
+
else
|
108
|
+
args.each {|a| Compiler.bytecode(g, a) }
|
109
|
+
g.send method.name, args.length
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,342 @@
|
|
1
|
+
module Apricot
|
2
|
+
class SpecialForm
|
3
|
+
class ArgList
|
4
|
+
attr_reader :required_args, :optional_args, :rest_arg, :block_arg,
|
5
|
+
:num_required, :num_optional, :num_total
|
6
|
+
|
7
|
+
def initialize(args, g)
|
8
|
+
state = :required
|
9
|
+
|
10
|
+
@required_args = []
|
11
|
+
@optional_args = []
|
12
|
+
@rest_arg = nil
|
13
|
+
@block_arg = nil
|
14
|
+
|
15
|
+
args.each do |arg|
|
16
|
+
# Check if we got one of the special identifiers which moves us to a
|
17
|
+
# different part of the argument list. If so, move to the new state
|
18
|
+
# and skip to the next argument. Also check that the current state
|
19
|
+
# is allowed to move to the new state (the arguments must come in a
|
20
|
+
# strict order: required, optional, rest, block).
|
21
|
+
if arg.is_a? Identifier
|
22
|
+
case arg.name
|
23
|
+
# '?' starts the optional arguments section.
|
24
|
+
when :'?'
|
25
|
+
case state
|
26
|
+
when :required
|
27
|
+
state = :start_optional
|
28
|
+
next
|
29
|
+
else
|
30
|
+
g.compile_error "Unexpected '?' in argument list"
|
31
|
+
end
|
32
|
+
|
33
|
+
# '&' precedes the rest argument.
|
34
|
+
when :&
|
35
|
+
case state
|
36
|
+
when :required, :optional
|
37
|
+
state = :rest
|
38
|
+
next
|
39
|
+
else
|
40
|
+
g.compile_error "Unexpected '&' in argument list"
|
41
|
+
end
|
42
|
+
|
43
|
+
# '|' precedes the block argument.
|
44
|
+
when :|
|
45
|
+
case state
|
46
|
+
when :required, :optional, :after_rest
|
47
|
+
state = :block
|
48
|
+
next
|
49
|
+
else
|
50
|
+
g.compile_error "Unexpected '|' in argument list"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Deal with the argument based on the current state.
|
56
|
+
case state
|
57
|
+
when :required
|
58
|
+
g.compile_error "Required argument in argument list must be an identifier" unless arg.is_a? Identifier
|
59
|
+
@required_args << arg.name
|
60
|
+
|
61
|
+
when :optional, :start_optional
|
62
|
+
unless arg.is_a?(Seq) && arg.count == 2 && arg.first.is_a?(Identifier)
|
63
|
+
g.compile_error "Optional argument in argument list must be of the form (name default)"
|
64
|
+
end
|
65
|
+
|
66
|
+
state = :optional
|
67
|
+
@optional_args << [arg.first.name, arg.rest.first]
|
68
|
+
|
69
|
+
when :rest
|
70
|
+
g.compile_error "Rest argument in argument list must be an identifier" unless arg.is_a? Identifier
|
71
|
+
@rest_arg = arg.name
|
72
|
+
state = :after_rest
|
73
|
+
|
74
|
+
when :block
|
75
|
+
g.compile_error "Block argument in argument list must be an identifier" unless arg.is_a? Identifier
|
76
|
+
@block_arg = arg.name
|
77
|
+
state = :after_block
|
78
|
+
|
79
|
+
when :after_rest
|
80
|
+
g.compile_error "Unexpected argument after rest argument"
|
81
|
+
|
82
|
+
when :after_block
|
83
|
+
g.compile_error "Unexpected arguments after block argument"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Check if we finished in the middle of things without getting an
|
88
|
+
# argument where we expected one.
|
89
|
+
case state
|
90
|
+
when :start_optional
|
91
|
+
g.compile_error "Expected optional arguments after '?' in argument list"
|
92
|
+
|
93
|
+
when :rest
|
94
|
+
g.compile_error "Expected rest argument after '&' in argument list"
|
95
|
+
|
96
|
+
when :block
|
97
|
+
g.compile_error "Expected block argument after '|' in argument list"
|
98
|
+
end
|
99
|
+
|
100
|
+
@num_required = @required_args.length
|
101
|
+
@num_optional = @optional_args.length
|
102
|
+
@num_total = @num_required + @num_optional
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_array
|
106
|
+
args = @required_args.map {|id| Identifier.intern(id) }
|
107
|
+
args += @optional_args.map {|name, val| [Identifier.intern(name), val.to_value] }
|
108
|
+
args += [Identifier.intern(:|), Identifier.intern(@block_arg)] if @block_arg
|
109
|
+
args += [Identifier.intern(:&), Identifier.intern(@rest_arg)] if @rest_arg
|
110
|
+
args
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
Overload = Struct.new(:arglist, :body)
|
115
|
+
|
116
|
+
# (fn name? [args*] body*)
|
117
|
+
# (fn name? [args* | block] body*)
|
118
|
+
# (fn name? [args* & rest] body*)
|
119
|
+
# (fn name? [args* & rest | block] body*)
|
120
|
+
# (fn name? ([args*] body*) ... ([args*] body*))
|
121
|
+
SpecialForm.define(:fn) do |g, args|
|
122
|
+
fn_name, args = args.first.name, args.rest if args.first.is_a? Identifier
|
123
|
+
doc_string, args = args.first, args.rest if args.first.is_a? String
|
124
|
+
|
125
|
+
overloads = []
|
126
|
+
|
127
|
+
case args.first
|
128
|
+
when Seq
|
129
|
+
# This is the multi-arity form (fn name? ([args*] body*) ... ([args*] body*))
|
130
|
+
args.each do |overload|
|
131
|
+
# Each overload is of the form ([args*] body*)
|
132
|
+
g.compile_error "Expected an arity overload (a list)" unless overload.is_a? Seq
|
133
|
+
arglist, body = overload.first, overload.rest
|
134
|
+
g.compile_error "Argument list in overload must be an array literal" unless arglist.is_a? Array
|
135
|
+
arglist = ArgList.new(arglist, g)
|
136
|
+
overloads << Overload.new(arglist, body)
|
137
|
+
end
|
138
|
+
when Array
|
139
|
+
# This is the single-arity form (fn name? [args*] body*)
|
140
|
+
arglist, body = args.first, args.rest
|
141
|
+
arglist = ArgList.new(arglist, g)
|
142
|
+
overloads << Overload.new(arglist, body)
|
143
|
+
else
|
144
|
+
# Didn't match any of the legal forms.
|
145
|
+
g.compile_error "Expected argument list or arity overload in fn definition"
|
146
|
+
end
|
147
|
+
|
148
|
+
# Check that the overloads do not conflict with each other.
|
149
|
+
if overloads.length > 1
|
150
|
+
variadic, normals = overloads.partition {|overload| overload.arglist.rest_arg }
|
151
|
+
|
152
|
+
g.compile_error "Can't have more than one variadic overload" if variadic.length > 1
|
153
|
+
|
154
|
+
# Sort the non-variadic overloads by ascending number of required arguments.
|
155
|
+
normals.sort_by! {|overload| overload.arglist.num_required }
|
156
|
+
|
157
|
+
if variadic.length == 1
|
158
|
+
# If there is a variadic overload, it should have at least as many
|
159
|
+
# required arguments as the next highest overload.
|
160
|
+
variadic_arglist = variadic.first.arglist
|
161
|
+
if variadic_arglist.num_required < normals.last.arglist.num_required
|
162
|
+
g.compile_error "Can't have a fixed arity overload with more params than a variadic overload"
|
163
|
+
end
|
164
|
+
|
165
|
+
# Can't have two overloads with same number of required args unless
|
166
|
+
# they have no optional args and one of them is the variadic overload.
|
167
|
+
if variadic_arglist.num_required == normals.last.arglist.num_required &&
|
168
|
+
(variadic_arglist.num_optional != 0 || normals.last.arglist.num_optional != 0)
|
169
|
+
g.compile_error "Can't have two overloads with the same arity"
|
170
|
+
elsif normals.last.arglist.num_total > variadic_arglist.num_required
|
171
|
+
g.compile_error "Can't have an overload with more total (required + optional) arguments than the variadic overload has required argument"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Compare each consecutive two non-variadic overloads.
|
176
|
+
normals.each_cons(2) do |o1, o2|
|
177
|
+
arglist1 = o1.arglist
|
178
|
+
arglist2 = o2.arglist
|
179
|
+
if arglist1.num_required == arglist2.num_required
|
180
|
+
g.compile_error "Can't have two overloads with the same arity"
|
181
|
+
elsif arglist1.num_total >= arglist2.num_required
|
182
|
+
g.compile_error "Can't have an overload with as many total (required + optional) arguments as another overload has required arguments"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
overloads = normals + variadic
|
187
|
+
end
|
188
|
+
|
189
|
+
fn = g.class.new
|
190
|
+
fn.name = fn_name || :__fn__
|
191
|
+
fn.file = g.file
|
192
|
+
|
193
|
+
fn.definition_line g.line
|
194
|
+
fn.set_line g.line
|
195
|
+
|
196
|
+
fn.total_args = 0
|
197
|
+
fn.required_args = 0
|
198
|
+
fn.local_count = 0
|
199
|
+
fn.local_names = []
|
200
|
+
|
201
|
+
fn_scope = FnScope.new(g.scope, fn_name)
|
202
|
+
|
203
|
+
# Generate the code that selects and jumps to the correct overload based
|
204
|
+
# on the number of arguments passed.
|
205
|
+
if overloads.length > 1
|
206
|
+
overload_labels = overloads.map { fn.new_label }
|
207
|
+
nomatch_possible = false # Is it possible to match no overload?
|
208
|
+
nomatch = fn.new_label
|
209
|
+
|
210
|
+
last_args = overloads.last.arglist
|
211
|
+
if last_args.rest_arg
|
212
|
+
if overloads[-2].arglist.num_required == last_args.num_required
|
213
|
+
fn.passed_arg last_args.num_required
|
214
|
+
else
|
215
|
+
fn.passed_arg last_args.num_required - 1
|
216
|
+
end
|
217
|
+
fn.git overload_labels.last
|
218
|
+
else
|
219
|
+
fn.passed_arg last_args.num_required - 1
|
220
|
+
fn.git overload_labels.last
|
221
|
+
end
|
222
|
+
|
223
|
+
prev_num_required = last_args.num_required
|
224
|
+
|
225
|
+
(overloads.length - 2).downto(0) do |i|
|
226
|
+
arglist = overloads[i].arglist
|
227
|
+
|
228
|
+
jump = prev_num_required - arglist.num_total
|
229
|
+
if jump > 1
|
230
|
+
nomatch_possible = true
|
231
|
+
fn.passed_arg arglist.num_total
|
232
|
+
fn.git nomatch
|
233
|
+
end
|
234
|
+
|
235
|
+
if arglist.num_required == 0
|
236
|
+
if nomatch_possible
|
237
|
+
fn.goto overload_labels[i]
|
238
|
+
end
|
239
|
+
else
|
240
|
+
fn.passed_arg arglist.num_required - 1
|
241
|
+
fn.git overload_labels[i]
|
242
|
+
end
|
243
|
+
|
244
|
+
prev_num_required = arglist.num_required
|
245
|
+
end
|
246
|
+
|
247
|
+
if nomatch_possible
|
248
|
+
nomatch.set!
|
249
|
+
fn.push_const :ArgumentError
|
250
|
+
fn.push_literal "No matching overload"
|
251
|
+
fn.string_dup
|
252
|
+
fn.send :new, 1
|
253
|
+
fn.raise_exc
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
overloads.each_with_index do |overload, i|
|
258
|
+
arglist, body = overload.arglist, overload.body
|
259
|
+
overload_scope = OverloadScope.new(fn_scope)
|
260
|
+
fn.scopes << overload_scope
|
261
|
+
|
262
|
+
# Check if there are any duplicate names in the argument list.
|
263
|
+
argnames = arglist.required_args + arglist.optional_args.map(&:first)
|
264
|
+
argnames << arglist.rest_arg if arglist.rest_arg
|
265
|
+
argnames << arglist.block_arg if arglist.block_arg
|
266
|
+
dup_name = argnames.detect {|name| argnames.count(name) > 1 }
|
267
|
+
g.compile_error "Duplicate argument name '#{dup_name}'" if dup_name
|
268
|
+
|
269
|
+
overload_labels[i].set! if overloads.length > 1
|
270
|
+
|
271
|
+
# Allocate slots for the required arguments
|
272
|
+
arglist.required_args.each {|arg| overload_scope.new_local(arg) }
|
273
|
+
|
274
|
+
next_optional = fn.new_label
|
275
|
+
|
276
|
+
arglist.optional_args.each_with_index do |(name, value), i|
|
277
|
+
# Calculate the position of this optional arg, off the end of the
|
278
|
+
# required args
|
279
|
+
arg_index = arglist.num_required + i
|
280
|
+
|
281
|
+
# Allocate a slot for this optional argument
|
282
|
+
overload_scope.new_local(name)
|
283
|
+
|
284
|
+
fn.passed_arg arg_index
|
285
|
+
fn.git next_optional
|
286
|
+
|
287
|
+
Compiler.bytecode(fn, value)
|
288
|
+
fn.set_local arg_index
|
289
|
+
fn.pop
|
290
|
+
|
291
|
+
next_optional.set!
|
292
|
+
next_optional = fn.new_label
|
293
|
+
end
|
294
|
+
|
295
|
+
if arglist.rest_arg
|
296
|
+
# Allocate the slot for the rest argument
|
297
|
+
overload_scope.new_local(arglist.rest_arg)
|
298
|
+
overload_scope.splat = true
|
299
|
+
end
|
300
|
+
|
301
|
+
overload_scope.loop_label = next_optional
|
302
|
+
overload_scope.loop_label.set!
|
303
|
+
|
304
|
+
# Allocate the slot for the block argument
|
305
|
+
if arglist.block_arg
|
306
|
+
overload_scope.new_local(arglist.block_arg)
|
307
|
+
overload_scope.block_arg = arglist.block_arg
|
308
|
+
fn.push_proc
|
309
|
+
fn.set_local overload_scope.find_var(arglist.block_arg).slot
|
310
|
+
fn.pop
|
311
|
+
end
|
312
|
+
|
313
|
+
fn.tail_position = true
|
314
|
+
SpecialForm[:do].bytecode(fn, body)
|
315
|
+
fn.ret
|
316
|
+
|
317
|
+
# TODO: does this make any sense with overloads?
|
318
|
+
fn.local_count += overload_scope.local_count
|
319
|
+
fn.local_names += overload_scope.local_names
|
320
|
+
|
321
|
+
# Pop the overload scope
|
322
|
+
fn.scopes.pop
|
323
|
+
end
|
324
|
+
|
325
|
+
fn.close
|
326
|
+
|
327
|
+
# Use the maximum total args
|
328
|
+
fn.total_args = overloads.last.arglist.num_total
|
329
|
+
# Use the minimum required args
|
330
|
+
fn.required_args = overloads.first.arglist.num_required
|
331
|
+
|
332
|
+
# If there is a rest arg, it will appear after all the required and
|
333
|
+
# optional arguments.
|
334
|
+
fn.splat_index = overloads.last.arglist.num_total if overloads.last.arglist.rest_arg
|
335
|
+
|
336
|
+
g.push_const :Kernel
|
337
|
+
g.create_block fn
|
338
|
+
g.send_with_block :lambda, 0
|
339
|
+
g.set_local fn_scope.self_reference.slot if fn_name
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|