rouge-lang 0.0.6 → 0.0.7
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/bin/rouge +1 -0
- data/lib/rouge.rb +1 -0
- data/lib/rouge/compiler.rb +19 -0
- data/lib/rouge/context.rb +12 -0
- data/lib/rouge/namespace.rb +5 -0
- data/lib/rouge/printer.rb +7 -0
- data/lib/rouge/reader.rb +30 -13
- data/lib/rouge/seq.rb +4 -0
- data/lib/rouge/version.rb +1 -1
- data/misc/TODO +4 -7
- data/spec/compiler_spec.rb +99 -94
- data/spec/namespace_spec.rb +1 -1
- data/spec/reader_spec.rb +26 -0
- data/spec/seq_spec.rb +25 -1
- metadata +9 -3
data/bin/rouge
CHANGED
data/lib/rouge.rb
CHANGED
data/lib/rouge/compiler.rb
CHANGED
@@ -24,6 +24,19 @@ module Rouge::Compiler
|
|
24
24
|
# TODO: cache found ns/var/context or no. of context parents.
|
25
25
|
form
|
26
26
|
else
|
27
|
+
if form.ns and !Rouge::Namespace.get(form.ns)
|
28
|
+
# ns specified but no actual namespace so-called.
|
29
|
+
if form.ns["/"]
|
30
|
+
# ns/Const/method ?
|
31
|
+
method = form.name
|
32
|
+
form = Rouge::Symbol[form.ns]
|
33
|
+
else
|
34
|
+
# Const/method ?
|
35
|
+
method = form.name
|
36
|
+
form = Rouge::Symbol[form.ns]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
27
40
|
resolved = form.ns ? Rouge[form.ns] : ns
|
28
41
|
|
29
42
|
lookups = form.name_parts
|
@@ -42,6 +55,12 @@ module Rouge::Compiler
|
|
42
55
|
resolved = klass.method(:new)
|
43
56
|
end
|
44
57
|
|
58
|
+
if method
|
59
|
+
receiver = resolved
|
60
|
+
receiver = receiver.deref if receiver.is_a?(Rouge::Var)
|
61
|
+
resolved = receiver.method(method)
|
62
|
+
end
|
63
|
+
|
45
64
|
Resolved.new resolved
|
46
65
|
end
|
47
66
|
when Array
|
data/lib/rouge/context.rb
CHANGED
@@ -210,6 +210,18 @@ class Rouge::Context
|
|
210
210
|
raise ArgumentError,
|
211
211
|
"Wrong number of args (#{num_args}) passed to ruby/Hash"
|
212
212
|
end
|
213
|
+
when Set
|
214
|
+
if num_args == 1
|
215
|
+
el = args[0]
|
216
|
+
if fun.include? el
|
217
|
+
el
|
218
|
+
else
|
219
|
+
nil
|
220
|
+
end
|
221
|
+
else
|
222
|
+
raise ArgumentError,
|
223
|
+
"Wrong number of args (#{num_args}) passed to ruby/Set"
|
224
|
+
end
|
213
225
|
else
|
214
226
|
fun.call(*args, &block)
|
215
227
|
end
|
data/lib/rouge/namespace.rb
CHANGED
@@ -28,6 +28,7 @@ class Rouge::Namespace
|
|
28
28
|
end
|
29
29
|
|
30
30
|
@refers << ns if not @refers.include? ns
|
31
|
+
self
|
31
32
|
end
|
32
33
|
|
33
34
|
def [](key)
|
@@ -79,6 +80,10 @@ class << Rouge::Namespace
|
|
79
80
|
@namespaces[ns] = new(ns)
|
80
81
|
end
|
81
82
|
|
83
|
+
def get(ns)
|
84
|
+
@namespaces[ns]
|
85
|
+
end
|
86
|
+
|
82
87
|
def []=(ns, value)
|
83
88
|
@namespaces[ns] = value
|
84
89
|
end
|
data/lib/rouge/printer.rb
CHANGED
@@ -53,6 +53,13 @@ module Rouge::Printer
|
|
53
53
|
print(kv[1], out)
|
54
54
|
end
|
55
55
|
out << "}"
|
56
|
+
when Set
|
57
|
+
out << "\#{"
|
58
|
+
form.each_with_index do |el, i|
|
59
|
+
print el, out
|
60
|
+
out << " " unless i == (form.size - 1)
|
61
|
+
end
|
62
|
+
out << "}"
|
56
63
|
when NilClass
|
57
64
|
out << "nil"
|
58
65
|
when TrueClass
|
data/lib/rouge/reader.rb
CHANGED
@@ -16,7 +16,6 @@ class Rouge::Reader
|
|
16
16
|
@gensyms = []
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
19
|
def lex
|
21
20
|
r =
|
22
21
|
case peek
|
@@ -247,32 +246,47 @@ class Rouge::Reader
|
|
247
246
|
end
|
248
247
|
|
249
248
|
def regexp
|
250
|
-
|
251
|
-
|
249
|
+
expression = ""
|
250
|
+
terminator = '"'
|
251
|
+
|
252
252
|
while true
|
253
|
-
|
253
|
+
char = @src[@n]
|
254
254
|
|
255
|
-
if
|
256
|
-
reader_raise EndOfDataError, "in regexp, got: #{
|
255
|
+
if char.nil?
|
256
|
+
reader_raise EndOfDataError, "in regexp, got: #{expression}"
|
257
257
|
end
|
258
258
|
|
259
259
|
@n += 1
|
260
260
|
|
261
|
-
if
|
261
|
+
if char == terminator
|
262
262
|
break
|
263
263
|
end
|
264
264
|
|
265
|
-
if
|
266
|
-
|
267
|
-
|
268
|
-
|
265
|
+
if char == ?\\
|
266
|
+
char = "\\"
|
267
|
+
|
268
|
+
# Prevent breaking early.
|
269
|
+
if peek == terminator
|
270
|
+
char << consume
|
269
271
|
end
|
270
272
|
end
|
271
273
|
|
272
|
-
|
274
|
+
expression << char
|
273
275
|
end
|
274
276
|
|
275
|
-
Regexp.new(
|
277
|
+
Regexp.new(expression).freeze
|
278
|
+
end
|
279
|
+
|
280
|
+
def set
|
281
|
+
s = Set.new
|
282
|
+
|
283
|
+
until peek == '}'
|
284
|
+
el = lex
|
285
|
+
s.add el
|
286
|
+
end
|
287
|
+
|
288
|
+
consume
|
289
|
+
s.freeze
|
276
290
|
end
|
277
291
|
|
278
292
|
def dispatch
|
@@ -284,6 +298,9 @@ class Rouge::Reader
|
|
284
298
|
Rouge::Symbol[:fn],
|
285
299
|
(1..count).map {|n| Rouge::Symbol[:"%#{n}"]}.freeze,
|
286
300
|
body]
|
301
|
+
when "{"
|
302
|
+
consume
|
303
|
+
set
|
287
304
|
when "'"
|
288
305
|
consume
|
289
306
|
Rouge::Seq::Cons[Rouge::Symbol[:var], lex]
|
data/lib/rouge/seq.rb
CHANGED
data/lib/rouge/version.rb
CHANGED
data/misc/TODO
CHANGED
@@ -1,8 +1,4 @@
|
|
1
1
|
NEXT:
|
2
|
-
- (count "xyz") => 3, (map … "xyz") etc.
|
3
|
-
- (ruby/)Time/now ==> Time.now
|
4
|
-
- continue decruft Context, shift tests to Compiler as appropriate; revise most
|
5
|
-
specs.
|
6
2
|
- extend DESTRUCTURE from LET to FN, etc.
|
7
3
|
- refactor DEFN, DEFMACRO, LET, BINDING(?) to make use of above
|
8
4
|
- may be faster for general wrapper classes to use Struct?
|
@@ -11,7 +7,6 @@ miscellaeny
|
|
11
7
|
-----------
|
12
8
|
- MACROEXPAND
|
13
9
|
- SPEED test. run core specs a few times and time.
|
14
|
-
- everything in ::Rouge should be accessible via rouge.builtin
|
15
10
|
- organise boot.rg
|
16
11
|
- test the whole core
|
17
12
|
- omit Ruby from tracebacks?
|
@@ -21,20 +16,22 @@ miscellaeny
|
|
21
16
|
ruby interop
|
22
17
|
------------
|
23
18
|
- accessing the scope/self (DSLs!)
|
24
|
-
- proc or lambda? setting ARITY.
|
19
|
+
- proc or lambda? setting ARITY. May need to emulate with generated code.
|
25
20
|
- DEFN/DEFMACRO/etc. should create strict lambdas, but FN/#() should not.
|
26
21
|
Emulate by accepting |*a| and selecting ourselves how strict we want to be.
|
27
22
|
|
28
23
|
"hard" tasks
|
29
24
|
------------
|
30
25
|
- have a core "apply" function which can be used by eval, defmacro, etc.
|
26
|
+
- continue decruft Context, shift tests to Compiler as appropriate; revise most
|
27
|
+
specs.
|
31
28
|
|
32
29
|
core lang
|
33
30
|
---------
|
34
31
|
- NON-dynamic/special vars
|
35
32
|
- INTERN
|
36
33
|
- FOR
|
37
|
-
- multi-form defn: (defn x ([a] ...) ([a b] ...))
|
34
|
+
- multi-form defn: (defn x ([a] ...) ([a b] ...)) (generalise defmacro's)
|
38
35
|
- atoms (actually multithreaded), refs, agents
|
39
36
|
- rest of the clojure core API (maybe even in clojure.core ns)
|
40
37
|
- clojure-ish IO libraries which expose async IO
|
data/spec/compiler_spec.rb
CHANGED
@@ -3,134 +3,139 @@ require 'spec_helper'
|
|
3
3
|
require 'rouge'
|
4
4
|
|
5
5
|
describe Rouge::Compiler do
|
6
|
-
|
7
|
-
@ns = Rouge[:"user.spec"].clear
|
8
|
-
@ns.refer Rouge[:"rouge.builtin"]
|
6
|
+
let(:ns) { Rouge[:"user.spec"].clear.refer(Rouge[:"rouge.builtin"]) }
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
let(:read) { lambda {|input|
|
9
|
+
Rouge::Reader.new(ns, input).lex
|
10
|
+
} }
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
12
|
+
let(:compile) { lambda {|input|
|
13
|
+
form = read.(input)
|
14
|
+
Rouge::Compiler.compile(ns, Set.new, form)
|
15
|
+
} }
|
19
16
|
|
20
17
|
describe "lexical lookup" do
|
21
|
-
it
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
18
|
+
it { expect { compile.("(fn [] a)")
|
19
|
+
}.to raise_exception(Rouge::Namespace::VarNotFoundError) }
|
20
|
+
|
21
|
+
it { expect { compile.("q")
|
22
|
+
}.to raise_exception(Rouge::Namespace::VarNotFoundError) }
|
23
|
+
|
24
|
+
it { expect { compile.("(let [x 8] x)").should eq read.("(let [x 8] x)")
|
25
|
+
}.to_not raise_exception }
|
26
|
+
|
27
|
+
it { expect { compile.("(let [x 8] y)")
|
28
|
+
}.to raise_exception(Rouge::Namespace::VarNotFoundError) }
|
29
|
+
|
30
|
+
it { expect { compile.("(let [x 8] ((fn [& b] (b)) | [e] e))")
|
31
|
+
}.to_not raise_exception }
|
32
|
+
|
33
|
+
it { expect { compile.("(let [x 8] ((fn [& b] (b)) | [e] f))")
|
34
|
+
}.to raise_exception(Rouge::Namespace::VarNotFoundError) }
|
47
35
|
end
|
48
36
|
|
49
37
|
describe "macro behaviour" do
|
50
|
-
|
51
|
-
|
38
|
+
before do
|
39
|
+
ns.set_here(:thingy, Rouge::Macro[lambda {|f|
|
52
40
|
Rouge::Seq::Cons[Rouge::Symbol[:list], *f.to_a]
|
53
|
-
}]
|
54
|
-
|
55
|
-
|
41
|
+
}])
|
42
|
+
end
|
43
|
+
|
44
|
+
it do
|
45
|
+
compile.("(let [list 'thing] (thingy (1 2 3)))").
|
46
|
+
should eq read.("(let [list 'thing] (list 1 2 3))")
|
56
47
|
end
|
57
48
|
end
|
58
49
|
|
59
50
|
describe "symbol lookup" do
|
60
|
-
it
|
61
|
-
x = double("
|
51
|
+
it do
|
52
|
+
x = double("class")
|
62
53
|
x.stub(:new => nil)
|
63
54
|
|
64
|
-
|
65
|
-
x_new =
|
55
|
+
ns.set_here(:x, x)
|
56
|
+
x_new = compile.("x.")
|
66
57
|
x_new.should be_an_instance_of Rouge::Compiler::Resolved
|
67
58
|
|
68
59
|
x.should_receive(:new).with(1, :z)
|
69
60
|
x_new.res.call(1, :z)
|
70
61
|
end
|
71
62
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
63
|
+
context "var in context ns" do
|
64
|
+
before { ns.set_here(:tiffany, "wha?") }
|
65
|
+
it { compile.("tiffany").res.
|
66
|
+
should eq Rouge::Var.new(:"user.spec", :tiffany, "wha?") }
|
67
|
+
end
|
68
|
+
|
69
|
+
context "vars in referred ns" do
|
70
|
+
subject { compile.("def").res }
|
71
|
+
it { should be_an_instance_of Rouge::Var }
|
72
|
+
its(:ns) { should eq :"rouge.builtin" }
|
73
|
+
its(:name) { should eq :def }
|
74
|
+
its(:deref) { should be_an_instance_of(Rouge::Builtin) }
|
76
75
|
end
|
77
76
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
77
|
+
context "var in qualified ns" do
|
78
|
+
subject { compile.("ruby/Kernel").res }
|
79
|
+
it { should be_an_instance_of Rouge::Var }
|
80
|
+
its(:ns) { should eq :ruby }
|
81
|
+
its(:name) { should eq :Kernel }
|
82
|
+
its(:deref) { should eq Kernel }
|
84
83
|
end
|
85
84
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
v.deref.should eq Kernel
|
85
|
+
context "class instantiation" do
|
86
|
+
subject { compile.("ruby/String.").res }
|
87
|
+
it { should be_an_instance_of Method }
|
88
|
+
its(:receiver) { should eq String }
|
89
|
+
its(:name) { should eq :new }
|
92
90
|
end
|
93
91
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
92
|
+
context "static method lookup" do
|
93
|
+
context "implied ns" do
|
94
|
+
before { ns.set_here(:String, String) }
|
95
|
+
subject { compile.("String/new").res }
|
96
|
+
it { should be_an_instance_of Method }
|
97
|
+
its(:receiver) { should eq String }
|
98
|
+
its(:name) { should eq :new }
|
99
|
+
end
|
100
|
+
|
101
|
+
context "fully-qualified" do
|
102
|
+
subject { compile.("ruby/String/new").res }
|
103
|
+
it { should be_an_instance_of Method }
|
104
|
+
its(:receiver) { should eq String }
|
105
|
+
its(:name) { should eq :new }
|
106
|
+
end
|
99
107
|
end
|
100
108
|
end
|
101
109
|
|
102
110
|
describe "sub-compilation behaviour" do
|
103
|
-
it
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
111
|
+
it { expect { compile.("[a]")
|
112
|
+
}.to raise_exception(Rouge::Namespace::VarNotFoundError) }
|
113
|
+
|
114
|
+
context do
|
115
|
+
before { ns.set_here(:a, :a) }
|
116
|
+
it { expect { compile.("[a]")
|
117
|
+
}.to_not raise_exception }
|
126
118
|
end
|
127
119
|
|
128
|
-
it
|
129
|
-
|
130
|
-
|
120
|
+
it { expect { compile.("{b c}")
|
121
|
+
}.to raise_exception(Rouge::Namespace::VarNotFoundError) }
|
122
|
+
|
123
|
+
context do
|
124
|
+
before { ns.set_here(:b, :b) }
|
125
|
+
it { expect { compile.("{b c}")
|
126
|
+
}.to raise_exception(Rouge::Namespace::VarNotFoundError) }
|
127
|
+
|
128
|
+
context do
|
129
|
+
before { ns.set_here(:c, :c) }
|
130
|
+
it { expect { compile.("{b c}")
|
131
|
+
}.to_not raise_exception }
|
132
|
+
end
|
131
133
|
end
|
132
134
|
|
133
|
-
it {
|
135
|
+
it { compile.("(let [a 'thing] (a | [b] b))").
|
136
|
+
should eq read.("(let [a 'thing] (a | (fn [b] b)))") }
|
137
|
+
|
138
|
+
it { compile.("()").should eq Rouge::Seq::Empty }
|
134
139
|
end
|
135
140
|
end
|
136
141
|
|
data/spec/namespace_spec.rb
CHANGED
data/spec/reader_spec.rb
CHANGED
@@ -175,6 +175,32 @@ describe Rouge::Reader do
|
|
175
175
|
end
|
176
176
|
end
|
177
177
|
|
178
|
+
describe "sets" do
|
179
|
+
context "the empty set" do
|
180
|
+
it { @ns.read('#{}').should eq Set.new }
|
181
|
+
end
|
182
|
+
|
183
|
+
context "multiple-element sets" do
|
184
|
+
it { @ns.read('#{1 2 3}').should eq Set.new.add(1).add(2).add(3) }
|
185
|
+
it { @ns.read('#{true () [] "no"}').
|
186
|
+
should eq Set.new([Rouge::Symbol[:true],
|
187
|
+
Rouge::Seq::Cons[],
|
188
|
+
[],
|
189
|
+
"no"]) }
|
190
|
+
end
|
191
|
+
|
192
|
+
context "nested sets" do
|
193
|
+
it { @ns.read('#{#{1} #{2} #{3}}').
|
194
|
+
should eq Set.new([Set.new([1]), Set.new([2]), Set.new([3])]) }
|
195
|
+
end
|
196
|
+
|
197
|
+
context "read as frozen" do
|
198
|
+
it { @ns.read('#{}').should be_frozen }
|
199
|
+
it { @ns.read('#{1}').should be_frozen }
|
200
|
+
it { @ns.read('#{1 2}').should be_frozen }
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
178
204
|
describe "quotations" do
|
179
205
|
it { @ns.read("'x").
|
180
206
|
should eq Rouge::Seq::Cons[Rouge::Symbol[:quote],
|
data/spec/seq_spec.rb
CHANGED
@@ -13,7 +13,7 @@ describe Rouge::Seq::ASeq do
|
|
13
13
|
describe "#seq" do
|
14
14
|
it "should return the original object" do
|
15
15
|
seq.seq.should be seq
|
16
|
-
end
|
16
|
+
end
|
17
17
|
end
|
18
18
|
|
19
19
|
describe "the unimplemented methods" do
|
@@ -196,6 +196,30 @@ describe Rouge::Seq do
|
|
196
196
|
let(:arrayseq) { Rouge::Seq::Array.new([:a], 0) }
|
197
197
|
it { Rouge::Seq.seq(arrayseq).should be arrayseq }
|
198
198
|
end
|
199
|
+
|
200
|
+
context Set do
|
201
|
+
subject { Rouge::Seq.seq(Set.new([1, 2, 3])) }
|
202
|
+
it { should be_an_instance_of Rouge::Seq::Array }
|
203
|
+
it { should eq Rouge::Seq::Array.new([1, 2, 3], 0) }
|
204
|
+
end
|
205
|
+
|
206
|
+
context Hash do
|
207
|
+
subject { Rouge::Seq.seq({:a => "a", :b => "b"}) }
|
208
|
+
it { should be_an_instance_of Rouge::Seq::Array }
|
209
|
+
it { should eq Rouge::Seq::Array.new([[:a, "a"], [:b, "b"]], 0) }
|
210
|
+
end
|
211
|
+
|
212
|
+
context String do
|
213
|
+
subject { Rouge::Seq.seq("foo") }
|
214
|
+
it { should be_an_instance_of Rouge::Seq::Array }
|
215
|
+
it { should eq Rouge::Seq::Array.new(['f', 'o', 'o'], 0) }
|
216
|
+
end
|
217
|
+
|
218
|
+
context Enumerator do
|
219
|
+
subject { Rouge::Seq.seq(1.upto(3)) }
|
220
|
+
it { should be_an_instance_of Rouge::Seq::Array }
|
221
|
+
it { should eq Rouge::Seq::Array.new([1, 2, 3], 0) }
|
222
|
+
end
|
199
223
|
end
|
200
224
|
end
|
201
225
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rouge-lang
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -186,15 +186,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
186
186
|
- - ! '>='
|
187
187
|
- !ruby/object:Gem::Version
|
188
188
|
version: '0'
|
189
|
+
segments:
|
190
|
+
- 0
|
191
|
+
hash: -862515277
|
189
192
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
190
193
|
none: false
|
191
194
|
requirements:
|
192
195
|
- - ! '>='
|
193
196
|
- !ruby/object:Gem::Version
|
194
197
|
version: '0'
|
198
|
+
segments:
|
199
|
+
- 0
|
200
|
+
hash: -862515277
|
195
201
|
requirements: []
|
196
202
|
rubyforge_project:
|
197
|
-
rubygems_version: 1.8.
|
203
|
+
rubygems_version: 1.8.24
|
198
204
|
signing_key:
|
199
205
|
specification_version: 3
|
200
206
|
summary: An implementation of Clojure for Ruby.
|