apricot 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/.travis.yml +1 -0
- data/Gemfile.lock +229 -11
- data/README.md +46 -29
- data/Rakefile +1 -1
- data/apricot.gemspec +7 -3
- data/benchmarks/factorial.rb +51 -0
- data/benchmarks/interpolate.rb +20 -0
- data/bin/apricot +5 -23
- data/examples/bot.apr +1 -4
- data/examples/cinch-bot.apr +3 -3
- data/examples/sinatra.apr +9 -0
- data/kernel/core.apr +124 -75
- data/kernel/repl.apr +37 -0
- data/lib/apricot.rb +7 -26
- data/lib/apricot/boot.rb +24 -0
- data/lib/apricot/code_loader.rb +108 -0
- data/lib/apricot/compiler.rb +265 -32
- data/lib/apricot/generator.rb +10 -3
- data/lib/apricot/identifier.rb +25 -10
- data/lib/apricot/list.rb +28 -41
- data/lib/apricot/macroexpand.rb +14 -8
- data/lib/apricot/misc.rb +2 -1
- data/lib/apricot/namespace.rb +20 -3
- data/lib/apricot/{parser.rb → reader.rb} +221 -194
- data/lib/apricot/repl.rb +67 -24
- data/lib/apricot/ruby_ext.rb +27 -16
- data/lib/apricot/scopes.rb +159 -0
- data/lib/apricot/seq.rb +43 -1
- data/lib/apricot/special_forms.rb +16 -695
- data/lib/apricot/special_forms/def.rb +32 -0
- data/lib/apricot/special_forms/do.rb +23 -0
- data/lib/apricot/special_forms/dot.rb +112 -0
- data/lib/apricot/special_forms/fn.rb +342 -0
- data/lib/apricot/special_forms/if.rb +31 -0
- data/lib/apricot/special_forms/let.rb +8 -0
- data/lib/apricot/special_forms/loop.rb +10 -0
- data/lib/apricot/special_forms/quote.rb +9 -0
- data/lib/apricot/special_forms/recur.rb +26 -0
- data/lib/apricot/special_forms/try.rb +146 -0
- data/lib/apricot/variables.rb +65 -0
- data/lib/apricot/version.rb +1 -1
- data/spec/compiler_spec.rb +53 -450
- data/spec/fn_spec.rb +206 -0
- data/spec/list_spec.rb +1 -1
- data/spec/reader_spec.rb +349 -0
- data/spec/spec_helper.rb +40 -4
- data/spec/special_forms_spec.rb +203 -0
- metadata +99 -133
- data/lib/apricot/ast.rb +0 -3
- data/lib/apricot/ast/identifier.rb +0 -111
- data/lib/apricot/ast/list.rb +0 -99
- data/lib/apricot/ast/literals.rb +0 -240
- data/lib/apricot/ast/node.rb +0 -45
- data/lib/apricot/ast/scopes.rb +0 -147
- data/lib/apricot/ast/toplevel.rb +0 -66
- data/lib/apricot/ast/variables.rb +0 -64
- data/lib/apricot/printers.rb +0 -12
- data/lib/apricot/stages.rb +0 -60
- data/spec/parser_spec.rb +0 -312
@@ -0,0 +1,31 @@
|
|
1
|
+
module Apricot
|
2
|
+
# (if cond body else_body?)
|
3
|
+
SpecialForm.define(:if) do |g, args|
|
4
|
+
g.compile_error "Too few arguments to if" if args.count < 2
|
5
|
+
g.compile_error "Too many arguments to if" if args.count > 3
|
6
|
+
|
7
|
+
cond, body, else_body = *args
|
8
|
+
else_label = g.new_label
|
9
|
+
end_label = g.new_label
|
10
|
+
|
11
|
+
tail_position = g.tail_position?
|
12
|
+
|
13
|
+
g.tail_position = false
|
14
|
+
Compiler.bytecode(g, cond)
|
15
|
+
g.gif else_label
|
16
|
+
|
17
|
+
g.tail_position = tail_position
|
18
|
+
Compiler.bytecode(g, body)
|
19
|
+
g.goto end_label
|
20
|
+
|
21
|
+
g.tail_position = tail_position
|
22
|
+
else_label.set!
|
23
|
+
if else_body
|
24
|
+
Compiler.bytecode(g, else_body)
|
25
|
+
else
|
26
|
+
g.push_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
end_label.set!
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Apricot
|
2
|
+
# (loop [binding*] body*) where binding is an identifier followed by a value
|
3
|
+
# Just like let but also introduces a loop target for (recur ...)
|
4
|
+
SpecialForm.define(:loop) do |g, args|
|
5
|
+
# loop and let share a lot of code. See special_forms.rb for the shared
|
6
|
+
# definition.
|
7
|
+
g.tail_position = true
|
8
|
+
let(g, args, :loop)
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Apricot
|
2
|
+
# (recur args*)
|
3
|
+
# Rebinds the arguments of the nearest enclosing loop or fn and jumps to the
|
4
|
+
# top of the loop/fn. Argument rebinding is done in parallel (rebinding a
|
5
|
+
# variable in a recur will not affect uses of that variable in the other
|
6
|
+
# recur bindings.)
|
7
|
+
SpecialForm.define(:recur) do |g, args|
|
8
|
+
target = g.scope.find_recur_target
|
9
|
+
g.compile_error "No recursion target found for recur" unless target
|
10
|
+
|
11
|
+
g.compile_error "Can only recur from tail position" unless g.tail_position?
|
12
|
+
|
13
|
+
vars = target.variables.values
|
14
|
+
g.compile_error "Arity of recur does not match enclosing loop or fn" unless vars.length == args.count
|
15
|
+
|
16
|
+
args.each {|arg| Compiler.bytecode(g, arg) }
|
17
|
+
|
18
|
+
vars.reverse_each do |var|
|
19
|
+
g.set_local var
|
20
|
+
g.pop
|
21
|
+
end
|
22
|
+
|
23
|
+
g.check_interrupts
|
24
|
+
g.goto target.loop_label
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module Apricot
|
2
|
+
# (try body* (rescue name|[name condition*] body*)* (ensure body*)?)
|
3
|
+
SpecialForm.define(:try) do |g, args|
|
4
|
+
body = []
|
5
|
+
rescue_clauses = []
|
6
|
+
ensure_clause = nil
|
7
|
+
|
8
|
+
args.each do |arg|
|
9
|
+
g.compile_error "Unexpected form after ensure clause" if ensure_clause
|
10
|
+
|
11
|
+
if arg.is_a?(Seq) && arg.first == Identifier.intern(:rescue)
|
12
|
+
rescue_clauses << arg.rest
|
13
|
+
elsif arg.is_a?(Seq) && arg.first == Identifier.intern(:ensure)
|
14
|
+
ensure_clause = arg.rest
|
15
|
+
else
|
16
|
+
g.compile_error "Unexpected form after rescue clause" unless rescue_clauses.empty?
|
17
|
+
body << arg
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Set up ensure
|
22
|
+
if ensure_clause
|
23
|
+
ensure_ex = g.new_label
|
24
|
+
ensure_ok = g.new_label
|
25
|
+
g.setup_unwind ensure_ex, 1
|
26
|
+
end
|
27
|
+
|
28
|
+
ex = g.new_label
|
29
|
+
done = g.new_label
|
30
|
+
|
31
|
+
g.push_exception_state
|
32
|
+
ex_state = g.new_stack_local
|
33
|
+
g.set_stack_local ex_state
|
34
|
+
g.pop
|
35
|
+
|
36
|
+
# Evaluate body
|
37
|
+
g.setup_unwind ex, 0
|
38
|
+
SpecialForm[:do].bytecode(g, body)
|
39
|
+
g.pop_unwind
|
40
|
+
g.goto done
|
41
|
+
|
42
|
+
# Body raised an exception
|
43
|
+
ex.set!
|
44
|
+
|
45
|
+
# Save exception state for re-raise
|
46
|
+
g.push_exception_state
|
47
|
+
raised_ex_state = g.new_stack_local
|
48
|
+
g.set_stack_local raised_ex_state
|
49
|
+
g.pop
|
50
|
+
|
51
|
+
# Push exception for rescue conditions
|
52
|
+
g.push_current_exception
|
53
|
+
|
54
|
+
rescue_clauses.each do |clause|
|
55
|
+
# Parse either (rescue e body) or (rescue [e Exception*] body)
|
56
|
+
if clause.first.is_a? Identifier
|
57
|
+
name, clause = clause.first, clause.rest
|
58
|
+
conditions = []
|
59
|
+
elsif clause.first.is_a? Array
|
60
|
+
conditions, clause = clause.first, clause.rest
|
61
|
+
name = conditions.shift
|
62
|
+
g.compile_error "Expected identifier as first form of rescue clause binding" unless name.is_a? Identifier
|
63
|
+
else
|
64
|
+
g.compile_error "Expected identifier or array as first form of rescue clause"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Default to StandardError for (rescue e body) and (rescue [e] body)
|
68
|
+
conditions << Identifier.intern(:StandardError) if conditions.empty?
|
69
|
+
|
70
|
+
body = g.new_label
|
71
|
+
next_rescue = g.new_label
|
72
|
+
|
73
|
+
conditions.each do |cond|
|
74
|
+
g.dup # The exception
|
75
|
+
Compiler.bytecode(g, cond)
|
76
|
+
g.swap
|
77
|
+
g.send :===, 1
|
78
|
+
g.git body
|
79
|
+
end
|
80
|
+
g.goto next_rescue
|
81
|
+
|
82
|
+
# This rescue condition matched
|
83
|
+
body.set!
|
84
|
+
|
85
|
+
# Create a new scope to hold the exception
|
86
|
+
scope = LetScope.new(g.scope)
|
87
|
+
g.scopes << scope
|
88
|
+
|
89
|
+
# Exception is still on the stack
|
90
|
+
g.set_local scope.new_local(name)
|
91
|
+
g.pop
|
92
|
+
|
93
|
+
SpecialForm[:do].bytecode(g, clause)
|
94
|
+
|
95
|
+
# Yay!
|
96
|
+
g.clear_exception
|
97
|
+
g.goto done
|
98
|
+
|
99
|
+
g.scopes.pop
|
100
|
+
|
101
|
+
# Rescue condition did not match
|
102
|
+
next_rescue.set!
|
103
|
+
end
|
104
|
+
|
105
|
+
# No rescue conditions matched, re-raise
|
106
|
+
g.pop # The exception
|
107
|
+
|
108
|
+
# Re-raise the original exception
|
109
|
+
g.push_stack_local raised_ex_state
|
110
|
+
g.restore_exception_state
|
111
|
+
g.reraise
|
112
|
+
|
113
|
+
# Body executed without exception or was rescued
|
114
|
+
done.set!
|
115
|
+
|
116
|
+
g.push_stack_local raised_ex_state
|
117
|
+
g.restore_exception_state
|
118
|
+
|
119
|
+
if ensure_clause
|
120
|
+
g.pop_unwind
|
121
|
+
g.goto ensure_ok
|
122
|
+
|
123
|
+
# Body raised an exception
|
124
|
+
ensure_ex.set!
|
125
|
+
|
126
|
+
# Execute ensure clause
|
127
|
+
g.push_exception_state
|
128
|
+
ensure_clause.each do |expr|
|
129
|
+
Compiler.bytecode(g, expr)
|
130
|
+
g.pop # Ensure cannot return anything
|
131
|
+
end
|
132
|
+
g.restore_exception_state
|
133
|
+
|
134
|
+
g.reraise
|
135
|
+
|
136
|
+
# Body executed without exception or was rescued
|
137
|
+
ensure_ok.set!
|
138
|
+
|
139
|
+
# Execute ensure clause
|
140
|
+
ensure_clause.each do |expr|
|
141
|
+
Compiler.bytecode(g, expr)
|
142
|
+
g.pop
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Apricot
|
2
|
+
class LocalReference
|
3
|
+
attr_reader :slot, :depth
|
4
|
+
|
5
|
+
def initialize(slot, depth = 0)
|
6
|
+
@slot = slot
|
7
|
+
@depth = depth
|
8
|
+
end
|
9
|
+
|
10
|
+
def bytecode(g)
|
11
|
+
if @depth == 0
|
12
|
+
g.push_local @slot
|
13
|
+
else
|
14
|
+
g.push_local_depth @depth, @slot
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class QualifiedReference
|
20
|
+
def initialize(name, qualifier)
|
21
|
+
unless qualifier.is_a? Module # Namespaces are Modules as well.
|
22
|
+
raise ArgumentError, "qualifier for #{self.class} must be a Namespace or Module"
|
23
|
+
end
|
24
|
+
|
25
|
+
@name = name
|
26
|
+
@qualifier = qualifier
|
27
|
+
@on_namespace = qualifier.is_a? Namespace
|
28
|
+
end
|
29
|
+
|
30
|
+
def bytecode(g)
|
31
|
+
if @qualifier.is_a?(Namespace) && !@qualifier.has_var?(@name)
|
32
|
+
g.compile_error "Unable to resolve name #{@name} in namespace #{@qualifier}"
|
33
|
+
end
|
34
|
+
|
35
|
+
ns_id = Identifier.intern(@qualifier.name)
|
36
|
+
g.push_const ns_id.const_names.first
|
37
|
+
ns_id.const_names.drop(1).each {|n| g.find_const(n) }
|
38
|
+
|
39
|
+
g.push_literal @name
|
40
|
+
|
41
|
+
if on_namespace?
|
42
|
+
g.send :get_var, 1
|
43
|
+
else # @qualifier is a regular Ruby module
|
44
|
+
g.send :method, 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_namespace?
|
49
|
+
@on_namespace
|
50
|
+
end
|
51
|
+
|
52
|
+
def meta
|
53
|
+
on_namespace? && @qualifier.vars[@name] &&
|
54
|
+
@qualifier.vars[@name].apricot_meta
|
55
|
+
end
|
56
|
+
|
57
|
+
def fn?
|
58
|
+
on_namespace? && @qualifier.fns.include?(@name)
|
59
|
+
end
|
60
|
+
|
61
|
+
def method?
|
62
|
+
!on_namespace? && @qualifier.respond_to?(@name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/apricot/version.rb
CHANGED
data/spec/compiler_spec.rb
CHANGED
@@ -1,499 +1,102 @@
|
|
1
1
|
describe 'Apricot' do
|
2
|
-
|
3
|
-
Apricot::Compiler.eval code
|
4
|
-
end
|
5
|
-
|
6
|
-
def bad_apricot(code)
|
7
|
-
expect { apricot(code) }.to raise_error(CompileError)
|
8
|
-
end
|
2
|
+
include CompilerSpec
|
9
3
|
|
10
4
|
it 'compiles an empty program' do
|
11
|
-
|
5
|
+
apr('').should == nil
|
12
6
|
end
|
13
7
|
|
14
8
|
it 'compiles false, true and nil' do
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
apr('true').should == true
|
10
|
+
apr('false').should == false
|
11
|
+
apr('nil').should == nil
|
18
12
|
end
|
19
13
|
|
20
14
|
it 'compiles numbers' do
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
apr('1').should == 1
|
16
|
+
apr('1.0').should == 1.0
|
17
|
+
apr('1/3').should == Rational(1, 3)
|
24
18
|
end
|
25
19
|
|
26
20
|
it 'compiles symbols' do
|
27
|
-
|
21
|
+
apr(':foo').should == :foo
|
22
|
+
end
|
23
|
+
|
24
|
+
it "doesn't mix up symbols and keywords in lists" do
|
25
|
+
# There once was a bug where :true would get compiled as the value true.
|
26
|
+
apr('(.class :true)').should == Symbol
|
28
27
|
end
|
29
28
|
|
30
29
|
it 'compiles strings' do
|
31
|
-
|
30
|
+
apr('"foo"').should == "foo"
|
32
31
|
end
|
33
32
|
|
34
33
|
it 'compiles regexes' do
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
apr('#r"foo"').should == /foo/
|
35
|
+
apr('#r/foo/x').should == /foo/x
|
36
|
+
apr('#r[foo]xim').should == /foo/xim
|
38
37
|
end
|
39
38
|
|
40
39
|
it 'compiles arrays' do
|
41
|
-
|
42
|
-
|
40
|
+
apr('[]').should == []
|
41
|
+
apr('[1 2 3]').should == [1, 2, 3]
|
43
42
|
end
|
44
43
|
|
45
44
|
it 'compiles hashes' do
|
46
|
-
|
47
|
-
|
45
|
+
apr('{}').should == {}
|
46
|
+
apr('{:foo 1, :bar 2}').should == {:foo => 1, :bar => 2}
|
48
47
|
end
|
49
48
|
|
50
49
|
it 'compiles sets' do
|
51
|
-
|
52
|
-
|
50
|
+
apr('#{}').should == Set.new
|
51
|
+
apr('#{:foo :foo :bar}').should == Set[:foo, :foo, :bar]
|
53
52
|
end
|
54
53
|
|
55
54
|
it 'compiles constants' do
|
56
|
-
|
57
|
-
|
55
|
+
apr('Array').should == Array
|
56
|
+
apr('Enumerable::Enumerator').should == Enumerable::Enumerator
|
58
57
|
end
|
59
58
|
|
60
59
|
it 'compiles call forms with data structures' do
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
apr('([:a :b] 1)').should == :b
|
61
|
+
apr('([:a :b] 3)').should == nil
|
62
|
+
apr('(#{:a :b} :b)').should == :b
|
63
|
+
apr('(#{:a :b} :c)').should == nil
|
64
|
+
apr('({:a 1} :a)').should == 1
|
65
|
+
apr('({:a 1} :b)').should == nil
|
67
66
|
end
|
68
67
|
|
69
68
|
it 'compiles symbol call forms' do
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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]
|
69
|
+
apr('(:a {:a 1 :b 2})').should == 1
|
70
|
+
apr('(:c {:a 1 :b 2})').should == nil
|
71
|
+
apr('(:a #{:a :b})').should == :a
|
72
|
+
apr('(:c #{:a :b})').should == nil
|
132
73
|
end
|
133
74
|
|
134
75
|
it 'compiles constant defs' do
|
135
76
|
expect { Foo }.to raise_error(NameError)
|
136
|
-
|
77
|
+
apr '(def Foo 1)'
|
137
78
|
Foo.should == 1
|
138
79
|
end
|
139
80
|
|
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
81
|
it 'compiles quoted forms' do
|
479
|
-
|
480
|
-
|
481
|
-
|
82
|
+
apr("'1").should == 1
|
83
|
+
apr("'a").should == Identifier.intern(:a)
|
84
|
+
apr("''a").should == List[
|
482
85
|
Identifier.intern(:quote),
|
483
86
|
Identifier.intern(:a)
|
484
87
|
]
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
88
|
+
apr("'1.2").should == 1.2
|
89
|
+
apr("'1/2").should == Rational(1,2)
|
90
|
+
apr("':a").should == :a
|
91
|
+
apr("'()").should == List::EMPTY_LIST
|
92
|
+
apr("'(1)").should == List[1]
|
93
|
+
apr("'[a]").should == [Identifier.intern(:a)]
|
94
|
+
apr("'{a 1}").should == {Identifier.intern(:a) => 1}
|
95
|
+
apr('\'"foo"').should == "foo"
|
96
|
+
apr("'true").should == true
|
97
|
+
apr("'false").should == false
|
98
|
+
apr("'nil").should == nil
|
99
|
+
apr("'self").should == Identifier.intern(:self)
|
100
|
+
apr("'Foo::Bar").should == Identifier.intern(:'Foo::Bar')
|
498
101
|
end
|
499
102
|
end
|