apricot 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +26 -0
- data/README.md +90 -0
- data/Rakefile +9 -0
- data/apricot.gemspec +22 -0
- data/bin/apricot +58 -0
- data/examples/bot.apr +23 -0
- data/examples/cinch-bot.apr +12 -0
- data/examples/hanoi.apr +10 -0
- data/examples/hello.apr +1 -0
- data/examples/plot.apr +28 -0
- data/examples/quine.apr +1 -0
- data/kernel/core.apr +928 -0
- data/lib/apricot/ast/identifier.rb +111 -0
- data/lib/apricot/ast/list.rb +99 -0
- data/lib/apricot/ast/literals.rb +240 -0
- data/lib/apricot/ast/node.rb +45 -0
- data/lib/apricot/ast/scopes.rb +147 -0
- data/lib/apricot/ast/toplevel.rb +66 -0
- data/lib/apricot/ast/variables.rb +64 -0
- data/lib/apricot/ast.rb +3 -0
- data/lib/apricot/compiler.rb +55 -0
- data/lib/apricot/cons.rb +27 -0
- data/lib/apricot/errors.rb +38 -0
- data/lib/apricot/generator.rb +15 -0
- data/lib/apricot/identifier.rb +91 -0
- data/lib/apricot/list.rb +96 -0
- data/lib/apricot/macroexpand.rb +47 -0
- data/lib/apricot/misc.rb +11 -0
- data/lib/apricot/namespace.rb +59 -0
- data/lib/apricot/parser.rb +541 -0
- data/lib/apricot/printers.rb +12 -0
- data/lib/apricot/repl.rb +254 -0
- data/lib/apricot/ruby_ext.rb +254 -0
- data/lib/apricot/seq.rb +44 -0
- data/lib/apricot/special_forms.rb +735 -0
- data/lib/apricot/stages.rb +60 -0
- data/lib/apricot/version.rb +3 -0
- data/lib/apricot.rb +30 -0
- data/spec/compiler_spec.rb +499 -0
- data/spec/identifier_spec.rb +58 -0
- data/spec/list_spec.rb +96 -0
- data/spec/parser_spec.rb +312 -0
- data/spec/spec_helper.rb +10 -0
- 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
|
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
|