rouge-lang 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/.autotest +11 -0
- data/.gitignore +20 -0
- data/.rspec +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +6 -0
- data/LICENSE +19 -0
- data/README.md +119 -0
- data/Rakefile +8 -0
- data/bin/rouge +2 -0
- data/lib/boot.rg +402 -0
- data/lib/rouge.rb +56 -0
- data/lib/rouge/atom.rb +25 -0
- data/lib/rouge/builtins.rb +596 -0
- data/lib/rouge/compiler.rb +108 -0
- data/lib/rouge/context.rb +235 -0
- data/lib/rouge/metadata.rb +19 -0
- data/lib/rouge/namespace.rb +125 -0
- data/lib/rouge/printer.rb +78 -0
- data/lib/rouge/reader.rb +433 -0
- data/lib/rouge/repl.rb +82 -0
- data/lib/rouge/seq.rb +221 -0
- data/lib/rouge/symbol.rb +77 -0
- data/lib/rouge/var.rb +69 -0
- data/lib/rouge/version.rb +7 -0
- data/lib/rouge/wrappers.rb +27 -0
- data/misc/TODO +45 -0
- data/misc/vimrc +1 -0
- data/rouge-lang.gemspec +28 -0
- data/spec/atom_spec.rb +39 -0
- data/spec/builtins_spec.rb +708 -0
- data/spec/compiler_spec.rb +137 -0
- data/spec/context_spec.rb +293 -0
- data/spec/core_spec.rg +123 -0
- data/spec/metadata_spec.rb +59 -0
- data/spec/namespace_spec.rb +125 -0
- data/spec/printer_spec.rb +191 -0
- data/spec/reader_spec.rb +422 -0
- data/spec/rouge_spec.rb +66 -0
- data/spec/seq_spec.rb +202 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/symbol_spec.rb +35 -0
- data/spec/var_spec.rb +61 -0
- data/spec/wrappers_spec.rb +51 -0
- metadata +216 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
[:Macro, :Builtin, :Dequote, :Splice].each do |name|
|
4
|
+
Rouge.const_set name, Class.new {
|
5
|
+
def initialize(inner)
|
6
|
+
@inner = inner
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.[](inner)
|
10
|
+
new inner
|
11
|
+
end
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"#{self.class.name}[#{@inner.inspect}]"
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s; inspect; end
|
18
|
+
|
19
|
+
def ==(right)
|
20
|
+
right.is_a?(self.class) and right.inner == @inner
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :inner
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
# vim: set sw=2 et cc=80:
|
data/misc/TODO
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
NEXT:
|
2
|
+
- tests for (. x y)
|
3
|
+
- (ruby/)Time/now ==> Time.now
|
4
|
+
- continue decruft Context, shift tests to Compiler as appropriate; revise most
|
5
|
+
specs.
|
6
|
+
- extend DESTRUCTURE from LET to FN, etc.
|
7
|
+
- refactor DEFN, DEFMACRO, LET, BINDING(?) to make use of above
|
8
|
+
- may be faster for general wrapper classes to use Struct?
|
9
|
+
|
10
|
+
miscellaeny
|
11
|
+
-----------
|
12
|
+
- MACROEXPAND
|
13
|
+
- SPEED test. run core specs a few times and time.
|
14
|
+
- everything in ::Rouge should be accessible via rouge.builtin
|
15
|
+
- organise boot.rg
|
16
|
+
- test the whole core
|
17
|
+
- omit Ruby from tracebacks?
|
18
|
+
- better exceptions/errors
|
19
|
+
- readline autocomplete
|
20
|
+
|
21
|
+
ruby interop
|
22
|
+
------------
|
23
|
+
- accessing the scope/self (DSLs!)
|
24
|
+
- proc or lambda? setting ARITY.
|
25
|
+
- DEFN/DEFMACRO/etc. should create strict lambdas, but FN/#() should not.
|
26
|
+
Emulate by accepting |*a| and selecting ourselves how strict we want to be.
|
27
|
+
|
28
|
+
"hard" tasks
|
29
|
+
------------
|
30
|
+
- have a core "apply" function which can be used by eval, defmacro, etc.
|
31
|
+
|
32
|
+
core lang
|
33
|
+
---------
|
34
|
+
- NON-dynamic/special vars
|
35
|
+
- INTERN
|
36
|
+
- FOR
|
37
|
+
- multi-form defn: (defn x ([a] ...) ([a b] ...))
|
38
|
+
- atoms (actually multithreaded), refs, agents
|
39
|
+
- rest of the clojure core API (maybe even in clojure.core ns)
|
40
|
+
- clojure-ish IO libraries which expose async IO
|
41
|
+
- defprotocol -- looks important! ISeq, ISeqable, ISequential and what have
|
42
|
+
you.
|
43
|
+
- metadata on all collections (not just symbols)
|
44
|
+
|
45
|
+
vim: set cc=80:
|
data/misc/vimrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
au BufNewFile,BufRead *.rg set filetype=clojure
|
data/rouge-lang.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path('../lib/rouge/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.authors = ["Arlen Christian Mart Cuss"]
|
7
|
+
gem.email = ["ar@len.me"]
|
8
|
+
gem.description = %q{Ruby + Clojure = Rouge.}
|
9
|
+
gem.summary = %q{An implementation of Clojure for Ruby.}
|
10
|
+
gem.homepage = "http://rouge.io/"
|
11
|
+
|
12
|
+
gem.add_development_dependency('rake')
|
13
|
+
gem.add_development_dependency('autotest')
|
14
|
+
gem.add_development_dependency('autotest-growl')
|
15
|
+
gem.add_development_dependency('autotest-fsevent')
|
16
|
+
gem.add_development_dependency('ZenTest')
|
17
|
+
gem.add_development_dependency('rspec')
|
18
|
+
gem.add_development_dependency('term-ansicolor')
|
19
|
+
|
20
|
+
gem.files = `git ls-files`.split($\)
|
21
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
22
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
23
|
+
gem.name = "rouge-lang"
|
24
|
+
gem.require_paths = ["lib"]
|
25
|
+
gem.version = Rouge::VERSION
|
26
|
+
end
|
27
|
+
|
28
|
+
# vim: set sw=2 et cc=80:
|
data/spec/atom_spec.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'rouge'
|
4
|
+
|
5
|
+
describe Rouge::Atom do
|
6
|
+
describe ".new" do
|
7
|
+
let(:v) { Rouge::Atom.new(:snorlax) }
|
8
|
+
it { v.deref.should eq :snorlax }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#==" do
|
12
|
+
let(:a) { Rouge::Atom.new(:raichu) }
|
13
|
+
let(:b) { Rouge::Atom.new(:raichu) }
|
14
|
+
|
15
|
+
it { a.should_not == b }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#swap!" do
|
19
|
+
let(:v) { Rouge::Atom.new(456) }
|
20
|
+
|
21
|
+
context "first swap" do
|
22
|
+
before { v.swap!(lambda {|n| n * 2}) }
|
23
|
+
it { v.deref.should eq 912 }
|
24
|
+
|
25
|
+
context "second swap" do
|
26
|
+
before { v.swap!(lambda {|n, m| [n / 2, m]}, 'quack') }
|
27
|
+
it { v.deref.should eq [456, 'quack'] }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#reset!" do
|
33
|
+
let(:v) { Rouge::Atom.new(999) }
|
34
|
+
before { v.reset!(:lol) }
|
35
|
+
it { v.deref.should eq :lol }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# vim: set sw=2 et cc=80:
|
@@ -0,0 +1,708 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'rouge'
|
4
|
+
|
5
|
+
describe Rouge::Builtins do
|
6
|
+
let(:ns) { Rouge::Namespace.new(:"user.spec").clear }
|
7
|
+
let(:context) { Rouge::Context.new ns }
|
8
|
+
|
9
|
+
before { ns.refer Rouge::Namespace[:"rouge.builtin"] }
|
10
|
+
|
11
|
+
describe "let" do
|
12
|
+
describe "making local bindings" do
|
13
|
+
it { context.readeval("(let [a 42] a)").should eq 42 }
|
14
|
+
it { context.readeval("(let [a 1 a 2] a)").should eq 2 }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "destructuring" do
|
18
|
+
before { context.set_here :"one-two", [1, 2] }
|
19
|
+
it { context.readeval("(let [[a b] one-two] [a b])").should eq [1, 2] }
|
20
|
+
|
21
|
+
before { context.set_here :"x3-y4l", {:x => 3, :y => [4]} }
|
22
|
+
it { context.readeval("(let [{a :x [b] :y} x3-y4l] b)").
|
23
|
+
should eq 4 }
|
24
|
+
it { context.readeval("(let [{:keys [x]} x3-y4l] x)").should eq 3 }
|
25
|
+
|
26
|
+
before { context.set_here :"one-two-three", [1, 2, 3] }
|
27
|
+
it { context.readeval("(let [[a & b] one-two-three] [a b])").
|
28
|
+
should eq [1, [2, 3]] }
|
29
|
+
it { context.readeval("(let [[a & b :as f] one-two-three] [a b f])").
|
30
|
+
should eq [1, [2, 3], [1, 2, 3]] }
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "compilation by adding binding names to bindings" do
|
34
|
+
context "flat" do
|
35
|
+
before do
|
36
|
+
Rouge::Compiler.should_receive(:compile).
|
37
|
+
with(ns, kind_of(Set), anything) do |ns, lexicals, f|
|
38
|
+
case f
|
39
|
+
when Rouge::Symbol[:c] then lexicals.should eq Set[]
|
40
|
+
when 2 then lexicals.should eq Set[:a]
|
41
|
+
when 1 then lexicals.should eq Set[:a, :b]
|
42
|
+
end
|
43
|
+
end.exactly(3).times
|
44
|
+
end
|
45
|
+
|
46
|
+
it do
|
47
|
+
Rouge::Builtins._compile_let(
|
48
|
+
ns, Set.new,
|
49
|
+
[Rouge::Symbol[:a], Rouge::Symbol[:c],
|
50
|
+
Rouge::Symbol[:b], 2],
|
51
|
+
1)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "nested" do
|
56
|
+
before do
|
57
|
+
Rouge::Compiler.should_receive(:compile).
|
58
|
+
with(ns, kind_of(Set), anything) do |ns, lexicals, f|
|
59
|
+
case f
|
60
|
+
when [1, 2] then lexicals.should eq Set[]
|
61
|
+
when 3 then lexicals.should eq Set[:a, :b]
|
62
|
+
when 1 then lexicals.should eq Set[:a, :b, :c]
|
63
|
+
end
|
64
|
+
end.exactly(3).times
|
65
|
+
end
|
66
|
+
|
67
|
+
it do
|
68
|
+
Rouge::Builtins._compile_let(
|
69
|
+
ns, Set.new,
|
70
|
+
[[Rouge::Symbol[:a], Rouge::Symbol[:b]], [1, 2],
|
71
|
+
Rouge::Symbol[:c], 3],
|
72
|
+
1)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "context" do
|
79
|
+
it "should return the calling context" do
|
80
|
+
context.readeval("(context)").should be context
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should have no special compile function" do
|
84
|
+
Rouge::Builtins.should_not respond_to :_compile_context
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "quote" do
|
89
|
+
it "should prevent evaluation" do
|
90
|
+
context.readeval("(quote lmnop)").should eq ns.read 'lmnop'
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should not compile the argument" do
|
94
|
+
Rouge::Compiler.should_not_receive(:compile).
|
95
|
+
with(ns, kind_of(Set), Rouge::Symbol[:z])
|
96
|
+
|
97
|
+
Rouge::Builtins._compile_quote(
|
98
|
+
ns, Set.new,
|
99
|
+
Rouge::Symbol[:z])
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "fn" do
|
104
|
+
describe "creating a new lambda function" do
|
105
|
+
let(:l) { context.readeval('(fn [] "Mystik Spiral")') }
|
106
|
+
|
107
|
+
it { l.should be_an_instance_of Proc }
|
108
|
+
it { l.call.should eq "Mystik Spiral" }
|
109
|
+
it { context.eval(Rouge::Seq::Cons[l]).should eq "Mystik Spiral" }
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should create functions of correct arity" do
|
113
|
+
expect {
|
114
|
+
context.readeval('(fn [])').call(true)
|
115
|
+
}.to raise_exception(
|
116
|
+
ArgumentError, "wrong number of arguments (1 for 0)")
|
117
|
+
|
118
|
+
expect {
|
119
|
+
context.readeval('(fn [a b c])').call(:x, :y)
|
120
|
+
}.to raise_exception(
|
121
|
+
ArgumentError, "wrong number of arguments (2 for 3)")
|
122
|
+
|
123
|
+
expect {
|
124
|
+
context.readeval('(fn [& rest])').call()
|
125
|
+
context.readeval('(fn [& rest])').call(1)#
|
126
|
+
context.readeval('(fn [& rest])').call(1, 2, 3)
|
127
|
+
context.readeval('(fn [& rest])').call(*(1..10000))
|
128
|
+
}.to_not raise_exception
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "argument binding" do
|
132
|
+
# TODO: refactor & break out individual assertions
|
133
|
+
it "should bind place arguments correctly" do
|
134
|
+
context.readeval('(fn [a] a)').call(:zzz).should eq :zzz
|
135
|
+
context.readeval('(fn [a b] [a b])').
|
136
|
+
call(:daria, :morgendorffer).
|
137
|
+
should eq [:daria, :morgendorffer]
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should bind rest arguments correctly" do
|
141
|
+
context.readeval('(fn [y z & rest] [y z rest])').
|
142
|
+
call("where", "is", "mordialloc", "gosh").
|
143
|
+
should eq ns.read('["where" "is" ("mordialloc" "gosh")]')
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "binding block arguments correctly" do
|
147
|
+
let(:l) { lambda {} }
|
148
|
+
|
149
|
+
it { context.readeval('(fn [a | b] [a b])').
|
150
|
+
call("hello", &l).
|
151
|
+
should eq ["hello", l] }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "storing its own name" do
|
156
|
+
let(:fn) { context.readeval('(fn lmnop [])') }
|
157
|
+
|
158
|
+
it { fn.to_s.should eq :lmnop }
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should compile with names bound" do
|
162
|
+
Rouge::Compiler.should_receive(:compile).
|
163
|
+
with(ns, kind_of(Set), [:xyzzy]) do |ns, lexicals, f|
|
164
|
+
lexicals.should eq Set[:a, :rest, :block]
|
165
|
+
[:xyzzy]
|
166
|
+
end
|
167
|
+
|
168
|
+
Rouge::Builtins._compile_fn(
|
169
|
+
ns, Set.new,
|
170
|
+
[Rouge::Symbol[:a],
|
171
|
+
Rouge::Symbol[:&],
|
172
|
+
Rouge::Symbol[:rest],
|
173
|
+
Rouge::Symbol[:|],
|
174
|
+
Rouge::Symbol[:block]],
|
175
|
+
:xyzzy)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should use a new context per invocation" do
|
179
|
+
context.readeval(<<-ROUGE).should eq :a
|
180
|
+
(let [f (fn [x]
|
181
|
+
(if (.== x :a)
|
182
|
+
(do
|
183
|
+
(f :b)
|
184
|
+
x)
|
185
|
+
nil))]
|
186
|
+
(f :a))
|
187
|
+
ROUGE
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "def" do
|
192
|
+
it "should create and intern a var" do
|
193
|
+
context.readeval("(def barge)").
|
194
|
+
should eq Rouge::Var.new(:"user.spec", :barge)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should always make a binding at the top of the namespace" do
|
198
|
+
subcontext = Rouge::Context.new context
|
199
|
+
subcontext.readeval("(def sarge :b)").
|
200
|
+
should eq Rouge::Var.new(:"user.spec", :sarge, :b)
|
201
|
+
context.readeval('sarge').should eq :b
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should not compile the first argument" do
|
205
|
+
Rouge::Compiler.should_not_receive(:compile).
|
206
|
+
with(ns, kind_of(Set), Rouge::Symbol[:a])
|
207
|
+
|
208
|
+
Rouge::Compiler.should_receive(:compile).
|
209
|
+
with(ns, kind_of(Set), [Rouge::Symbol[:b]]) do |ns, lexicals, f|
|
210
|
+
lexicals.should eq Set[:a]
|
211
|
+
end
|
212
|
+
|
213
|
+
Rouge::Builtins._compile_def(
|
214
|
+
ns, Set.new,
|
215
|
+
Rouge::Symbol[:a],
|
216
|
+
Rouge::Symbol[:b])
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe "if" do
|
221
|
+
# TODO: refactor & break out individual assertions
|
222
|
+
it "should execute one branch or the other" do
|
223
|
+
a = mock("a")
|
224
|
+
b = mock("b")
|
225
|
+
a.should_receive(:call).with(any_args)
|
226
|
+
b.should_not_receive(:call).with(any_args)
|
227
|
+
subcontext = Rouge::Context.new context
|
228
|
+
subcontext.set_here :a, a
|
229
|
+
subcontext.set_here :b, b
|
230
|
+
subcontext.readeval('(if true (a) (b))')
|
231
|
+
end
|
232
|
+
|
233
|
+
# TODO: refactor & break out individual assertions
|
234
|
+
it "should not do anything in the case of a missing second branch" do
|
235
|
+
a = mock("a")
|
236
|
+
a.should_not_receive(:call)
|
237
|
+
subcontext = Rouge::Context.new context
|
238
|
+
subcontext.set_here :a, a
|
239
|
+
subcontext.readeval('(if false (a))')
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should have no special compile function" do
|
243
|
+
Rouge::Builtins.should_not respond_to :_compile_if
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
describe "do" do
|
248
|
+
it "should return nil with no arguments" do
|
249
|
+
context.readeval('(do)').should eq nil
|
250
|
+
end
|
251
|
+
|
252
|
+
describe "evaluating and returning one argument" do
|
253
|
+
let(:subcontext) { Rouge::Context.new context }
|
254
|
+
|
255
|
+
before { subcontext.set_here :x, lambda {4} }
|
256
|
+
|
257
|
+
it { subcontext.readeval('(do (x))').should eq 4 }
|
258
|
+
end
|
259
|
+
|
260
|
+
# TODO: refactor & break out individual assertions
|
261
|
+
it "should evaluate multiple arguments and return the last value" do
|
262
|
+
a = mock("a")
|
263
|
+
a.should_receive(:call)
|
264
|
+
subcontext = Rouge::Context.new context
|
265
|
+
subcontext.set_here :a, a
|
266
|
+
subcontext.set_here :b, lambda {7}
|
267
|
+
subcontext.readeval('(do (a) (b))').should eq 7
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should compile and do the right thing with multiple forms" do
|
271
|
+
context.readeval(<<-ROUGE).should eq 1
|
272
|
+
(do
|
273
|
+
(def o (ruby/Rouge.Atom. 1))
|
274
|
+
(.deref o))
|
275
|
+
ROUGE
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should compile straight-through" do
|
279
|
+
Rouge::Builtins._compile_do(ns, Set.new, :x, Rouge::Symbol[:y]).
|
280
|
+
should eq [Rouge::Symbol[:do], :x, Rouge::Symbol[:y]]
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe "ns" do
|
285
|
+
before { Rouge::Namespace.destroy :"user.spec2" }
|
286
|
+
|
287
|
+
it "should create and use a new context pointing at a given ns" do
|
288
|
+
context.readeval('(do (ns user.spec2) (def nope 8))')
|
289
|
+
Rouge[:"user.spec2"][:nope].deref.should eq 8
|
290
|
+
expect {
|
291
|
+
context[:nope]
|
292
|
+
}.to raise_exception Rouge::Namespace::VarNotFoundError
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should support the :use option" do
|
296
|
+
context.readeval(<<-ROUGE).should eq "<3"
|
297
|
+
(do
|
298
|
+
(ns user.spec2)
|
299
|
+
(def love "<3")
|
300
|
+
|
301
|
+
(ns user.spec3
|
302
|
+
(:use user.spec2))
|
303
|
+
love)
|
304
|
+
ROUGE
|
305
|
+
end
|
306
|
+
|
307
|
+
describe ":require" do
|
308
|
+
it "should support the option" do
|
309
|
+
Kernel.should_receive(:require).with("blah")
|
310
|
+
context.readeval(<<-ROUGE)
|
311
|
+
(ns user.spec2
|
312
|
+
(:require blah))
|
313
|
+
ROUGE
|
314
|
+
end
|
315
|
+
|
316
|
+
it "should support it with :as" do
|
317
|
+
File.should_receive(:read).with("blah.rg").and_return("")
|
318
|
+
context.readeval(<<-ROUGE)
|
319
|
+
(ns user.spec2
|
320
|
+
(:require [blah :as x]))
|
321
|
+
ROUGE
|
322
|
+
Rouge::Namespace[:x].should be Rouge::Namespace[:blah]
|
323
|
+
end
|
324
|
+
|
325
|
+
it ":as should not reload it" do
|
326
|
+
File.should_not_receive(:read).with("moop.rg")
|
327
|
+
context.readeval(<<-ROUGE)
|
328
|
+
(do
|
329
|
+
(ns moop)
|
330
|
+
(ns user.spec2
|
331
|
+
(:require [moop :as y])))
|
332
|
+
ROUGE
|
333
|
+
Rouge::Namespace[:y].should be Rouge::Namespace[:moop]
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
it "should compile without compiling any of its components" do
|
338
|
+
Rouge::Compiler.should_not_receive(:compile)
|
339
|
+
Rouge::Builtins._compile_ns(ns, Set.new, Rouge::Symbol[:non_extant])
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
describe "defmacro" do
|
344
|
+
let(:v) { context.readeval("(defmacro a [] 'b)") }
|
345
|
+
|
346
|
+
it { v.should be_an_instance_of Rouge::Var }
|
347
|
+
it { v.ns.should eq :"user.spec" }
|
348
|
+
it { v.name.should eq :a }
|
349
|
+
|
350
|
+
describe "evaluation in the defining context" do
|
351
|
+
it { expect { context.readeval("(defmacro a [] b)")
|
352
|
+
}.to raise_exception Rouge::Namespace::VarNotFoundError }
|
353
|
+
|
354
|
+
describe "after readeval (def ...)" do
|
355
|
+
before { context.readeval("(def b 'c)") }
|
356
|
+
|
357
|
+
it { expect { context.readeval("(defmacro a [] b)")
|
358
|
+
}.to_not raise_exception }
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
it "should expand in the calling context" do
|
363
|
+
context.readeval("(def b 'c)")
|
364
|
+
context.readeval("(defmacro zoom [] b)")
|
365
|
+
|
366
|
+
expect {
|
367
|
+
context.readeval("(zoom)")
|
368
|
+
}.to raise_exception Rouge::Namespace::VarNotFoundError
|
369
|
+
|
370
|
+
context.readeval("(let [c 9] (zoom))").should eq 9
|
371
|
+
end
|
372
|
+
|
373
|
+
it "should support the multiple argument list form" do
|
374
|
+
ns.set_here :vector, context.readeval(<<-ROUGE)
|
375
|
+
(fn [& r] r)
|
376
|
+
ROUGE
|
377
|
+
|
378
|
+
context.readeval(<<-ROUGE)
|
379
|
+
(defmacro m
|
380
|
+
([a] (vector 'vector ''a (vector 'quote a)))
|
381
|
+
([b c] (vector 'vector ''b (vector 'quote b) (vector 'quote c))))
|
382
|
+
ROUGE
|
383
|
+
|
384
|
+
context.readeval("(m x)").should eq ns.read("(a x)")
|
385
|
+
context.readeval("(m x y)").should eq ns.read("(b x y)")
|
386
|
+
end
|
387
|
+
|
388
|
+
describe "compilation" do
|
389
|
+
it "should compile single-arg form with names bound" do
|
390
|
+
Rouge::Compiler.should_receive(:compile).
|
391
|
+
with(ns, kind_of(Set), [:foo]) do |ns, lexicals, f|
|
392
|
+
lexicals.should eq Set[:quux, :rest, :block]
|
393
|
+
[:foo]
|
394
|
+
end
|
395
|
+
|
396
|
+
Rouge::Builtins._compile_defmacro(
|
397
|
+
ns, Set.new,
|
398
|
+
Rouge::Symbol[:barge],
|
399
|
+
[Rouge::Symbol[:quux],
|
400
|
+
Rouge::Symbol[:&],
|
401
|
+
Rouge::Symbol[:rest],
|
402
|
+
Rouge::Symbol[:|],
|
403
|
+
Rouge::Symbol[:block]],
|
404
|
+
:foo)
|
405
|
+
end
|
406
|
+
|
407
|
+
it "should compile multi-arg form with names bound" do
|
408
|
+
# TODO: consider breaking out these two test assertion
|
409
|
+
# blocks into their own context: they actually both fail
|
410
|
+
# in isolation but those failures are masked because
|
411
|
+
# the "Rouge::Builtins._compile_defmacro" test passes,
|
412
|
+
# and that last evaluation to 'true' gets passed as the
|
413
|
+
# return value to the enclosing 'it' method. Try it: change
|
414
|
+
# the order around, and you'll see the failure. REW
|
415
|
+
#
|
416
|
+
#Rouge::Compiler.should_receive(:compile).
|
417
|
+
# with(ns, kind_of(Set), [:a1]) do |n, lexicals, f|
|
418
|
+
# lexicals.should eq Set[:f]
|
419
|
+
# [:a1]
|
420
|
+
#end
|
421
|
+
#
|
422
|
+
#Rouge::Compiler.should_receive(:compile).
|
423
|
+
# with(ns, kind_of(Set), [:a2]) do |n, lexicals, f|
|
424
|
+
# lexicals.should eq Set[:g]
|
425
|
+
# [:a2]
|
426
|
+
#end
|
427
|
+
#
|
428
|
+
Rouge::Builtins._compile_defmacro(
|
429
|
+
ns, Set.new,
|
430
|
+
Rouge::Symbol[:barge],
|
431
|
+
Rouge::Seq::Cons[[Rouge::Symbol[:f]], :a1],
|
432
|
+
Rouge::Seq::Cons[[Rouge::Symbol[:g]], :a2])
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
describe "storing its own name" do
|
437
|
+
before { context.readeval('(defmacro lmnop [])') }
|
438
|
+
|
439
|
+
it { context[:lmnop].deref.to_s.should eq :"user.spec/lmnop" }
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
describe "apply" do
|
444
|
+
let(:a) { lambda {|*args| args} }
|
445
|
+
let(:subcontext) { Rouge::Context.new context }
|
446
|
+
|
447
|
+
before { subcontext.set_here :a, a }
|
448
|
+
|
449
|
+
describe "calling a function with the argument list" do
|
450
|
+
it { subcontext.readeval("(apply a [1 2 3])").should eq [1, 2, 3] }
|
451
|
+
it { subcontext.readeval("(apply a '(1 2 3))").should eq [1, 2, 3] }
|
452
|
+
end
|
453
|
+
|
454
|
+
describe "calling a function with intermediate arguments" do
|
455
|
+
it { subcontext.readeval("(apply a 8 9 [1 2 3])").should eq [8, 9, 1, 2, 3] }
|
456
|
+
it { subcontext.readeval("(apply a 8 9 '(1 2 3))").should eq [8, 9, 1, 2, 3] }
|
457
|
+
end
|
458
|
+
|
459
|
+
it "should have no special compile function" do
|
460
|
+
Rouge::Builtins.should_not respond_to :_compile_apply
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
describe "var" do
|
465
|
+
it "should return the var for a given symbol" do
|
466
|
+
ns.set_here :x, 42
|
467
|
+
context.readeval("(var x)").
|
468
|
+
should eq Rouge::Var.new(:"user.spec", :x, 42)
|
469
|
+
end
|
470
|
+
|
471
|
+
it "should compile directly to the var" do
|
472
|
+
ns.set_here :x, 80
|
473
|
+
Rouge::Builtins._compile_var(ns, Set.new, Rouge::Symbol[:x]).
|
474
|
+
should eq [Rouge::Symbol[:quote],
|
475
|
+
Rouge::Var.new(:"user.spec", :x, 80)]
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
describe "throw" do
|
480
|
+
it "should raise the given throwable as an exception" do
|
481
|
+
expect {
|
482
|
+
context.readeval('(throw (ruby/RuntimeError. "boo"))')
|
483
|
+
}.to raise_exception RuntimeError, 'boo'
|
484
|
+
end
|
485
|
+
|
486
|
+
it "should have no special compile function" do
|
487
|
+
Rouge::Builtins.should_not respond_to :_compile_throw
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
describe "try" do
|
492
|
+
it "should catch exceptions mentioned in the catch clause" do
|
493
|
+
context.readeval(<<-ROUGE).should eq :eofe
|
494
|
+
(try
|
495
|
+
(throw (ruby/EOFError. "bad"))
|
496
|
+
:baa
|
497
|
+
(catch ruby/EOFError _ :eofe))
|
498
|
+
ROUGE
|
499
|
+
end
|
500
|
+
|
501
|
+
it "should catch only the desired exception" do
|
502
|
+
context.readeval(<<-ROUGE).should eq :nie
|
503
|
+
(try
|
504
|
+
(throw (ruby/NotImplementedError. "bro"))
|
505
|
+
:baa
|
506
|
+
(catch ruby/EOFError _ :eofe)
|
507
|
+
(catch ruby/NotImplementedError _ :nie))
|
508
|
+
ROUGE
|
509
|
+
end
|
510
|
+
|
511
|
+
it "should actually catch exceptions" do
|
512
|
+
context.readeval(<<-ROUGE).should eq 3
|
513
|
+
(try
|
514
|
+
{:a 1 :b 2}
|
515
|
+
(throw (ruby/Exception.))
|
516
|
+
(catch ruby/Exception _ 3))
|
517
|
+
ROUGE
|
518
|
+
end
|
519
|
+
|
520
|
+
it "should let other exceptions fall through" do
|
521
|
+
expect {
|
522
|
+
context.readeval(<<-ROUGE)
|
523
|
+
(try
|
524
|
+
(throw (ruby/Exception. "kwok"))
|
525
|
+
:baa
|
526
|
+
(catch ruby/EOFError _ :eofe)
|
527
|
+
(catch ruby/NotImplementedError _ :nie))
|
528
|
+
ROUGE
|
529
|
+
}.to raise_exception Exception, 'kwok'
|
530
|
+
end
|
531
|
+
|
532
|
+
it "should work despite catch or finally being interned elsewhere" do
|
533
|
+
context.readeval(<<-ROUGE).should eq :baa
|
534
|
+
(try
|
535
|
+
:baa
|
536
|
+
(b/catch ruby/EOFError _ :eofe)
|
537
|
+
(a/catch ruby/NotImplementedError _ :nie))
|
538
|
+
ROUGE
|
539
|
+
end
|
540
|
+
|
541
|
+
it "should return the block's value if no exception was raised" do
|
542
|
+
context.readeval(<<-ROUGE).should eq :baa
|
543
|
+
(try
|
544
|
+
:baa
|
545
|
+
(catch ruby/EOFError _ :eofe)
|
546
|
+
(catch ruby/NotImplementedError _ :nie))
|
547
|
+
ROUGE
|
548
|
+
end
|
549
|
+
|
550
|
+
it "should evaluate the finally expressions without returning them" do
|
551
|
+
ns.set_here :m, Rouge::Atom.new(1)
|
552
|
+
|
553
|
+
context.readeval(<<-ROUGE).should eq :baa
|
554
|
+
(try
|
555
|
+
:baa
|
556
|
+
(catch ruby/NotImplementedError _ :nie)
|
557
|
+
(finally
|
558
|
+
(.swap! m #(.+ 1 %))))
|
559
|
+
ROUGE
|
560
|
+
|
561
|
+
context[:m].deref.deref.should eq 2
|
562
|
+
|
563
|
+
ns.set_here :o, Rouge::Atom.new(1)
|
564
|
+
|
565
|
+
expect {
|
566
|
+
context.readeval(<<-ROUGE).should eq :baa
|
567
|
+
(try
|
568
|
+
(throw (ruby/ArgumentError. "fire"))
|
569
|
+
:baa
|
570
|
+
(catch ruby/NotImplementedError _ :nie)
|
571
|
+
(finally
|
572
|
+
(.swap! o #(.+ 1 %))))
|
573
|
+
ROUGE
|
574
|
+
}.to raise_exception ArgumentError, 'fire'
|
575
|
+
|
576
|
+
context[:o].deref.deref.should eq 2
|
577
|
+
end
|
578
|
+
|
579
|
+
it "should bind the exception expressions" do
|
580
|
+
context.readeval(<<-ROUGE).should be_an_instance_of(NotImplementedError)
|
581
|
+
(try
|
582
|
+
(throw (ruby/NotImplementedError. "wat"))
|
583
|
+
(catch ruby/NotImplementedError e
|
584
|
+
e))
|
585
|
+
ROUGE
|
586
|
+
end
|
587
|
+
|
588
|
+
describe "compilation" do
|
589
|
+
let(:compile) { lambda {|rouge, lexicals=[]|
|
590
|
+
Rouge::Builtins._compile_try(
|
591
|
+
ns, Set[*lexicals],
|
592
|
+
*Rouge::Reader.new(ns, rouge).lex.to_a) } }
|
593
|
+
|
594
|
+
it "should compile the main body" do
|
595
|
+
expect {
|
596
|
+
compile.call("(a)")
|
597
|
+
}.to raise_exception Rouge::Namespace::VarNotFoundError
|
598
|
+
|
599
|
+
expect {
|
600
|
+
compile.call("(a)", [:a])
|
601
|
+
}.to_not raise_exception
|
602
|
+
end
|
603
|
+
|
604
|
+
it "should compile catch clauses with bindings" do
|
605
|
+
expect {
|
606
|
+
compile.call("(a (catch ruby/NotImplementedError b c))", [:a])
|
607
|
+
}.to raise_exception Rouge::Namespace::VarNotFoundError
|
608
|
+
|
609
|
+
expect {
|
610
|
+
compile.call("(a (catch ruby/NotImplementedError b b))", [:a])
|
611
|
+
}.to_not raise_exception
|
612
|
+
end
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
describe "destructure" do
|
617
|
+
it "should return a hash of symbols to assigned values" do
|
618
|
+
context.readeval("(destructure [a b c] [1 2 3])").to_s.
|
619
|
+
should eq({Rouge::Symbol[:a] => 1,
|
620
|
+
Rouge::Symbol[:b] => 2,
|
621
|
+
Rouge::Symbol[:c] => 3}.to_s)
|
622
|
+
end
|
623
|
+
|
624
|
+
it "should assign rest arguments" do
|
625
|
+
context.readeval("(destructure [a & b] [1 2 3])").to_s.
|
626
|
+
should eq({Rouge::Symbol[:a] => 1,
|
627
|
+
Rouge::Symbol[:b] => Rouge::Seq::Cons[2, 3]}.to_s)
|
628
|
+
end
|
629
|
+
|
630
|
+
it "should destructure seqs" do
|
631
|
+
context.readeval("(destructure [[a b] c] [[1 2] 3])").to_s.
|
632
|
+
should eq({Rouge::Symbol[:a] => 1,
|
633
|
+
Rouge::Symbol[:b] => 2,
|
634
|
+
Rouge::Symbol[:c] => 3}.to_s)
|
635
|
+
end
|
636
|
+
|
637
|
+
it "should destructure rests in nested seqs" do
|
638
|
+
context.readeval("(destructure [a [b & c]] [1 [2 3 4]])").to_s.
|
639
|
+
should eq({Rouge::Symbol[:a] => 1,
|
640
|
+
Rouge::Symbol[:b] => 2,
|
641
|
+
Rouge::Symbol[:c] => Rouge::Seq::Cons[3, 4]}.to_s)
|
642
|
+
end
|
643
|
+
|
644
|
+
context "destructuring blocks" do
|
645
|
+
let(:x) { lambda {} }
|
646
|
+
|
647
|
+
before { context.set_here :x, x }
|
648
|
+
|
649
|
+
it { context.readeval("(destructure [a | b] [1 | x])").to_s.
|
650
|
+
should eq({Rouge::Symbol[:a] => 1,
|
651
|
+
Rouge::Symbol[:b] => x}.to_s) }
|
652
|
+
end
|
653
|
+
|
654
|
+
it { context.readeval(
|
655
|
+
"(destructure {the-x :x the-y :y} {:x 5 :y 7})").to_s.
|
656
|
+
should eq({Rouge::Symbol[:"the-x"] => 5,
|
657
|
+
Rouge::Symbol[:"the-y"] => 7}.to_s) }
|
658
|
+
|
659
|
+
it { context.readeval(
|
660
|
+
"(destructure [x & more :as full-list] [1 2 3])").to_s.
|
661
|
+
should eq(
|
662
|
+
{Rouge::Symbol[:"x"] => 1,
|
663
|
+
Rouge::Symbol[:"more"] => Rouge::Seq::Cons[2, 3],
|
664
|
+
Rouge::Symbol[:"full-list"] => Rouge::Seq::Cons[1, 2, 3]
|
665
|
+
}.to_s) }
|
666
|
+
|
667
|
+
it { context.readeval(
|
668
|
+
"(destructure {:keys [x y]} {:x 5 :y 7})").to_s.
|
669
|
+
should eq({Rouge::Symbol[:x] => 5,
|
670
|
+
Rouge::Symbol[:y] => 7}.to_s) }
|
671
|
+
|
672
|
+
describe "compiling the value part" do
|
673
|
+
it { expect {
|
674
|
+
Rouge::Builtins._compile_destructure(
|
675
|
+
ns, Set.new, Rouge::Symbol[:z], Rouge::Symbol[:z])
|
676
|
+
}.to raise_exception }
|
677
|
+
|
678
|
+
it { expect {
|
679
|
+
Rouge::Builtins._compile_destructure(
|
680
|
+
ns, Set[:y], Rouge::Symbol[:z], Rouge::Symbol[:y])
|
681
|
+
}.to_not raise_exception }
|
682
|
+
end
|
683
|
+
|
684
|
+
describe "vigorous complaints about letting qualified names" do
|
685
|
+
it { expect { context.readeval("(destructure [user.spec/x] [1])")
|
686
|
+
}.to raise_exception Rouge::Context::BadBindingError }
|
687
|
+
end
|
688
|
+
|
689
|
+
describe "not forcing what needn't be forced" do
|
690
|
+
it { Rouge::Builtins.destructure(
|
691
|
+
context,
|
692
|
+
[Rouge::Symbol[:a], Rouge::Symbol[:&], Rouge::Symbol[:b]],
|
693
|
+
Rouge::Seq::Lazy.new(lambda do
|
694
|
+
Rouge::Seq::Cons.new(
|
695
|
+
1,
|
696
|
+
Rouge::Seq::Lazy.new(lambda do
|
697
|
+
Rouge::Seq::Cons.new(
|
698
|
+
2,
|
699
|
+
Rouge::Seq::Lazy.new(lambda do
|
700
|
+
raise "shouldn't be forced"
|
701
|
+
end))
|
702
|
+
end))
|
703
|
+
end)) }
|
704
|
+
end
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
# vim: set sw=2 et cc=80:
|