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
@@ -0,0 +1,206 @@
1
+ describe 'Apricot' do
2
+ include CompilerSpec
3
+
4
+ it 'compiles fn forms' do
5
+ apr('((fn []))').should == nil
6
+ apr('((fn [] 42))').should == 42
7
+ apr('((fn [x] x) 42)').should == 42
8
+ apr('((fn [x y] [y x]) 1 2)').should == [2, 1]
9
+ end
10
+
11
+ it 'compiles fn forms with optional arguments' do
12
+ apr('((fn [? (x 42)] x))').should == 42
13
+ apr('((fn [? (x 42)] x) 0)').should == 0
14
+ apr('((fn [x ? (y 2)] [x y]) 1)').should == [1, 2]
15
+ apr('((fn [x ? (y 2)] [x y]) 3 4)').should == [3, 4]
16
+ apr('((fn [? (x 1) (y 2)] [x y]))').should == [1, 2]
17
+ apr('((fn [? (x 1) (y 2)] [x y]) 3)').should == [3, 2]
18
+ apr('((fn [? (x 1) (y 2)] [x y]) 3 4)').should == [3, 4]
19
+ end
20
+
21
+ it 'compiles fn forms with splat arguments' do
22
+ apr('((fn [& x] x))').should == []
23
+ apr('((fn [& x] x) 1)').should == [1]
24
+ apr('((fn [& x] x) 1 2)').should == [1, 2]
25
+ apr('((fn [x & y] y) 1)').should == []
26
+ apr('((fn [x & y] y) 1 2 3)').should == [2, 3]
27
+ end
28
+
29
+ it 'compiles fn forms with optional and splat arguments' do
30
+ apr('((fn [x ? (y 2) & z] [x y z]) 1)').should == [1, 2, []]
31
+ apr('((fn [x ? (y 2) & z] [x y z]) 1 3)').should == [1, 3, []]
32
+ apr('((fn [x ? (y 2) & z] [x y z]) 1 3 4 5)').should == [1, 3, [4, 5]]
33
+ end
34
+
35
+ it 'compiles fn forms with block arguments' do
36
+ apr('((fn [| block] block))').should == nil
37
+ apr('(.call (fn [| block] (block)) | (fn [] 42))').should == 42
38
+
39
+ fn = apr '(fn [x | block] (block x))'
40
+ # Without passing a block, 'block' is nil.
41
+ expect { fn.call(2) }.to raise_error(NoMethodError)
42
+ fn.call(2) {|x| x + 40 }.should == 42
43
+
44
+ reduce_args = apr <<-CODE
45
+ (fn reduce-args
46
+ ([x] x)
47
+ ([x y | f] (f x y))
48
+ ([x y & more | f]
49
+ (if (seq more)
50
+ (recur (f x y) (first more) (next more) f)
51
+ (f x y))))
52
+ CODE
53
+
54
+ reduce_args.call(1).should == 1
55
+ reduce_args.call(40, 2) {|x,y| x * y }.should == 80
56
+ reduce_args.call(1,2,3,4,5,6) {|x,y| x + y }.should == 21
57
+ end
58
+
59
+ it 'does not compile invalid fn forms' do
60
+ bad_apr '(fn :foo)'
61
+ bad_apr '(fn [1])'
62
+ bad_apr '(fn [?])'
63
+ bad_apr '(fn [? (x 1) y])'
64
+ bad_apr '(fn [? (1 1)])'
65
+ bad_apr '(fn [? (x)])'
66
+ bad_apr '(fn [&])'
67
+ bad_apr '(fn [? &])'
68
+ bad_apr '(fn [& ?])'
69
+ bad_apr '(fn [& rest ? (opt 1)])'
70
+ bad_apr '(fn [& x y])'
71
+ bad_apr '(fn [x x])'
72
+ bad_apr '(fn [x & rest1 & rest2])'
73
+ bad_apr '(fn [a b x c d x e f])'
74
+ bad_apr '(fn [a x b ? (x 1)])'
75
+ bad_apr '(fn [a b x c d & x])'
76
+ bad_apr '(fn [a b c ? (x 1) (y 2) (x 3)])'
77
+ bad_apr '(fn [a b ? (x 1) & x])'
78
+ bad_apr '(fn [|])'
79
+ bad_apr '(fn [? |])'
80
+ bad_apr '(fn [| ?])'
81
+ bad_apr '(fn [| block ? (opt 1)])'
82
+ bad_apr '(fn [| &])'
83
+ bad_apr '(fn [| & a])'
84
+ bad_apr '(fn [| a &])'
85
+ bad_apr '(fn [& x |])'
86
+ bad_apr '(fn [| x y])'
87
+ bad_apr '(fn [| x & y])'
88
+ bad_apr '(fn [x | x])'
89
+ bad_apr '(fn [x | b1 | b2])'
90
+ end
91
+
92
+ it 'compiles arity-overloaded fn forms' do
93
+ apr('((fn ([] 0)))').should == 0
94
+ apr('((fn ([x] x)) 42)').should == 42
95
+ apr('((fn ([? (x 42)] x)))').should == 42
96
+ apr('((fn ([& rest] rest)) 1 2 3)').should == [1, 2, 3]
97
+ apr('((fn ([] 0) ([x] x)))').should == 0
98
+ apr('((fn ([] 0) ([x] x)) 42)').should == 42
99
+ apr('((fn ([x] x) ([x y] y)) 42)').should == 42
100
+ apr('((fn ([x] x) ([x y] y)) 42 13)').should == 13
101
+ apr('((fn ([x] x) ([x y & z] z)) 1 2 3 4)').should == [3, 4]
102
+
103
+ add_fn = apr <<-CODE
104
+ (fn
105
+ ([] 0)
106
+ ([x] x)
107
+ ([x y] (.+ x y))
108
+ ([x y & more]
109
+ (.reduce more (.+ x y) :+)))
110
+ CODE
111
+
112
+ add_fn.call.should == 0
113
+ add_fn.call(42).should == 42
114
+ add_fn.call(1,2).should == 3
115
+ add_fn.call(1,2,3).should == 6
116
+ add_fn.call(1,2,3,4,5,6,7,8).should == 36
117
+
118
+ two_or_three = apr '(fn ([x y] 2) ([x y z] 3))'
119
+ expect { two_or_three.call }.to raise_error(ArgumentError)
120
+ expect { two_or_three.call(1) }.to raise_error(ArgumentError)
121
+ two_or_three.call(1,2).should == 2
122
+ two_or_three.call(1,2,3).should == 3
123
+ expect { two_or_three.call(1,2,3,4) }.to raise_error(ArgumentError)
124
+ expect { two_or_three.call(1,2,3,4,5) }.to raise_error(ArgumentError)
125
+ end
126
+
127
+ it 'compiles arity-overloaded fns with no matching overloads for some arities' do
128
+ zero_or_two = apr '(fn ([] 0) ([x y] 2))'
129
+ zero_or_two.call.should == 0
130
+ expect { zero_or_two.call(1) }.to raise_error(ArgumentError)
131
+ zero_or_two.call(1,2).should == 2
132
+ expect { zero_or_two.call(1,2,3) }.to raise_error(ArgumentError)
133
+
134
+ one_or_four = apr '(fn ([w] 1) ([w x y z] 4))'
135
+ expect { one_or_four.call }.to raise_error(ArgumentError)
136
+ one_or_four.call(1).should == 1
137
+ expect { one_or_four.call(1,2) }.to raise_error(ArgumentError)
138
+ expect { one_or_four.call(1,2,3) }.to raise_error(ArgumentError)
139
+ one_or_four.call(1,2,3,4).should == 4
140
+ expect { one_or_four.call(1,2,3,4,5) }.to raise_error(ArgumentError)
141
+ end
142
+
143
+ it 'does not compile invalid arity-overloaded fn forms' do
144
+ bad_apr '(fn ([] 1) :foo)'
145
+ bad_apr '(fn ([] 1) ([] 2))'
146
+ bad_apr '(fn ([? (o 1)] 1) ([] 2))'
147
+ bad_apr '(fn ([] 1) ([? (o 2)] 2))'
148
+ bad_apr '(fn ([? (o 1)] 1) ([? (o 2)] 2))'
149
+ bad_apr '(fn ([x ? (o 1)] 1) ([x] 2))'
150
+ bad_apr '(fn ([x ? (o 1)] 1) ([? (o 2)] 2))'
151
+ bad_apr '(fn ([x y z ? (o 1)] 1) ([x y z & rest] 2))'
152
+ bad_apr '(fn ([x ? (o 1) (p 2) (q 3)] 1) ([x y z] 2))'
153
+ bad_apr '(fn ([x & rest] 1) ([x y] 2))'
154
+ bad_apr '(fn ([x & rest] 1) ([x ? (o 1)] 2))'
155
+ bad_apr '(fn ([x ? (o 1) & rest] 1) ([x] 2))'
156
+ bad_apr '(fn ([? (x 1) (y 2)] 3) ([x & y] 4))'
157
+ end
158
+
159
+ it 'compiles fn forms with self-reference' do
160
+ foo = apr '(fn foo [] foo)'
161
+ foo.call.should == foo
162
+
163
+ # This one will stack overflow from the infinite loop.
164
+ expect { apr '((fn foo [] (foo)))' }.to raise_error(SystemStackError)
165
+
166
+ add = apr <<-CODE
167
+ (fn add
168
+ ([] 0)
169
+ ([& args]
170
+ (.+ (first args) (apply add (rest args)))))
171
+ CODE
172
+
173
+ add.call.should == 0
174
+ add.call(1).should == 1
175
+ add.call(1,2,3).should == 6
176
+ end
177
+
178
+ it 'compiles recur forms in fns' do
179
+ apr(<<-CODE).should == 15
180
+ ((fn [x y]
181
+ (if (. x > 0)
182
+ (recur (. x - 1) (. y + x))
183
+ y))
184
+ 5 0)
185
+ CODE
186
+ end
187
+
188
+ it 'compiles recur forms in fns with optional arguments' do
189
+ apr(<<-CODE).should == 150
190
+ ((fn [x y ? (mult 10)]
191
+ (if (. x > 0)
192
+ (recur (. x - 1) (. y + x) mult)
193
+ (* y mult)))
194
+ 5 0)
195
+ CODE
196
+
197
+ apr(<<-CODE).should == 300
198
+ ((fn [x y ? (mult 10)]
199
+ (if (. x > 0)
200
+ (recur (. x - 1) (. y + x) mult)
201
+ (* y mult)))
202
+ 5 0 20)
203
+ CODE
204
+ end
205
+
206
+ end
@@ -4,7 +4,7 @@ describe Apricot::List do
4
4
  end
5
5
 
6
6
  def empty_list
7
- described_class::EmptyList
7
+ described_class::EMPTY_LIST
8
8
  end
9
9
 
10
10
  it 'has a single instance of the empty list' do
@@ -0,0 +1,349 @@
1
+ describe Apricot::Reader do
2
+ def read(s)
3
+ @forms = described_class.read_string(s, "(spec)")
4
+ @first = @forms.first
5
+ @forms
6
+ end
7
+
8
+ def read_one(s, klass = nil)
9
+ read(s).length.should == 1
10
+ @first.should be_a(klass) if klass
11
+ @first
12
+ end
13
+
14
+ def expect_syntax_error(s)
15
+ expect { read(s) }.to raise_error(Apricot::SyntaxError)
16
+ end
17
+
18
+ it 'reads nothing' do
19
+ read('').should be_empty
20
+ end
21
+
22
+ it 'skips whitespace' do
23
+ read(" \n\t,").should be_empty
24
+ end
25
+
26
+ it 'skips comments' do
27
+ read('; example').should be_empty
28
+ read('#!/usr/bin/env apricot').should be_empty
29
+ end
30
+
31
+ it 'discards commented forms' do
32
+ read('#_form').should be_empty
33
+ end
34
+
35
+ it 'reads identifiers' do
36
+ read_one('example', Identifier)
37
+ @first.name.should == :example
38
+ end
39
+
40
+ it 'reads pipe identifiers' do
41
+ read_one('#|example|').should == Identifier.intern(:example)
42
+ read_one('#|foo bar|').should == Identifier.intern(:"foo bar")
43
+ read_one('#|foo\nbar|')
44
+ @first.should == Identifier.intern(:"foo\nbar")
45
+ read_one('#|foo\|bar|').should == Identifier.intern(:"foo|bar")
46
+ read_one('#|foo"bar|').should == Identifier.intern(:'foo"bar')
47
+ end
48
+
49
+ it 'does not read incomplete pipe identifiers' do
50
+ expect_syntax_error '#|foo'
51
+ end
52
+
53
+ it 'reads constants' do
54
+ read_one('Example', Identifier)
55
+ @first.constant?.should be_true
56
+ @first.const_names.should == [:Example]
57
+ end
58
+
59
+ it 'reads invalid constants as identifiers' do
60
+ read_one('Fo$o', Identifier)
61
+ @first.constant?.should be_false
62
+ end
63
+
64
+ it 'reads scoped constants' do
65
+ read_one('Foo::Bar::Baz', Identifier)
66
+ @first.constant?.should be_true
67
+ @first.const_names.should == [:Foo, :Bar, :Baz]
68
+ end
69
+
70
+ it 'reads invalid scoped constants as identifiers' do
71
+ read_one('Foo::', Identifier)
72
+ @first.constant?.should be_false
73
+ read_one('Foo:', Identifier)
74
+ @first.constant?.should be_false
75
+ read_one('Foo::a', Identifier)
76
+ @first.constant?.should be_false
77
+ read_one('Foo::::Bar', Identifier)
78
+ @first.constant?.should be_false
79
+ end
80
+
81
+ it 'reads true, false, nil, and self' do
82
+ read('true false nil self').length.should == 4
83
+ @forms[0].should == true
84
+ @forms[1].should == false
85
+ @forms[2].should == nil
86
+ @forms[3].should == Identifier.intern(:self)
87
+ end
88
+
89
+ it 'reads fixnums' do
90
+ read_one('123').should == 123
91
+ read_one('-123').should == -123
92
+ read_one('+123').should == 123
93
+ end
94
+
95
+ it 'reads bignums' do
96
+ read_one('12345678901234567890').should == 12345678901234567890
97
+ end
98
+
99
+ it 'reads radix integers' do
100
+ read_one('2r10').should == 2
101
+ end
102
+
103
+ it 'reads floats' do
104
+ read_one('1.23').should == 1.23
105
+ end
106
+
107
+ it 'reads rationals' do
108
+ read_one('12/34').should == Rational(12, 34)
109
+ end
110
+
111
+ it 'does not read invalid numbers' do
112
+ expect_syntax_error '12abc'
113
+ end
114
+
115
+ it 'reads empty strings' do
116
+ read_one('""', String).should == ''
117
+ end
118
+
119
+ it 'reads strings' do
120
+ read_one('"Hello, world!"').should == 'Hello, world!'
121
+ end
122
+
123
+ it 'reads multiline strings' do
124
+ read_one(%{"This is\na test"}).should == "This is\na test"
125
+ end
126
+
127
+ it 'does not read unfinished strings' do
128
+ expect_syntax_error '"'
129
+ end
130
+
131
+ it 'reads strings with character escapes' do
132
+ read_one('"\\a\\b\\t\\n\\v\\f\\r\\e\\"\\\\"').should == "\a\b\t\n\v\f\r\e\"\\"
133
+ end
134
+
135
+ it 'reads strings with octal escapes' do
136
+ read_one('"\\1\\01\\001"').should == "\001\001\001"
137
+ end
138
+
139
+ it 'reads strings with hex escapes' do
140
+ read_one('"\\x1\\x01"').should == "\001\001"
141
+ end
142
+
143
+ it 'does not read strings with invalid hex escapes' do
144
+ expect_syntax_error '"\\x"'
145
+ end
146
+
147
+ it 'stops parsing hex/octal escapes in strings at non-hex/octal digits' do
148
+ read_one('"\xAZ\082"').should == "\x0AZ\00082"
149
+ end
150
+
151
+ it 'reads #q quotation strings' do
152
+ read_one('#q{foo}').should == 'foo'
153
+ read_one('#q{\n}').should == '\n'
154
+ read_one('#Q{\n}').should == "\n"
155
+ read_one('#q{\\\\}').should == '\\'
156
+ read_one('#q{\\}}').should == '}'
157
+ end
158
+
159
+ it 'does not read incomplete #q quotation strings' do
160
+ expect_syntax_error '#q{'
161
+ end
162
+
163
+ it 'reads regexes' do
164
+ read_one('#r!!').should == //
165
+ read_one('#r!egex!').should == /egex/
166
+ read_one('#r(egex)').should == /egex/
167
+ read_one('#r[egex]').should == /egex/
168
+ read_one('#r{egex}').should == /egex/
169
+ read_one('#r<egex>').should == /egex/
170
+ read_one('#r!\!!').should == /!/
171
+ read_one('#r!foo\bar!').should == /foo\bar/
172
+ read_one('#r!\\\\!').should == /\\/
173
+ end
174
+
175
+ it 'reads regexes with trailing options' do
176
+ read_one('#r//i', Regexp)
177
+ @first.options.should == Regexp::IGNORECASE
178
+ read_one('#r/foo/x', Regexp)
179
+ @first.options.should == Regexp::EXTENDED
180
+ read_one('#r//im', Regexp)
181
+ @first.options.should == Regexp::IGNORECASE | Regexp::MULTILINE
182
+ end
183
+
184
+ it 'does not read regexes with unknown trailing options' do
185
+ expect_syntax_error '#r/foo/asdf'
186
+ end
187
+
188
+ it 'does not read incomplete regexes' do
189
+ expect_syntax_error '#r/foo'
190
+ end
191
+
192
+ it 'reads symbols' do
193
+ read_one(':example').should == :example
194
+ end
195
+
196
+ it 'reads quoted symbols' do
197
+ read_one(':"\x01()"').should == :"\x01()"
198
+ end
199
+
200
+ it 'does not read unfinished quoted symbols' do
201
+ expect_syntax_error ':"'
202
+ end
203
+
204
+ it 'does not read empty symbols' do
205
+ expect_syntax_error ':'
206
+ end
207
+
208
+ it 'does read empty quoted symbols' do
209
+ read_one(':""').should == :""
210
+ end
211
+
212
+ it 'reads empty lists' do
213
+ read_one('()').should == List[]
214
+ end
215
+
216
+ it 'reads lists' do
217
+ read_one('(1 two)').should == List[1, Identifier.intern(:two)]
218
+ end
219
+
220
+ it 'reads empty arrays' do
221
+ read_one('[]').should == []
222
+ end
223
+
224
+ it 'reads arrays' do
225
+ read_one('[1 two]').should == [1, Identifier.intern(:two)]
226
+ end
227
+
228
+ it 'reads empty hashes' do
229
+ read_one('{}').should == {}
230
+ end
231
+
232
+ it 'reads hashes' do
233
+ read_one('{:example 1}').should == {example: 1}
234
+ end
235
+
236
+ it 'does not read invalid hashes' do
237
+ expect_syntax_error '{:foo 1 :bar}'
238
+ end
239
+
240
+ it 'reads empty sets' do
241
+ read_one('#{}').should == Set[]
242
+ end
243
+
244
+ it 'reads sets' do
245
+ read_one('#{1 two}').should == Set[1, Identifier.intern(:two)]
246
+ end
247
+
248
+ it 'does not read incomplete structures' do
249
+ expect_syntax_error '('
250
+ expect_syntax_error '['
251
+ expect_syntax_error '{'
252
+ expect_syntax_error '#{'
253
+ end
254
+
255
+ it 'reads multiple forms' do
256
+ read('foo bar').length.should == 2
257
+ @forms[0].should == Identifier.intern(:foo)
258
+ @forms[1].should == Identifier.intern(:bar)
259
+ end
260
+
261
+ it 'reads quoted forms' do
262
+ read_one("'test").should == List[Identifier.intern(:quote), Identifier.intern(:test)]
263
+ end
264
+
265
+ it 'reads syntax quoted forms' do
266
+ read_one('`1').should == 1
267
+ read_one('`~1').should == 1
268
+
269
+ apply = Identifier.intern(:apply)
270
+ concat = Identifier.intern(:concat)
271
+ list = Identifier.intern(:list)
272
+ quote = Identifier.intern(:quote)
273
+
274
+ begin
275
+ old_gensym = Apricot.instance_variable_get :@gensym
276
+ Apricot.instance_variable_set :@gensym, 41
277
+
278
+ read_one("`(foo ~bar ~@baz quux#)").should ==
279
+ List[concat,
280
+ List[list,
281
+ List[quote,
282
+ Identifier.intern(:foo)]],
283
+ List[list,
284
+ Identifier.intern(:bar)],
285
+ Identifier.intern(:baz),
286
+ List[list,
287
+ List[quote,
288
+ Identifier.intern(:'quux#__42')]]]
289
+ ensure
290
+ Apricot.instance_variable_set :@gensym, old_gensym
291
+ end
292
+
293
+ read_one('`[~a]').should ==
294
+ List[apply,
295
+ Identifier.intern(:array),
296
+ List[concat,
297
+ List[list,
298
+ Identifier.intern(:a)]]]
299
+
300
+ read_one('`{:a ~b}').should ==
301
+ List[apply,
302
+ Identifier.intern(:hash),
303
+ List[concat,
304
+ List[list,
305
+ :a],
306
+ List[list,
307
+ Identifier.intern(:b)]]]
308
+
309
+ read_one('`#{~a}').should ==
310
+ List[apply,
311
+ Identifier.intern(:'hash-set'),
312
+ List[concat,
313
+ List[list,
314
+ Identifier.intern(:a)]]]
315
+ end
316
+
317
+ it 'does not read invalid unquote forms' do
318
+ expect_syntax_error '`~@a'
319
+ end
320
+
321
+ it 'does not read incomplete unquote forms' do
322
+ expect_syntax_error '~'
323
+ expect_syntax_error '~@'
324
+ end
325
+
326
+ it 'reads #() shorthand' do
327
+ ids = (:a..:z).map {|sym| Identifier.intern(sym) }
328
+ Apricot.stub(:gensym).and_return(*ids)
329
+
330
+ read("#()").should == read("(fn [] ())")
331
+ read("#(foo)").should == read("(fn [] (foo))")
332
+ read("#(%)").should == read("(fn [a] (a))")
333
+ read("#(% %2)").should == read("(fn [b c] (b c))")
334
+ read("#(%1 %2)").should == read("(fn [d e] (d e))")
335
+ read("#(%2)").should == read("(fn [g f] (f))")
336
+ read("#(%&)").should == read("(fn [& h] (h))")
337
+ read("#(% %&)").should == read("(fn [i & j] (i j))")
338
+
339
+ expect_syntax_error("#(%0)")
340
+ expect_syntax_error("#(%-1)")
341
+ expect_syntax_error("#(%x)")
342
+ expect_syntax_error("#(%1.1)")
343
+ expect_syntax_error("#(%1asdf)")
344
+ end
345
+
346
+ it 'does not read invalid reader macros' do
347
+ expect_syntax_error('#x')
348
+ end
349
+ end