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,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