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