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