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,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: