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,137 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'rouge'
4
+
5
+ describe Rouge::Compiler do
6
+ before do
7
+ @ns = Rouge[:"user.spec"].clear
8
+ @ns.refer Rouge[:"rouge.builtin"]
9
+
10
+ @read = lambda do |input|
11
+ Rouge::Reader.new(@ns, input).lex
12
+ end
13
+
14
+ @compile = lambda do |input|
15
+ form = @read.call(input)
16
+ Rouge::Compiler.compile(@ns, Set.new, form)
17
+ end
18
+ end
19
+
20
+ describe "lexical lookup" do
21
+ it "should compile with respect to locals" do
22
+ lambda {
23
+ @compile.call("(fn [] a)")
24
+ }.should raise_exception(Rouge::Namespace::VarNotFoundError)
25
+
26
+ lambda {
27
+ @compile.call("q")
28
+ }.should raise_exception(Rouge::Namespace::VarNotFoundError)
29
+
30
+ lambda {
31
+ @compile.call("(let [x 8] x)").
32
+ should eq @read.call("(let [x 8] x)")
33
+ }.should_not raise_exception
34
+
35
+ lambda {
36
+ @compile.call("(let [x 8] y)")
37
+ }.should raise_exception(Rouge::Namespace::VarNotFoundError)
38
+
39
+ lambda {
40
+ @compile.call("(let [x 8] ((fn [& b] (b)) | [e] e))")
41
+ }.should_not raise_exception
42
+
43
+ lambda {
44
+ @compile.call("(let [x 8] ((fn [& b] (b)) | [e] f))")
45
+ }.should raise_exception(Rouge::Namespace::VarNotFoundError)
46
+ end
47
+ end
48
+
49
+ describe "macro behaviour" do
50
+ it "should execute macro calls when compiling" do
51
+ @ns.set_here :thingy, Rouge::Macro[lambda {|f|
52
+ Rouge::Seq::Cons[Rouge::Symbol[:list], *f.to_a]
53
+ }]
54
+ @compile.call("(let [list 'thing] (thingy (1 2 3)))").
55
+ should eq @read.call("(let [list 'thing] (list 1 2 3))")
56
+ end
57
+ end
58
+
59
+ describe "symbol lookup" do
60
+ it "should compile X. symbols to procs which call X.new" do
61
+ x = double("<class>")
62
+ x.stub(:new => nil)
63
+
64
+ @ns.set_here :x, x
65
+ x_new = @compile.call("x.")
66
+ x_new.should be_an_instance_of Rouge::Compiler::Resolved
67
+
68
+ x.should_receive(:new).with(1, :z)
69
+ x_new.res.call(1, :z)
70
+ end
71
+
72
+ it "should find the var in our namespace for an unqualified symbol" do
73
+ @ns.set_here :tiffany, "wha?"
74
+ @compile.call("tiffany").res.
75
+ should eq Rouge::Var.new(:"user.spec", :tiffany, "wha?")
76
+ end
77
+
78
+ it "should find the var in a referred ns for an unqualified symbol" do
79
+ v = @compile.call("def").res
80
+ v.should be_an_instance_of(Rouge::Var)
81
+ v.ns.should eq :"rouge.builtin"
82
+ v.name.should eq :def
83
+ v.deref.should be_an_instance_of(Rouge::Builtin)
84
+ end
85
+
86
+ it "should find the var in any namespace for a qualified symbol" do
87
+ v = @compile.call("ruby/Kernel").res
88
+ v.should be_an_instance_of(Rouge::Var)
89
+ v.ns.should eq :ruby
90
+ v.name.should eq :Kernel
91
+ v.deref.should eq Kernel
92
+ end
93
+
94
+ it "should find the method for a new class instantiation" do
95
+ m = @compile.call("ruby/String.").res
96
+ m.should be_an_instance_of Method
97
+ m.receiver.should eq String
98
+ m.name.should eq :new
99
+ end
100
+ end
101
+
102
+ describe "sub-compilation behaviour" do
103
+ it "should compile Arrays and Hashes" do
104
+ lambda {
105
+ @compile.call("[a]")
106
+ }.should raise_exception(Rouge::Namespace::VarNotFoundError)
107
+
108
+ @ns.set_here :a, :a
109
+ lambda {
110
+ @compile.call("[a]")
111
+ }.should_not raise_exception
112
+
113
+ lambda {
114
+ @compile.call("{b c}")
115
+ }.should raise_exception(Rouge::Namespace::VarNotFoundError)
116
+
117
+ @ns.set_here :b, :b
118
+ lambda {
119
+ @compile.call("{b c}")
120
+ }.should raise_exception(Rouge::Namespace::VarNotFoundError)
121
+
122
+ @ns.set_here :c, :c
123
+ lambda {
124
+ @compile.call("{b c}")
125
+ }.should_not raise_exception
126
+ end
127
+
128
+ it "should compile inline blocks to fns" do
129
+ @compile.call("(let [a 'thing] (a | [b] b))").
130
+ should eq @read.call("(let [a 'thing] (a | (fn [b] b)))")
131
+ end
132
+
133
+ it { @compile.call("()").should eq Rouge::Seq::Empty }
134
+ end
135
+ end
136
+
137
+ # vim: set sw=2 et cc=80:
@@ -0,0 +1,293 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'rouge'
4
+
5
+ describe Rouge::Context do
6
+ before do
7
+ @a = Rouge::Context.new nil
8
+ @ab = Rouge::Context.new @a
9
+ @abb = Rouge::Context.new @ab
10
+ @ac = Rouge::Context.new @a
11
+ @a.set_here :root, 42
12
+ @a.set_here :bah, (@bah = Object.new)
13
+ @ab.set_here :root, 80
14
+ @ac.set_here :non, 50
15
+
16
+ @spec = Rouge::Namespace[:"user.spec"].clear
17
+ @spec.refer Rouge::Namespace[:"rouge.builtin"]
18
+ @spec.set_here :tiffany, "wha?"
19
+ @in_spec = Rouge::Context.new @spec
20
+ @in_spec.set_here :blah, "code code"
21
+
22
+ @ns = Rouge[:"rouge.builtin"]
23
+ @context = Rouge::Context.new @ns
24
+ @nested_context = Rouge::Context.new @context
25
+ end
26
+
27
+ describe "the [] method" do
28
+ it "should get the closest binding" do
29
+ @a[:root].should eq 42
30
+ @ab[:root].should eq 80
31
+ @abb[:root].should eq 80
32
+ @ac[:root].should eq 42
33
+ # var, because it's from a namespace
34
+ @context[:let].deref.should be_an_instance_of Rouge::Builtin
35
+ end
36
+
37
+ it "should raise an exception if a binding is not found" do
38
+ lambda {
39
+ @a[:non]
40
+ }.should raise_exception(Rouge::Context::BindingNotFoundError)
41
+ end
42
+ end
43
+
44
+ describe "the ns method" do
45
+ it "should get the namespace of a context that has one" do
46
+ @context.ns.should eq Rouge::Namespace[:"rouge.builtin"]
47
+ end
48
+
49
+ it "should get the namespace of a nested context that has one" do
50
+ @nested_context.ns.should eq Rouge::Namespace[:"rouge.builtin"]
51
+ end
52
+
53
+ it "should return nil if a context has none" do
54
+ @a.ns.should eq nil
55
+ @ab.ns.should eq nil
56
+ end
57
+ end
58
+
59
+ describe "the set_here method" do
60
+ it "should set in the given context, shadowing outer bindings" do
61
+ @ac.set_here :root, 90
62
+ @ac[:root].should eq 90
63
+ @a[:root].should eq 42
64
+ end
65
+ end
66
+
67
+ describe "the set_lexical method" do
68
+ it "should set in the closest context" do
69
+ @abb.set_lexical :root, 777
70
+ @abb[:root].should eq 777
71
+ @ab[:root].should eq 777
72
+ @a[:root].should eq 42
73
+ end
74
+
75
+ it "should raise an exception if a closest binding is not found" do
76
+ lambda {
77
+ @abb.set_lexical :non, 10
78
+ }.should raise_exception(Rouge::Context::BindingNotFoundError)
79
+ end
80
+ end
81
+
82
+ describe "the eval method" do
83
+ it "should eval a form in this context without processing the backtrace" do
84
+ @a.eval([5]).should eq [5]
85
+
86
+ begin
87
+ @a.eval(Rouge::Seq::Cons[Rouge::Symbol[:blorgh]])
88
+ raise "failed!"
89
+ rescue Rouge::Context::BindingNotFoundError => e
90
+ e.backtrace.any? {|line| line =~ /^\(rouge\):/}.should be_false
91
+ end
92
+ end
93
+ end
94
+
95
+ describe "the readeval method" do
96
+ it "should post-process the backtrace", :pending do
97
+ Rouge.boot!
98
+ context = Rouge::Context.new Rouge[:user]
99
+
100
+ ex = nil
101
+ begin
102
+ context.readeval(<<-ROUGE)
103
+ (do
104
+ (defn z [] (throw (RuntimeError. "boo")))
105
+ (defn y [] (z))
106
+ (defn x [] (y))
107
+ (x))
108
+ ROUGE
109
+ rescue RuntimeError => e
110
+ ex = e
111
+ end
112
+
113
+ ex.should_not be_nil
114
+ ex.backtrace[0..3].
115
+ should eq ["(rouge):?:rouge.builtin/throw",
116
+ "(rouge):?:user/z",
117
+ "(rouge):?:user/y",
118
+ "(rouge):?:user/x"]
119
+ end
120
+
121
+ it "should compile with lexicals from the found context" do
122
+ context = Rouge::Context.new nil
123
+ context.set_here :quux, 4
124
+ context.set_here :bar, 5
125
+
126
+ Rouge::Compiler.should_receive(:compile).
127
+ with(context.ns, kind_of(Set), true) do |ns, lexicals, f|
128
+ lexicals.should eq Set[:quux, :bar]
129
+ end
130
+
131
+ context.readeval("true")
132
+ end
133
+ end
134
+
135
+ describe "the locate method" do
136
+ it "should find the method for a new call to a lexical" do
137
+ arg = double("<arg>")
138
+ m = @a.locate(Rouge::Symbol[:"bah."])
139
+ m.should be_an_instance_of Proc
140
+ @bah.should_receive(:new).with(arg)
141
+ m.call(arg)
142
+ end
143
+ end
144
+
145
+ it "should evaluate quotations to their unquoted form" do
146
+ @context.readeval("'x").should eq @ns.read("x")
147
+ @context.readeval("'':zzy").should eq @ns.read("':zzy")
148
+ end
149
+
150
+ describe "symbols" do
151
+ it "should evaluate symbols to the object within their context" do
152
+ @context.set_here :vitamin_b, "vegemite"
153
+ @context.readeval("vitamin_b").should eq "vegemite"
154
+
155
+ subcontext = Rouge::Context.new @context
156
+ subcontext.set_here :joy, [:yes]
157
+ subcontext.set_here :/, "wah"
158
+ subcontext.readeval("joy").should eq [:yes]
159
+ subcontext.readeval("vitamin_b").should eq "vegemite"
160
+ subcontext.readeval("/").should eq "wah"
161
+ end
162
+
163
+ it "should evaluate symbols in other namespaces" do
164
+ @context.readeval("ruby/Object").should eq Object
165
+ @context.readeval("ruby/Exception").should eq Exception
166
+ end
167
+
168
+ it "should evaluate nested objects" do
169
+ @context.readeval("ruby/Rouge.Context").should eq Rouge::Context
170
+ @context.readeval("ruby/Errno.EAGAIN").should eq Errno::EAGAIN
171
+ end
172
+ end
173
+
174
+ it "should evaluate other things to themselves" do
175
+ @context.eval(4).should eq 4
176
+ @context.eval("bleep bloop").should eq "bleep bloop"
177
+ @context.eval(:"nom it").should eq :"nom it"
178
+ @context.readeval("{:a :b, 1 2}").to_s.should eq({:a => :b, 1 => 2}.to_s)
179
+
180
+ l = lambda {}
181
+ @context.eval(l).should eq l
182
+
183
+ o = Object.new
184
+ @context.eval(o).should eq o
185
+ end
186
+
187
+ it "should evaluate hash and vector arguments" do
188
+ @context.readeval("{\"z\" 92, 'x ''5}").to_s.
189
+ should eq @ns.read("{\"z\" 92, x '5}").to_s
190
+
191
+ subcontext = Rouge::Context.new @context
192
+ subcontext.set_here :lolwut, "off"
193
+ subcontext.readeval("{lolwut [lolwut]}").
194
+ should eq @ns.read('{"off" ["off"]}')
195
+ end
196
+
197
+ describe "function calls" do
198
+ it "should evaluate function calls" do
199
+ subcontext = Rouge::Context.new @context
200
+ subcontext.set_here :f, lambda {|x| "hello #{x}"}
201
+ subcontext.readeval('(f "world")').should eq "hello world"
202
+ end
203
+
204
+ it "should evaluate macro calls" do
205
+ macro = Rouge::Macro[lambda {|n, *body|
206
+ Rouge::Seq::Cons[Rouge::Symbol[:let], Rouge::Seq::Cons[n, "example"].freeze,
207
+ *body]
208
+ }]
209
+
210
+ @ns.set_here :macro, macro
211
+
212
+ subcontext = Rouge::Context.new @context
213
+ subcontext.set_here :f, lambda {|x,y| x + y}
214
+ subcontext.readeval('(macro bar (f bar bar))').should eq "exampleexample"
215
+ end
216
+
217
+ it "should evaluate calls with inline blocks and block binds" do
218
+ @context.readeval('((fn [a | b] (b a)) 42 | [e] (./ e 2))').should eq 21
219
+ end
220
+
221
+ describe "Ruby interop" do
222
+ describe "new object creation" do
223
+ it "should call X.new with (X.)" do
224
+ klass = double("klass")
225
+ klass.should_receive(:new).with(@ns.read('a')).and_return(:b)
226
+
227
+ subcontext = Rouge::Context.new @context
228
+ subcontext.set_here :klass, klass
229
+ subcontext.readeval("(klass. 'a)").should eq :b
230
+ end
231
+ end
232
+
233
+ describe "generic method calls" do
234
+ it "should call x.y(:z) with (.y x 'z)" do
235
+ x = double("x")
236
+ x.should_receive(:y).with(@ns.read('z')).and_return(:tada)
237
+
238
+ subcontext = Rouge::Context.new @context
239
+ subcontext.set_here :x, x
240
+ subcontext.readeval("(.y x 'z)").should eq :tada
241
+ end
242
+
243
+ it "should call q.r(:s, &t) with (.r q 's | t)" do
244
+ q = double("q")
245
+ t = lambda {}
246
+ q.should_receive(:r).with(@ns.read('s'), &t).and_return(:bop)
247
+
248
+ subcontext = Rouge::Context.new @context
249
+ subcontext.set_here :q, q
250
+ subcontext.set_here :t, t
251
+ subcontext.readeval("(.r q 's | t)").should eq :bop
252
+ end
253
+
254
+ it "should call a.b(:c) {|d| d + 1} with (.b a 'c | [d] (.+ d 1))" do
255
+ a = double("a")
256
+ a.should_receive(:b) do |c, &b|
257
+ c.should eq @ns.read('c')
258
+ b.call(1).should eq 2
259
+ b.call(2).should eq 3
260
+ b.call(3).should eq 4
261
+ :ok
262
+ end
263
+
264
+ subcontext = Rouge::Context.new @context
265
+ subcontext.set_here :a, a
266
+ subcontext.readeval("(.b a 'c | [d] (.+ d 1))").should eq :ok
267
+ end
268
+ end
269
+ end
270
+
271
+ describe "keywords in call position" do
272
+ it { @context.readeval('(:nope {:yep true})').should be_nil }
273
+ it { @context.readeval('(:test "fail")').should be_nil }
274
+ it { @context.readeval('(:pale {:pale "ale"})').should eq "ale" }
275
+ it { @context.readeval('(:red {:white "moscato"} "merlot")').
276
+ should eq "merlot" }
277
+
278
+ it { expect { @context.readeval('(:will "raise" "an" "error")')
279
+ }.to raise_error(ArgumentError) }
280
+ end
281
+
282
+ describe "hashes in call position" do
283
+ it { @context.readeval('({:wham "bam"} :boom)').should be_nil }
284
+ it { @context.readeval('({:slam "dunk"} :slam)').should eq "dunk" }
285
+ it { @context.readeval('({:immortal false} :mortal true)').
286
+ should be_true }
287
+ it { expect { @context.readeval('({:this "will"} :raise "an" "error")')
288
+ }.to raise_error(ArgumentError) }
289
+ end
290
+ end
291
+ end
292
+
293
+ # vim: set sw=2 et cc=80:
data/spec/core_spec.rg ADDED
@@ -0,0 +1,123 @@
1
+ ;; -*- mode: clojure; -*-
2
+
3
+ (ns ^{:doc "Spec tests for the Rouge core."
4
+ :author "Arlen Christian Mart Cuss"}
5
+ spec.rouge.core
6
+ (:use rouge.test))
7
+
8
+ (testing "list"
9
+ (testing "empty list creation"
10
+ (is (= (list) '())))
11
+ (testing "unary list creation"
12
+ (is (= (list "trent") '("trent")))
13
+ (is (= (list true) '(true))))
14
+ (testing "n-ary list creation"
15
+ (is (= (apply list (range 1 6)) (.to_a (ruby/Range. 1 5))))))
16
+
17
+ (testing "or"
18
+ (is (let [q (atom 0)]
19
+ (or 1 (swap! q inc))
20
+ (= @q 0))))
21
+
22
+ (testing "and"
23
+ (is (let [q (atom 0)]
24
+ (and nil (swap! q inc))
25
+ (= @q 0))))
26
+
27
+ (testing "sequential"
28
+ (is (sequential? []))
29
+ (is (sequential? [1]))
30
+ (is (sequential? ()))
31
+ (is (sequential? '(1)))
32
+ (is (not (sequential? nil))))
33
+
34
+ (testing "="
35
+ (is (.== false (= () nil))))
36
+
37
+ (testing "seq"
38
+ (is (.nil? (seq ())))
39
+ (is (.nil? (seq nil)))
40
+ (is (.nil? (seq []))))
41
+
42
+ (testing "first"
43
+ (is (.nil? (first nil)))
44
+ (is (.nil? (first ()))))
45
+
46
+ (testing "rest"
47
+ (is (.== (rest nil) ()))
48
+ (is (.== (rest ()) ())))
49
+
50
+ (testing "next"
51
+ (is (.nil? (next nil)))
52
+ (is (.nil? (next ()))))
53
+
54
+ (testing "nth"
55
+ (is (= 1 (nth [1 2 3] 0)))
56
+ (is (= 2 (nth [1 2 3] 1)))
57
+ (is (= 3 (nth [1 2 3] 2))))
58
+
59
+ (defmacro sample-1 [f] `(do ~f))
60
+ (defmacro sample-2 [f] `(do ~@f ~@f))
61
+
62
+ (testing "macroexpand"
63
+ (is (= '(do x) (macroexpand '(sample-1 x))))
64
+ (is (= '(do x y x y) (macroexpand '(sample-2 (x y))))))
65
+
66
+ #_(testing "var passing"
67
+ (is (= #'my-var (do
68
+ (def my-var 4)
69
+ (let [take-var (fn [v] v)]
70
+ (take-var #'my-var))))))
71
+
72
+ #_(testing "for")
73
+
74
+ (testing "the -> macro"
75
+ (is (= 3 (macroexpand '(-> 3))))
76
+ (is (= '(:x 3) (macroexpand '(-> 3 :x))))
77
+ (is (= '(:y 3 2) (macroexpand '(-> 3 (:y 2)))))
78
+ (is (= '(:z (:y 3 2)) (macroexpand '(-> 3 (:y 2) :z))))
79
+ (is (= '(:z (:y 3 2) 8 9) (macroexpand '(-> 3 (:y 2) (:z 8 9)))))
80
+
81
+ (pending
82
+ ; this fails -- it compiles the inc ref.
83
+ (is (= '(inc 3) (macroexpand '(-> 3 inc))))
84
+ ; this fails too -- something weird.
85
+ (is (= '('inc 3) (macroexpand '(-> 3 'inc))))))
86
+
87
+ (testing "map"
88
+ (is (= Rouge.Seq.Lazy (class (map inc [1 2 3]))))
89
+ (is (= '(2 3 4) (map inc [1 2 3])))
90
+ (is (= 1
91
+ (let [q (atom 0)
92
+ lazy (map #(do (swap! q inc) (inc %)) [1 2 3])]
93
+ (first lazy)
94
+ @q)))
95
+ (is (= 1
96
+ (let [q (atom 0)
97
+ lazy (map #(do (swap! q inc) (inc %)) [1 2 3])]
98
+ (first lazy)
99
+ (first lazy)
100
+ @q)))
101
+ (is (= 2
102
+ (let [q (atom 0)
103
+ lazy (map #(do (swap! q inc) (inc %)) [1 2 3])]
104
+ (first (next lazy))
105
+ @q)))
106
+ (is (= 3
107
+ (let [q (atom 0)
108
+ lazy (map #(do (swap! q inc) (inc %)) [1 2 3])]
109
+ (first (next (next lazy)))
110
+ @q)))
111
+ (is (= 3
112
+ (let [q (atom 0)
113
+ lazy (map #(do (swap! q inc) (inc %)) [1 2 3])]
114
+ (first (next (next (next lazy))))
115
+ @q)))
116
+
117
+ (testing "in destructuring"
118
+ (is (= 2
119
+ (let [q (atom 0)
120
+ [hd & tl] (map #(do (swap! q inc) (inc %)) [1 2 3])]
121
+ @q)))))
122
+
123
+ ; vim: set ft=clojure: