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,60 @@
1
+ module Apricot
2
+ class Compiler
3
+ class Generator < Rubinius::Compiler::Stage
4
+ stage :apricot_bytecode
5
+ next_stage Rubinius::Compiler::Encoder
6
+
7
+ def initialize(compiler, last)
8
+ super
9
+ compiler.generator = self
10
+ end
11
+
12
+ def run
13
+ @output = Apricot::Generator.new
14
+ @input.bytecode @output
15
+ @output.close
16
+ run_next
17
+ end
18
+ end
19
+
20
+ class Parser < Rubinius::Compiler::Stage
21
+ def initialize(compiler, last)
22
+ super
23
+ compiler.parser = self
24
+ end
25
+
26
+ def run
27
+ @output = parse
28
+ run_next
29
+ end
30
+ end
31
+
32
+ class FileParser < Parser
33
+ stage :apricot_file
34
+ next_stage Generator
35
+
36
+ def input(file)
37
+ @file = file
38
+ end
39
+
40
+ def parse
41
+ Apricot::Parser.parse_file(@file)
42
+ end
43
+ end
44
+
45
+ class StringParser < Parser
46
+ stage :apricot_string
47
+ next_stage Generator
48
+
49
+ def input(code, file = "(none)", line = 1)
50
+ @input = code
51
+ @file = file
52
+ @line = line
53
+ end
54
+
55
+ def parse
56
+ Apricot::Parser.parse_string(@input, @file, @line)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,3 @@
1
+ module Apricot
2
+ VERSION = '0.0.1'
3
+ end
data/lib/apricot.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'set'
2
+
3
+ %w[
4
+ version misc parser compiler ast macroexpand generator stages printers
5
+ special_forms errors seq cons list identifier ruby_ext namespace
6
+ ].each {|r| require "apricot/#{r}" }
7
+
8
+ # Start "booting" apricot. Set up core namespace and load the core library.
9
+ module Apricot
10
+ Core = Namespace.new
11
+
12
+ Core.set_var(:"*ns*", Core)
13
+
14
+ Core.set_var(:"in-ns", lambda do |constant|
15
+ Apricot.current_namespace = Namespace.find_or_create constant
16
+ end)
17
+
18
+ Core.set_var(:ns, lambda do |constant|
19
+ List[Identifier.intern(:"in-ns"),
20
+ List[Identifier.intern(:quote), constant]]
21
+ end)
22
+ Core.get_var(:ns).apricot_meta = {macro: true}
23
+
24
+ # TODO: add and use a proper code loader
25
+ Apricot::Compiler.compile(File.expand_path('../../kernel/core.apr', __FILE__))
26
+
27
+ # ::User = Namespace.new
28
+ Apricot.current_namespace = Core
29
+ # TODO: make Apricot::Core public vars visible in User, default to User
30
+ end
@@ -0,0 +1,499 @@
1
+ describe 'Apricot' do
2
+ def apricot(code)
3
+ Apricot::Compiler.eval code
4
+ end
5
+
6
+ def bad_apricot(code)
7
+ expect { apricot(code) }.to raise_error(CompileError)
8
+ end
9
+
10
+ it 'compiles an empty program' do
11
+ apricot('').should == nil
12
+ end
13
+
14
+ it 'compiles false, true and nil' do
15
+ apricot('true').should == true
16
+ apricot('false').should == false
17
+ apricot('nil').should == nil
18
+ end
19
+
20
+ it 'compiles numbers' do
21
+ apricot('1').should == 1
22
+ apricot('1.0').should == 1.0
23
+ apricot('1/3').should == Rational(1, 3)
24
+ end
25
+
26
+ it 'compiles symbols' do
27
+ apricot(':foo').should == :foo
28
+ end
29
+
30
+ it 'compiles strings' do
31
+ apricot('"foo"').should == "foo"
32
+ end
33
+
34
+ it 'compiles regexes' do
35
+ apricot('#r"foo"').should == /foo/
36
+ apricot('#r/foo/x').should == /foo/x
37
+ apricot('#r[foo]xim').should == /foo/xim
38
+ end
39
+
40
+ it 'compiles arrays' do
41
+ apricot('[]').should == []
42
+ apricot('[1 2 3]').should == [1, 2, 3]
43
+ end
44
+
45
+ it 'compiles hashes' do
46
+ apricot('{}').should == {}
47
+ apricot('{:foo 1, :bar 2}').should == {:foo => 1, :bar => 2}
48
+ end
49
+
50
+ it 'compiles sets' do
51
+ apricot('#{}').should == Set.new
52
+ apricot('#{:foo :foo :bar}').should == Set[:foo, :foo, :bar]
53
+ end
54
+
55
+ it 'compiles constants' do
56
+ apricot('Array').should == Array
57
+ apricot('Rubinius::Compiler').should == Rubinius::Compiler
58
+ end
59
+
60
+ it 'compiles call forms with data structures' do
61
+ apricot('([:a :b] 1)').should == :b
62
+ apricot('([:a :b] 3)').should == nil
63
+ apricot('(#{:a :b} :b)').should == :b
64
+ apricot('(#{:a :b} :c)').should == nil
65
+ apricot('({:a 1} :a)').should == 1
66
+ apricot('({:a 1} :b)').should == nil
67
+ end
68
+
69
+ it 'compiles symbol call forms' do
70
+ apricot('(:a {:a 1 :b 2})').should == 1
71
+ apricot('(:c {:a 1 :b 2})').should == nil
72
+ apricot('(:a #{:a :b})').should == :a
73
+ apricot('(:c #{:a :b})').should == nil
74
+ end
75
+
76
+ it 'compiles send forms' do
77
+ apricot('(. 1 class)').should == Fixnum
78
+ apricot('(. 1 (class))').should == Fixnum
79
+ apricot('(. "foo" append "bar")').should == "foobar"
80
+ apricot('(. "foo" (append "bar"))').should == "foobar"
81
+ end
82
+
83
+ it 'compiles send forms with block args' do
84
+ apricot('(. [1 2 3] map | :to_s)').should == ['1', '2', '3']
85
+ apricot('(. [1 2 3] (map | :to_s))').should == ['1', '2', '3']
86
+ apricot('(. [1 2 3] map | #(. % + 1))').should == [2, 3, 4]
87
+ apricot('(. [1 2 3] (map | #(. % + 1)))').should == [2, 3, 4]
88
+ end
89
+
90
+ it 'compiles shorthand send forms' do
91
+ apricot('(.class 1)').should == Fixnum
92
+ apricot('(.append "foo" "bar")').should == "foobar"
93
+ end
94
+
95
+ it 'compiles shorthand send forms with block args' do
96
+ apricot('(.map [1 2 3] | :to_s)').should == ['1', '2', '3']
97
+ apricot('(.map [1 2 3] | #(. % + 1))').should == [2, 3, 4]
98
+ end
99
+
100
+ it 'macroexpands shorthand send forms' do
101
+ form = apricot "'(.meth recv arg1 arg2)"
102
+ ex = Apricot.macroexpand(form)
103
+
104
+ dot = Identifier.intern(:'.')
105
+ recv = Identifier.intern(:recv)
106
+ meth = Identifier.intern(:meth)
107
+ arg1 = Identifier.intern(:arg1)
108
+ arg2 = Identifier.intern(:arg2)
109
+ ex.should == List[dot, recv, meth, arg1, arg2]
110
+ end
111
+
112
+ it 'compiles shorthand new forms' do
113
+ apricot('(Range. 1 10)').should == (1..10)
114
+ apricot('(Array. 2 5)').should == [5, 5]
115
+ end
116
+
117
+ it 'compiles shorthand new forms with block args' do
118
+ apricot('(Array. 3 | :to_s)').should == ["0", "1", "2"]
119
+ apricot('(Array. 5 | #(* % %))').should == [0, 1, 4, 9, 16]
120
+ end
121
+
122
+ it 'macroexpands shorthand new forms' do
123
+ form = apricot "'(Klass. arg1 arg2)"
124
+ ex = Apricot.macroexpand(form)
125
+
126
+ dot = Identifier.intern(:'.')
127
+ klass = Identifier.intern(:Klass)
128
+ new = Identifier.intern(:new)
129
+ arg1 = Identifier.intern(:arg1)
130
+ arg2 = Identifier.intern(:arg2)
131
+ ex.should == List[dot, klass, new, arg1, arg2]
132
+ end
133
+
134
+ it 'compiles constant defs' do
135
+ expect { Foo }.to raise_error(NameError)
136
+ apricot '(def Foo 1)'
137
+ Foo.should == 1
138
+ end
139
+
140
+ it 'compiles if forms' do
141
+ apricot('(if true :foo :bar)').should == :foo
142
+ apricot('(if false :foo :bar)').should == :bar
143
+ apricot('(if true :foo)').should == :foo
144
+ apricot('(if false :foo)').should == nil
145
+ end
146
+
147
+ it 'compiles do forms' do
148
+ apricot('(do)').should == nil
149
+ apricot('(do 1)').should == 1
150
+ apricot('(do 1 2 3)').should == 3
151
+ expect { Bar }.to raise_error(NameError)
152
+ apricot('(do (def Bar 1) Bar)').should == 1
153
+ end
154
+
155
+ it 'compiles let forms' do
156
+ apricot('(let [])').should == nil
157
+ apricot('(let [a 1])').should == nil
158
+ apricot('(let [a 1] a)').should == 1
159
+ apricot('(let [a 1 b 2] [b a])').should == [2, 1]
160
+ apricot('(let [a 1] [(let [a 2] a) a])').should == [2, 1]
161
+ apricot('(let [a 1 b 2] (let [a 42] [b a]))').should == [2, 42]
162
+ apricot('(let [a 1 b a] [a b])').should == [1, 1]
163
+ apricot('(let [a 1] (let [b a] [a b]))').should == [1, 1]
164
+ end
165
+
166
+ it 'compiles fn forms' do
167
+ apricot('((fn []))').should == nil
168
+ apricot('((fn [] 42))').should == 42
169
+ apricot('((fn [x] x) 42)').should == 42
170
+ apricot('((fn [x y] [y x]) 1 2)').should == [2, 1]
171
+ end
172
+
173
+ it 'compiles fn forms with optional arguments' do
174
+ apricot('((fn [[x 42]] x))').should == 42
175
+ apricot('((fn [[x 42]] x) 0)').should == 0
176
+ apricot('((fn [x [y 2]] [x y]) 1)').should == [1, 2]
177
+ apricot('((fn [x [y 2]] [x y]) 3 4)').should == [3, 4]
178
+ apricot('((fn [[x 1] [y 2]] [x y]))').should == [1, 2]
179
+ apricot('((fn [[x 1] [y 2]] [x y]) 3)').should == [3, 2]
180
+ apricot('((fn [[x 1] [y 2]] [x y]) 3 4)').should == [3, 4]
181
+ end
182
+
183
+ it 'compiles fn forms with splat arguments' do
184
+ apricot('((fn [& x] x))').should == []
185
+ apricot('((fn [& x] x) 1)').should == [1]
186
+ apricot('((fn [& x] x) 1 2)').should == [1, 2]
187
+ apricot('((fn [x & y] y) 1)').should == []
188
+ apricot('((fn [x & y] y) 1 2 3)').should == [2, 3]
189
+ end
190
+
191
+ it 'compiles fn forms with optional and splat arguments' do
192
+ apricot('((fn [x [y 2] & z] [x y z]) 1)').should == [1, 2, []]
193
+ apricot('((fn [x [y 2] & z] [x y z]) 1 3)').should == [1, 3, []]
194
+ apricot('((fn [x [y 2] & z] [x y z]) 1 3 4 5)').should == [1, 3, [4, 5]]
195
+ end
196
+
197
+ it 'compiles fn forms with block arguments' do
198
+ apricot('((fn [| block] block))').should == nil
199
+ apricot('(.call (fn [| block] (block)) | (fn [] 42))').should == 42
200
+
201
+ fn = apricot '(fn [x | block] (block x))'
202
+ # Without passing a block, 'block' is nil.
203
+ expect { fn.call(2) }.to raise_error(NoMethodError)
204
+ fn.call(2) {|x| x + 40 }.should == 42
205
+
206
+ reduce_args = apricot <<-CODE
207
+ (fn reduce-args
208
+ ([x] x)
209
+ ([x y | f] (f x y))
210
+ ([x y & more | f]
211
+ (recur (f x y) (first more) (next more))))
212
+ CODE
213
+
214
+ reduce_args.call(1).should == 1
215
+ reduce_args.call(40, 2) {|x,y| x * y }.should == 80
216
+ reduce_args.call(1,2,3,4,5,6) {|x,y| x + y }.should == 21
217
+ end
218
+
219
+ it 'does not compile invalid fn forms' do
220
+ bad_apricot '(fn :foo)'
221
+ bad_apricot '(fn [1])'
222
+ bad_apricot '(fn [[x 1] y])'
223
+ bad_apricot '(fn [[1 1]])'
224
+ bad_apricot '(fn [[x]])'
225
+ bad_apricot '(fn [&])'
226
+ bad_apricot '(fn [& x y])'
227
+ bad_apricot '(fn [x x])'
228
+ bad_apricot '(fn [x & rest1 & rest2])'
229
+ bad_apricot '(fn [a b x c d x e f])'
230
+ bad_apricot '(fn [a x b [x 1]])'
231
+ bad_apricot '(fn [a b x c d & x])'
232
+ bad_apricot '(fn [a b c [x 1] [y 2] [x 3]])'
233
+ bad_apricot '(fn [a b [x 1] & x])'
234
+ bad_apricot '(fn [|])'
235
+ bad_apricot '(fn [| &])'
236
+ bad_apricot '(fn [| & a])'
237
+ bad_apricot '(fn [| a &])'
238
+ bad_apricot '(fn [& x |])'
239
+ bad_apricot '(fn [| x y])'
240
+ bad_apricot '(fn [| x & y])'
241
+ bad_apricot '(fn [x | x])'
242
+ bad_apricot '(fn [x | b1 | b2])'
243
+ end
244
+
245
+ it 'compiles arity-overloaded fn forms' do
246
+ apricot('((fn ([] 0)))') == 0
247
+ apricot('((fn ([x] x)) 42)') == 42
248
+ apricot('((fn ([[x 42]] x)))') == 42
249
+ apricot('((fn ([& rest] rest)) 1 2 3)') == [1, 2, 3]
250
+ apricot('((fn ([] 0) ([x] x)))') == 0
251
+ apricot('((fn ([] 0) ([x] x)) 42)') == 42
252
+ apricot('((fn ([x] x) ([x y] y)) 42)') == 42
253
+ apricot('((fn ([x] x) ([x y] y)) 42 13)') == 13
254
+
255
+ add_fn = apricot <<-CODE
256
+ (fn
257
+ ([] 0)
258
+ ([x] x)
259
+ ([x y] (.+ x y))
260
+ ([x y & more]
261
+ (.reduce more (.+ x y) :+)))
262
+ CODE
263
+
264
+ add_fn.call.should == 0
265
+ add_fn.call(42).should == 42
266
+ add_fn.call(1,2).should == 3
267
+ add_fn.call(1,2,3).should == 6
268
+ add_fn.call(1,2,3,4,5,6,7,8).should == 36
269
+
270
+ two_or_three = apricot '(fn ([x y] 2) ([x y z] 3))'
271
+ expect { two_or_three.call }.to raise_error(ArgumentError)
272
+ expect { two_or_three.call(1) }.to raise_error(ArgumentError)
273
+ two_or_three.call(1,2).should == 2
274
+ two_or_three.call(1,2,3).should == 3
275
+ expect { two_or_three.call(1,2,3,4) }.to raise_error(ArgumentError)
276
+ expect { two_or_three.call(1,2,3,4,5) }.to raise_error(ArgumentError)
277
+ end
278
+
279
+ it 'compiles arity-overloaded fns with no matching overloads for some arities' do
280
+ zero_or_two = apricot '(fn ([] 0) ([x y] 2))'
281
+ zero_or_two.call.should == 0
282
+ expect { zero_or_two.call(1) }.to raise_error(ArgumentError)
283
+ zero_or_two.call(1,2).should == 2
284
+ expect { zero_or_two.call(1,2,3) }.to raise_error(ArgumentError)
285
+
286
+ one_or_four = apricot '(fn ([w] 1) ([w x y z] 4))'
287
+ expect { one_or_four.call }.to raise_error(ArgumentError)
288
+ one_or_four.call(1).should == 1
289
+ expect { one_or_four.call(1,2) }.to raise_error(ArgumentError)
290
+ expect { one_or_four.call(1,2,3) }.to raise_error(ArgumentError)
291
+ one_or_four.call(1,2,3,4).should == 4
292
+ expect { one_or_four.call(1,2,3,4,5) }.to raise_error(ArgumentError)
293
+ end
294
+
295
+ it 'does not compile invalid arity-overloaded fn forms' do
296
+ bad_apricot '(fn ([] 1) :foo)'
297
+ bad_apricot '(fn ([] 1) ([] 2))'
298
+ bad_apricot '(fn ([[o 1]] 1) ([] 2))'
299
+ bad_apricot '(fn ([] 1) ([[o 2]] 2))'
300
+ bad_apricot '(fn ([[o 1]] 1) ([[o 2]] 2))'
301
+ bad_apricot '(fn ([x [o 1]] 1) ([x] 2))'
302
+ bad_apricot '(fn ([x [o 1]] 1) ([[o 2]] 2))'
303
+ bad_apricot '(fn ([x y z [o 1]] 1) ([x y z & rest] 2))'
304
+ bad_apricot '(fn ([x [o 1] [p 2] [q 3]] 1) ([x y z] 2))'
305
+ bad_apricot '(fn ([x & rest] 1) ([x y] 2))'
306
+ bad_apricot '(fn ([x & rest] 1) ([x [o 1]] 2))'
307
+ bad_apricot '(fn ([x [o 1] & rest] 1) ([x] 2))'
308
+ end
309
+
310
+ it 'compiles fn forms with self-reference' do
311
+ foo = apricot '(fn foo [] foo)'
312
+ foo.call.should == foo
313
+
314
+ # This one will stack overflow from the infinite loop.
315
+ expect { apricot '((fn foo [] (foo)))' }.to raise_error(SystemStackError)
316
+
317
+ add = apricot <<-CODE
318
+ (fn add
319
+ ([] 0)
320
+ ([& args]
321
+ (.+ (first args) (apply add (rest args)))))
322
+ CODE
323
+
324
+ add.call.should == 0
325
+ add.call(1).should == 1
326
+ add.call(1,2,3).should == 6
327
+ end
328
+
329
+ it 'compiles loop and recur forms' do
330
+ apricot('(loop [])').should == nil
331
+ apricot('(loop [a 1])').should == nil
332
+ apricot('(loop [a 1] a)').should == 1
333
+
334
+ apricot(<<-CODE).should == [5,4,3,2,1]
335
+ (let [a []]
336
+ (loop [x 5]
337
+ (if (. x > 0)
338
+ (do
339
+ (. a << x)
340
+ (recur (. x - 1)))))
341
+ a)
342
+ CODE
343
+ end
344
+
345
+ it 'compiles recur forms in fns' do
346
+ apricot(<<-CODE).should == 15
347
+ ((fn [x y]
348
+ (if (. x > 0)
349
+ (recur (. x - 1) (. y + x))
350
+ y))
351
+ 5 0)
352
+ CODE
353
+ end
354
+
355
+ it 'compiles recur forms in fns with optional arguments' do
356
+ apricot(<<-CODE).should == 150
357
+ ((fn [x y [mult 10]]
358
+ (if (. x > 0)
359
+ (recur (. x - 1) (. y + x) mult)
360
+ (* y mult)))
361
+ 5 0)
362
+ CODE
363
+
364
+ apricot(<<-CODE).should == 300
365
+ ((fn [x y [mult 10]]
366
+ (if (. x > 0)
367
+ (recur (. x - 1) (. y + x) mult)
368
+ (* y mult)))
369
+ 5 0 20)
370
+ CODE
371
+ end
372
+
373
+ it 'does not compile invalid recur forms' do
374
+ bad_apricot '(fn [] (recur 1))'
375
+ bad_apricot '(fn [x] (recur))'
376
+ bad_apricot '(fn [[x 10]] (recur))'
377
+ bad_apricot '(fn [x & rest] (recur 1))'
378
+ bad_apricot '(fn [x & rest] (recur 1 2 3))'
379
+ end
380
+
381
+ it 'compiles recur forms in arity-overloaded fns' do
382
+ apricot(<<-CODE).should == 0
383
+ ((fn
384
+ ([] 0)
385
+ ([& args] (recur [])))
386
+ 1 2 3)
387
+ CODE
388
+
389
+ apricot(<<-CODE).should == 0
390
+ ((fn
391
+ ([] 0)
392
+ ([& args] (recur (rest args))))
393
+ 1 2 3)
394
+ CODE
395
+
396
+ apricot(<<-CODE).should == 6
397
+ ((fn
398
+ ([x] x)
399
+ ([x & args] (recur (.+ x (first args)) (rest args))))
400
+ 1 2 3)
401
+ CODE
402
+
403
+ apricot(<<-CODE).should == 42
404
+ ((fn
405
+ ([] 0)
406
+ ([x y & args]
407
+ (if (.empty? args)
408
+ 42
409
+ (recur x y (rest args)))))
410
+ 1 2 3)
411
+ CODE
412
+ end
413
+
414
+ it 'compiles try forms' do
415
+ apricot('(try)').should == nil
416
+ apricot('(try :foo)').should == :foo
417
+
418
+ apricot('(try :success (rescue e :rescue))').should == :success
419
+ expect { apricot '(try (. Kernel raise))' }.to raise_error(RuntimeError)
420
+ apricot('(try (. Kernel raise) (rescue e :rescue))').should == :rescue
421
+ apricot('(try (. Kernel raise) (rescue [e] :rescue))').should == :rescue
422
+ apricot(<<-CODE).should == :rescue
423
+ (try
424
+ (. Kernel raise)
425
+ (rescue [e 1 2 RuntimeError] :rescue))
426
+ CODE
427
+ apricot(<<-CODE).should == :rescue_bar
428
+ (try
429
+ (. Kernel raise ArgumentError)
430
+ (rescue [e TypeError] :rescue_foo)
431
+ (rescue [e ArgumentError] :rescue_bar))
432
+ CODE
433
+ apricot(<<-CODE).should be_a(TypeError)
434
+ (try
435
+ (. Kernel raise TypeError)
436
+ (rescue e e))
437
+ CODE
438
+ expect { apricot(<<-CODE) }.to raise_error(TypeError)
439
+ (try
440
+ (. Kernel raise TypeError)
441
+ (rescue [e ArgumentError] :rescue))
442
+ CODE
443
+
444
+ apricot(<<-CODE).should == :rescue
445
+ (try
446
+ (try
447
+ (. Kernel raise)
448
+ (rescue e (. Kernel raise)))
449
+ (rescue e :rescue))
450
+ CODE
451
+
452
+ apricot(<<-CODE).should == []
453
+ (let [a [1]]
454
+ (try
455
+ :success
456
+ (ensure (.pop a)))
457
+ a)
458
+ CODE
459
+ apricot(<<-CODE).should == []
460
+ (let [a [1]]
461
+ (try
462
+ (. Kernel raise)
463
+ (rescue e :rescue)
464
+ (ensure (.pop a)))
465
+ a)
466
+ CODE
467
+ apricot(<<-CODE).should == []
468
+ (let [a [1]]
469
+ (try
470
+ (try
471
+ (. Kernel raise)
472
+ (ensure (.pop a)))
473
+ (rescue e :rescue))
474
+ a)
475
+ CODE
476
+ end
477
+
478
+ it 'compiles quoted forms' do
479
+ apricot("'1").should == 1
480
+ apricot("'a").should == Identifier.intern(:a)
481
+ apricot("''a").should == List[
482
+ Identifier.intern(:quote),
483
+ Identifier.intern(:a)
484
+ ]
485
+ apricot("'1.2").should == 1.2
486
+ apricot("'1/2").should == Rational(1,2)
487
+ apricot("':a").should == :a
488
+ apricot("'()").should == List::EmptyList
489
+ apricot("'(1)").should == List[1]
490
+ apricot("'[a]").should == [Identifier.intern(:a)]
491
+ apricot("'{a 1}").should == {Identifier.intern(:a) => 1}
492
+ apricot('\'"foo"').should == "foo"
493
+ apricot("'true").should == true
494
+ apricot("'false").should == false
495
+ apricot("'nil").should == nil
496
+ apricot("'self").should == Identifier.intern(:self)
497
+ apricot("'Foo::Bar").should == Identifier.intern(:'Foo::Bar')
498
+ end
499
+ end
@@ -0,0 +1,58 @@
1
+ describe Apricot::Identifier do
2
+ def intern(name)
3
+ described_class.intern name
4
+ end
5
+
6
+ it 'does not support .new' do
7
+ expect { described_class.new :foo }.to raise_error(NoMethodError)
8
+ end
9
+
10
+ it 'creates only one identifier for each name' do
11
+ id1 = intern :foo
12
+ id2 = intern :foo
13
+
14
+ id1.object_id.should == id2.object_id
15
+ end
16
+
17
+ it 'supports the == operator' do
18
+ id1 = intern :id1
19
+ id2 = intern :id1
20
+ id3 = intern :id3
21
+
22
+ id1.should == id2
23
+ id2.should == id1
24
+
25
+ id1.should_not == id3
26
+ id3.should_not == id1
27
+
28
+ id1.should_not == 42
29
+ end
30
+
31
+ it 'can be inspected' do
32
+ intern(:test).inspect.should == "test"
33
+ intern(:true).inspect.should == "#|true|"
34
+ intern(:false).inspect.should == "#|false|"
35
+ intern(:nil).inspect.should == "#|nil|"
36
+ intern(:"foo bar").inspect.should == "#|foo bar|"
37
+ intern(:"foo | bar").inspect.should == '#|foo \| bar|'
38
+ intern(:"foo\nbar").inspect.should == '#|foo\nbar|'
39
+ intern(:"test\n").inspect.should == '#|test\n|'
40
+ end
41
+
42
+ it 'can be used as a key in Hashes' do
43
+ id1 = intern :id1
44
+ id2 = intern :id1
45
+ id3 = intern :id3
46
+ h = {}
47
+
48
+ h[id1] = 1
49
+ h[id2] = 2
50
+ h[id3] = 3
51
+
52
+ h[id1].should == 2
53
+ h[id2].should == 2
54
+ h[id3].should == 3
55
+
56
+ h[:id1].should == nil
57
+ end
58
+ end