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