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.
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ module Rouge
4
+ VERSION = "0.0.1"
5
+ end
6
+
7
+ # vim: set sw=2 et cc=80:
@@ -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
@@ -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: