rouge-lang 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: