apricot 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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