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.
- data/.autotest +11 -0
- data/.gitignore +20 -0
- data/.rspec +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +6 -0
- data/LICENSE +19 -0
- data/README.md +119 -0
- data/Rakefile +8 -0
- data/bin/rouge +2 -0
- data/lib/boot.rg +402 -0
- data/lib/rouge.rb +56 -0
- data/lib/rouge/atom.rb +25 -0
- data/lib/rouge/builtins.rb +596 -0
- data/lib/rouge/compiler.rb +108 -0
- data/lib/rouge/context.rb +235 -0
- data/lib/rouge/metadata.rb +19 -0
- data/lib/rouge/namespace.rb +125 -0
- data/lib/rouge/printer.rb +78 -0
- data/lib/rouge/reader.rb +433 -0
- data/lib/rouge/repl.rb +82 -0
- data/lib/rouge/seq.rb +221 -0
- data/lib/rouge/symbol.rb +77 -0
- data/lib/rouge/var.rb +69 -0
- data/lib/rouge/version.rb +7 -0
- data/lib/rouge/wrappers.rb +27 -0
- data/misc/TODO +45 -0
- data/misc/vimrc +1 -0
- data/rouge-lang.gemspec +28 -0
- data/spec/atom_spec.rb +39 -0
- data/spec/builtins_spec.rb +708 -0
- data/spec/compiler_spec.rb +137 -0
- data/spec/context_spec.rb +293 -0
- data/spec/core_spec.rg +123 -0
- data/spec/metadata_spec.rb +59 -0
- data/spec/namespace_spec.rb +125 -0
- data/spec/printer_spec.rb +191 -0
- data/spec/reader_spec.rb +422 -0
- data/spec/rouge_spec.rb +66 -0
- data/spec/seq_spec.rb +202 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/symbol_spec.rb +35 -0
- data/spec/var_spec.rb +61 -0
- data/spec/wrappers_spec.rb +51 -0
- metadata +216 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'rouge'
|
4
|
+
|
5
|
+
describe Rouge::Metadata do
|
6
|
+
before do
|
7
|
+
@class = Class.new do
|
8
|
+
include Rouge::Metadata
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "the metadata accessors" do
|
13
|
+
it "should be present on class instances" do
|
14
|
+
@class.new.should respond_to(:meta).with(0).arguments
|
15
|
+
@class.new.should respond_to(:meta=).with(1).argument
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should default to nil" do
|
19
|
+
@class.new.meta.should be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return the new value when set" do
|
23
|
+
i = @class.new
|
24
|
+
i.meta = {:x => 4}
|
25
|
+
i.meta.to_s.should eq({:x => 4}.to_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should not allow being set to anything other than a Hash or nil" do
|
29
|
+
lambda {
|
30
|
+
@class.new.meta = {:a => 1}
|
31
|
+
@class.new.meta = {}
|
32
|
+
@class.new.meta = {1 => 2, 3 => 4, 5 => {}}
|
33
|
+
@class.new.meta = nil
|
34
|
+
}.should_not raise_exception
|
35
|
+
|
36
|
+
lambda {
|
37
|
+
@class.new.meta = 4
|
38
|
+
}.should raise_exception(Rouge::Metadata::InvalidMetadataError)
|
39
|
+
|
40
|
+
lambda {
|
41
|
+
@class.new.meta = true
|
42
|
+
}.should raise_exception(Rouge::Metadata::InvalidMetadataError)
|
43
|
+
|
44
|
+
lambda {
|
45
|
+
@class.new.meta = Rouge::Symbol[:blah]
|
46
|
+
}.should raise_exception(Rouge::Metadata::InvalidMetadataError)
|
47
|
+
|
48
|
+
lambda {
|
49
|
+
@class.new.meta = []
|
50
|
+
}.should raise_exception(Rouge::Metadata::InvalidMetadataError)
|
51
|
+
|
52
|
+
lambda {
|
53
|
+
@class.new.meta = Rouge::Seq::Cons["what"]
|
54
|
+
}.should raise_exception(Rouge::Metadata::InvalidMetadataError)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# vim: set sw=2 et cc=80:
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'rouge'
|
4
|
+
|
5
|
+
describe Rouge::Namespace do
|
6
|
+
describe "the [] method" do
|
7
|
+
it "should vivify non-extant namespaces" do
|
8
|
+
Rouge::Namespace.exists?(:vivify_test).should eq false
|
9
|
+
Rouge::Namespace[:vivify_test].should be_an_instance_of Rouge::Namespace
|
10
|
+
Rouge::Namespace.exists?(:vivify_test).should eq true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "the Rouge[] shortcut" do
|
15
|
+
it "should directly call the [] method" do
|
16
|
+
Rouge::Namespace.should_receive(:[]).with(:trazzle)
|
17
|
+
Rouge[:trazzle]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "the set_here method" do
|
22
|
+
it "should create a var for the name, assigning the root to the value" do
|
23
|
+
w = Rouge::Namespace.new :w
|
24
|
+
w.set_here :waldorf, "Yes!"
|
25
|
+
w[:waldorf].should eq Rouge::Var.new(:w, :waldorf, "Yes!")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "the intern method" do
|
30
|
+
it "should create an unbound var for the name if it doesn't exist" do
|
31
|
+
m = Rouge::Namespace.new :m
|
32
|
+
m.intern :connor
|
33
|
+
m[:connor].should eq Rouge::Var.new(:m, :connor)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should do nothing if the var already exists" do
|
37
|
+
q = Rouge::Namespace.new :q
|
38
|
+
q.set_here :matthias, 50
|
39
|
+
q.intern :matthias
|
40
|
+
q[:matthias].should eq Rouge::Var.new(:q, :matthias, 50)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "the refer method" do
|
45
|
+
it "should cause items in one namespace to be locatable from the other" do
|
46
|
+
abc = Rouge::Namespace.new :abc
|
47
|
+
xyz = Rouge::Namespace.new :xyz
|
48
|
+
|
49
|
+
xyz.refer abc
|
50
|
+
|
51
|
+
abc.set_here :hello, :wow
|
52
|
+
xyz[:hello].deref.should eq :wow
|
53
|
+
end
|
54
|
+
|
55
|
+
it "may not be used to refer namespaces to themselves" do
|
56
|
+
lambda {
|
57
|
+
Rouge[:user].refer Rouge[:user]
|
58
|
+
}.should raise_exception(Rouge::Namespace::RecursiveNamespaceError)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "the destroy method" do
|
63
|
+
it "should obliterate a namespace" do
|
64
|
+
Rouge[:"user.spec2"].set_here :nope, :ok
|
65
|
+
Rouge::Namespace.destroy :"user.spec2"
|
66
|
+
lambda {
|
67
|
+
Rouge[:"user.spec2"][:nope]
|
68
|
+
}.should raise_exception(Rouge::Namespace::VarNotFoundError)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "the rouge.builtin namespace" do
|
73
|
+
before do
|
74
|
+
@ns = Rouge[:"rouge.builtin"]
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should contain elements from Rouge::Builtins" do
|
78
|
+
@ns[:let].deref.should be_an_instance_of Rouge::Builtin
|
79
|
+
@ns[:quote].deref.should be_an_instance_of Rouge::Builtin
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should not find objects from ruby" do
|
83
|
+
lambda {
|
84
|
+
@ns[:Float]
|
85
|
+
}.should raise_exception(Rouge::Namespace::VarNotFoundError)
|
86
|
+
lambda {
|
87
|
+
@ns[:String]
|
88
|
+
}.should raise_exception(Rouge::Namespace::VarNotFoundError)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should have a name" do
|
92
|
+
@ns.name.should eq :"rouge.builtin"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "the ruby namespace" do
|
97
|
+
before do
|
98
|
+
@ns = Rouge::Namespace[:ruby]
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should contain elements from Kernel" do
|
102
|
+
@ns[:Hash].should eq Rouge::Var.new(:ruby, :Hash, Hash)
|
103
|
+
@ns[:Fixnum].should eq Rouge::Var.new(:ruby, :Fixnum, Fixnum)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should contain global variables" do
|
107
|
+
@ns[:"$LOAD_PATH"].
|
108
|
+
should eq Rouge::Var.new(:ruby, :$LOAD_PATH, $LOAD_PATH)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should have a name" do
|
112
|
+
@ns.name.should eq :ruby
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "the read method" do
|
117
|
+
it "should read input in this namespace" do
|
118
|
+
Rouge::Namespace[:xyz].read("`ham").
|
119
|
+
should eq Rouge::Seq::Cons[Rouge::Symbol[:quote],
|
120
|
+
Rouge::Symbol[:"xyz/ham"]]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# vim: set sw=2 et cc=80:
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'rouge'
|
4
|
+
|
5
|
+
describe Rouge::Printer do
|
6
|
+
describe ".print" do
|
7
|
+
context "numbers" do
|
8
|
+
it { Rouge.print(12755, "").should eq "12755" }
|
9
|
+
end
|
10
|
+
|
11
|
+
context "symbols" do
|
12
|
+
it { Rouge.print(Rouge::Symbol[:loki], "").should eq "loki" }
|
13
|
+
it { Rouge.print(Rouge::Symbol[:/], "").should eq "/" }
|
14
|
+
it { Rouge.print(Rouge::Symbol[:wah?], "").should eq "wah?" }
|
15
|
+
it { Rouge.print(Rouge::Symbol[:"!ruby!"], "").should eq "!ruby!" }
|
16
|
+
it { Rouge.print(Rouge::Symbol[:nil], "").should eq "nil" }
|
17
|
+
it { Rouge.print(Rouge::Symbol[:true], "").should eq "true" }
|
18
|
+
it { Rouge.print(Rouge::Symbol[:false], "").should eq "false" }
|
19
|
+
end
|
20
|
+
|
21
|
+
context "keywords" do
|
22
|
+
context "plain keywords" do
|
23
|
+
it { Rouge.print(:loki, "").should eq ":loki" }
|
24
|
+
it { Rouge.print(:/, "").should eq ":/" }
|
25
|
+
it { Rouge.print(:wah?, "").should eq ":wah?" }
|
26
|
+
it { Rouge.print(:nil, "").should eq ":nil" }
|
27
|
+
it { Rouge.print(:true, "").should eq ":true" }
|
28
|
+
it { Rouge.print(:false, "").should eq ":false" }
|
29
|
+
end
|
30
|
+
|
31
|
+
context "string-symbols" do
|
32
|
+
it { Rouge.print(:"!ruby!", "").should eq ":\"!ruby!\"" }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "strings" do
|
37
|
+
context "plain strings" do
|
38
|
+
it { Rouge.print("akashi yo", "").should eq "\"akashi yo\"" }
|
39
|
+
it { Rouge.print("akashi\nwoah!", "").should eq "\"akashi\\nwoah!\"" }
|
40
|
+
end
|
41
|
+
|
42
|
+
context "escape sequences" do
|
43
|
+
it { Rouge.print("here \" goes", "").should eq "\"here \\\" goes\"" }
|
44
|
+
it { Rouge.print("here \\ goes", "").should eq "\"here \\\\ goes\"" }
|
45
|
+
it { Rouge.print("\a\b\e\f\n", "").should eq "\"\\a\\b\\e\\f\\n\"" }
|
46
|
+
it { Rouge.print("\r\t\v", "").should eq "\"\\r\\t\\v\"" }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "lists" do
|
51
|
+
context "empty list" do
|
52
|
+
it { Rouge.print(Rouge::Seq::Cons[], "").should eq "()" }
|
53
|
+
end
|
54
|
+
|
55
|
+
context "one-element lists" do
|
56
|
+
it { Rouge.print(Rouge::Seq::Cons[Rouge::Symbol[:tiffany]], "").
|
57
|
+
should eq "(tiffany)" }
|
58
|
+
it { Rouge.print(Rouge::Seq::Cons[:raaaaash], "").
|
59
|
+
should eq "(:raaaaash)" }
|
60
|
+
end
|
61
|
+
|
62
|
+
context "multiple-element lists" do
|
63
|
+
it { Rouge.print(Rouge::Seq::Cons[1, 2, 3], "").should eq "(1 2 3)" }
|
64
|
+
it { Rouge.print(Rouge::Seq::Cons[Rouge::Symbol[:true],
|
65
|
+
Rouge::Seq::Cons[], [], "no"], "").
|
66
|
+
should eq "(true () [] \"no\")" }
|
67
|
+
end
|
68
|
+
|
69
|
+
context "nested lists" do
|
70
|
+
it { Rouge.print(
|
71
|
+
Rouge::Seq::Cons[
|
72
|
+
Rouge::Seq::Cons[Rouge::Seq::Cons[3],
|
73
|
+
Rouge::Seq::Cons[Rouge::Seq::Cons[]]],
|
74
|
+
9,
|
75
|
+
Rouge::Seq::Cons[Rouge::Seq::Cons[8],
|
76
|
+
Rouge::Seq::Cons[8]]],
|
77
|
+
"").
|
78
|
+
should eq "(((3) (())) 9 ((8) (8)))" }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "vectors" do
|
83
|
+
context "the empty vector" do
|
84
|
+
it { Rouge.print([], "").should eq "[]" }
|
85
|
+
end
|
86
|
+
|
87
|
+
context "one-element vectors" do
|
88
|
+
it { Rouge.print([Rouge::Symbol[:tiffany]], "").should eq "[tiffany]" }
|
89
|
+
it { Rouge.print([:raaaaash], "").should eq "[:raaaaash]" }
|
90
|
+
end
|
91
|
+
|
92
|
+
context "multiple-element vectors" do
|
93
|
+
it { Rouge.print([1, 2, 3], "").should eq "[1 2 3]" }
|
94
|
+
it { Rouge.print([Rouge::Symbol[:true], Rouge::Seq::Cons[], [], "no"], "").
|
95
|
+
should eq "[true () [] \"no\"]" }
|
96
|
+
end
|
97
|
+
|
98
|
+
context "nested vectors" do
|
99
|
+
it { Rouge.print([[[3], [[]]], 9, [[8], [8]]], "").
|
100
|
+
should eq "[[[3] [[]]] 9 [[8] [8]]]" }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "quotations" do
|
105
|
+
it { Rouge.print(
|
106
|
+
Rouge::Seq::Cons[Rouge::Symbol[:quote], Rouge::Symbol[:x]],
|
107
|
+
"").
|
108
|
+
should eq "'x" }
|
109
|
+
it { Rouge.print(Rouge::Seq::Cons[Rouge::Symbol[:quote],
|
110
|
+
Rouge::Seq::Cons[Rouge::Symbol[:quote],
|
111
|
+
Rouge::Seq::Cons[Rouge::Seq::Cons[Rouge::Symbol[:quote],
|
112
|
+
Rouge::Symbol[:x]]]]], "").
|
113
|
+
should eq "''('x)" }
|
114
|
+
end
|
115
|
+
|
116
|
+
context "vars" do
|
117
|
+
it { Rouge.print(Rouge::Seq::Cons[Rouge::Symbol[:var],
|
118
|
+
Rouge::Symbol[:"x/y"]], "").
|
119
|
+
should eq "#'x/y" }
|
120
|
+
|
121
|
+
it { Rouge.print(Rouge::Seq::Cons[Rouge::Symbol[:var],
|
122
|
+
Rouge::Seq::Cons[Rouge::Symbol[:var],
|
123
|
+
Rouge::Seq::Cons[Rouge::Seq::Cons[Rouge::Symbol[:var],
|
124
|
+
Rouge::Symbol[:"x/y"]]]]], "").
|
125
|
+
should eq "#'#'(#'x/y)" }
|
126
|
+
|
127
|
+
it { Rouge.print(Rouge::Var.new(:x, :y), "").should eq "#'x/y" }
|
128
|
+
end
|
129
|
+
|
130
|
+
context "maps" do
|
131
|
+
context "the empty map" do
|
132
|
+
it { Rouge.print({}, "").should eq "{}" }
|
133
|
+
end
|
134
|
+
|
135
|
+
context "one-element maps" do
|
136
|
+
it { Rouge.print({Rouge::Symbol[:a] => 1}, "").should eq "{a 1}" }
|
137
|
+
it { Rouge.print({"quux" => [Rouge::Symbol[:lambast]]}, "").
|
138
|
+
should eq "{\"quux\" [lambast]}" }
|
139
|
+
end
|
140
|
+
|
141
|
+
context "multiple-element maps" do
|
142
|
+
# XXX(arlen): these tests rely on stable-ish Hash order
|
143
|
+
it { Rouge.print({:a => 1, :b => 2}, "").should eq "{:a 1, :b 2}" }
|
144
|
+
it { Rouge.print({:f => :f, :y => :y, :z => :z}, "").
|
145
|
+
should eq "{:f :f, :y :y, :z :z}" }
|
146
|
+
end
|
147
|
+
|
148
|
+
context "nested maps" do
|
149
|
+
# XXX(arlen): this test relies on stable-ish Hash order
|
150
|
+
it { Rouge.print({:a => {:z => 9},
|
151
|
+
:b => {:q => Rouge::Symbol[:q]}}, "").
|
152
|
+
should eq "{:a {:z 9}, :b {:q q}}" }
|
153
|
+
it { Rouge.print({{9 => 7} => 5}, "").should eq "{{9 7} 5}" }
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "fundamental objects" do
|
158
|
+
it { Rouge.print(nil, "").should eq "nil" }
|
159
|
+
it { Rouge.print(true, "").should eq "true" }
|
160
|
+
it { Rouge.print(false, "").should eq "false" }
|
161
|
+
end
|
162
|
+
|
163
|
+
context "Ruby classes" do
|
164
|
+
it { Rouge.print(Object, "").should eq "ruby/Object" }
|
165
|
+
it { Rouge.print(Class, "").should eq "ruby/Class" }
|
166
|
+
it { Rouge.print(Rouge::Context, "").should eq "ruby/Rouge.Context" }
|
167
|
+
|
168
|
+
let(:anon) { Class.new }
|
169
|
+
it { Rouge.print(anon, "").should eq anon.inspect }
|
170
|
+
end
|
171
|
+
|
172
|
+
context "builtin forms" do
|
173
|
+
it { Rouge.print(Rouge::Builtin[Rouge::Builtins.method(:let)], "").
|
174
|
+
should eq "rouge.builtin/let" }
|
175
|
+
it { Rouge.print(Rouge::Builtin[Rouge::Builtins.method(:def)], "").
|
176
|
+
should eq "rouge.builtin/def" }
|
177
|
+
end
|
178
|
+
|
179
|
+
context "unknown forms" do
|
180
|
+
let(:l) { lambda {} }
|
181
|
+
it { Rouge.print(l, "").should eq l.inspect }
|
182
|
+
end
|
183
|
+
|
184
|
+
context "regexp" do
|
185
|
+
let(:rx) { Regexp.new("abc") }
|
186
|
+
it { Rouge.print(rx, "").should eq "#\"abc\"" }
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# vim: set sw=2 et cc=80:
|
data/spec/reader_spec.rb
ADDED
@@ -0,0 +1,422 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'rouge'
|
4
|
+
|
5
|
+
describe Rouge::Reader do
|
6
|
+
before do
|
7
|
+
@ns = Rouge[:"user.spec"].clear
|
8
|
+
@ns.refer Rouge[:"rouge.builtin"]
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "reading numbers" do
|
12
|
+
it { @ns.read("12755").should eq 12755 }
|
13
|
+
it { @ns.read("2_50_9").should eq 2509 }
|
14
|
+
it { @ns.read("-999").should eq(-999) }
|
15
|
+
it { @ns.read("+1704").should eq(1704) }
|
16
|
+
|
17
|
+
context "floats" do
|
18
|
+
it { @ns.read("23.0").should be_an_instance_of Float }
|
19
|
+
it { @ns.read("23.0").should eq 23.0 }
|
20
|
+
it { @ns.read("23.1").should eq 23.1 }
|
21
|
+
it { @ns.read("-23.1").should eq(-23.1) }
|
22
|
+
it { @ns.read("+17.04").should eq(17.04) }
|
23
|
+
it { @ns.read("+1_000e-3").should eq(1.0) }
|
24
|
+
it { @ns.read("+1E+3").should eq(1000) }
|
25
|
+
end
|
26
|
+
|
27
|
+
context "binary" do
|
28
|
+
it { @ns.read("+0b10").should eq(2) }
|
29
|
+
it { @ns.read("-0b10").should eq(-2) }
|
30
|
+
end
|
31
|
+
|
32
|
+
context "hexadecimal" do
|
33
|
+
it { @ns.read("+0xf").should eq(15) }
|
34
|
+
it { @ns.read("-0xf").should eq(-15) }
|
35
|
+
end
|
36
|
+
|
37
|
+
context "octal" do
|
38
|
+
it { @ns.read("+0333").should eq(219) }
|
39
|
+
it { @ns.read("-0333").should eq(-219) }
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should fail on invalid numbers" do
|
43
|
+
lambda { @ns.read("1.2.3") }.should raise_exception(Rouge::Reader::UnexpectedCharacterError)
|
44
|
+
lambda { @ns.read("12..") }.should raise_exception(Rouge::Reader::UnexpectedCharacterError)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should read symbols" do
|
49
|
+
@ns.read("loki").should eq Rouge::Symbol[:loki]
|
50
|
+
@ns.read("wah?").should eq Rouge::Symbol[:wah?]
|
51
|
+
@ns.read("!ruby!").should eq Rouge::Symbol[:"!ruby!"]
|
52
|
+
@ns.read("nil").should eq Rouge::Symbol[:nil]
|
53
|
+
@ns.read("nil").should eq nil
|
54
|
+
@ns.read("true").should eq Rouge::Symbol[:true]
|
55
|
+
@ns.read("true").should eq true
|
56
|
+
@ns.read("false").should eq Rouge::Symbol[:false]
|
57
|
+
@ns.read("false").should eq false
|
58
|
+
@ns.read("&").should eq Rouge::Symbol[:&]
|
59
|
+
@ns.read("*").should eq Rouge::Symbol[:*]
|
60
|
+
@ns.read("-").should eq Rouge::Symbol[:-]
|
61
|
+
@ns.read("+").should eq Rouge::Symbol[:+]
|
62
|
+
@ns.read("/").should eq Rouge::Symbol[:/]
|
63
|
+
@ns.read("|").should eq Rouge::Symbol[:|]
|
64
|
+
@ns.read("$").should eq Rouge::Symbol[:"$"]
|
65
|
+
@ns.read(".").should eq Rouge::Symbol[:"."]
|
66
|
+
@ns.read(".[]").should eq Rouge::Symbol[:".[]"]
|
67
|
+
@ns.read("=").should eq Rouge::Symbol[:"="]
|
68
|
+
@ns.read("%").should eq Rouge::Symbol[:"%"]
|
69
|
+
@ns.read(">").should eq Rouge::Symbol[:">"]
|
70
|
+
@ns.read("<").should eq Rouge::Symbol[:"<"]
|
71
|
+
@ns.read("%50").should eq Rouge::Symbol[:"%50"]
|
72
|
+
@ns.read("xyz#").should eq Rouge::Symbol[:"xyz#"]
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "keywords" do
|
76
|
+
it "should read plain keywords" do
|
77
|
+
@ns.read(":loki").should eq :loki
|
78
|
+
@ns.read(":/").should eq :/
|
79
|
+
@ns.read(":wah?").should eq :wah?
|
80
|
+
@ns.read(":nil").should eq :nil
|
81
|
+
@ns.read(":true").should eq :true
|
82
|
+
@ns.read(":false").should eq :false
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should read string-symbols" do
|
86
|
+
@ns.read(":\"!ruby!\"").should eq :"!ruby!"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "strings" do
|
91
|
+
it "should read plain strings" do
|
92
|
+
@ns.read("\"akashi yo\"").should eq "akashi yo"
|
93
|
+
@ns.read("\"akashi \n woah!\"").should eq "akashi \n woah!"
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should read escape sequences" do
|
97
|
+
@ns.read("\"here \\\" goes\"").should eq "here \" goes"
|
98
|
+
@ns.read("\"here \\\\ goes\"").should eq "here \\ goes"
|
99
|
+
@ns.read("\"\\a\\b\\e\\f\\n\\r\"").should eq "\a\b\e\f\n\r"
|
100
|
+
@ns.read("\"\\s\\t\\v\"").should eq "\s\t\v"
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should read strings as frozen" do
|
104
|
+
@ns.read("\"bah\"").should be_frozen
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "lists" do
|
109
|
+
it "should read the empty list" do
|
110
|
+
@ns.read("()").should eq Rouge::Seq::Cons[]
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should read one-element lists" do
|
114
|
+
@ns.read("(tiffany)").should eq Rouge::Seq::Cons[Rouge::Symbol[:tiffany]]
|
115
|
+
@ns.read("(:raaaaash)").
|
116
|
+
should eq Rouge::Seq::Cons[:raaaaash]
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should read multiple-element lists" do
|
120
|
+
@ns.read("(1 2 3)").should eq Rouge::Seq::Cons[1, 2, 3]
|
121
|
+
@ns.read("(true () [] \"no\")").
|
122
|
+
should eq Rouge::Seq::Cons[Rouge::Symbol[:true], Rouge::Seq::Cons[], [], "no"]
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should read nested lists" do
|
126
|
+
@ns.read("(((3) (())) 9 ((8) (8)))").
|
127
|
+
should eq Rouge::Seq::Cons[Rouge::Seq::Cons[Rouge::Seq::Cons[3],
|
128
|
+
Rouge::Seq::Cons[Rouge::Seq::Cons[]]], 9,
|
129
|
+
Rouge::Seq::Cons[Rouge::Seq::Cons[8], Rouge::Seq::Cons[8]]]
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should read lists as frozen" do
|
133
|
+
@ns.read("()").should be_frozen
|
134
|
+
@ns.read("(1)").should be_frozen
|
135
|
+
@ns.read("(1 2)").should be_frozen
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "vectors" do
|
140
|
+
it "should read the empty vector" do
|
141
|
+
@ns.read("[]").should eq []
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should read one-element vectors" do
|
145
|
+
@ns.read("[tiffany]").should eq [Rouge::Symbol[:tiffany]]
|
146
|
+
@ns.read("[:raaaaash]").should eq [:raaaaash]
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should read multiple-element vectors" do
|
150
|
+
@ns.read("[1 2 3]").should eq [1, 2, 3]
|
151
|
+
@ns.read("[true () [] \"no\"]").
|
152
|
+
should eq [Rouge::Symbol[:true], Rouge::Seq::Cons[], [], "no"]
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should read nested vectors" do
|
156
|
+
@ns.read("[[[3] [[]]] 9 [[8] [8]]]").
|
157
|
+
should eq [[[3], [[]]], 9, [[8], [8]]]
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should read vectors as frozen" do
|
161
|
+
@ns.read("[]").should be_frozen
|
162
|
+
@ns.read("[1]").should be_frozen
|
163
|
+
@ns.read("[1 2]").should be_frozen
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "quotations" do
|
168
|
+
it "should read 'X as (QUOTE X)" do
|
169
|
+
@ns.read("'x").
|
170
|
+
should eq Rouge::Seq::Cons[Rouge::Symbol[:quote], Rouge::Symbol[:x]]
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should read ''('X) as (QUOTE (QUOTE ((QUOTE X))))" do
|
174
|
+
@ns.read("''('x)").
|
175
|
+
should eq Rouge::Seq::Cons[Rouge::Symbol[:quote],
|
176
|
+
Rouge::Seq::Cons[Rouge::Symbol[:quote],
|
177
|
+
Rouge::Seq::Cons[Rouge::Seq::Cons[Rouge::Symbol[:quote],
|
178
|
+
Rouge::Symbol[:x]]]]]
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe "vars" do
|
183
|
+
it "should read #'X as (VAR X)" do
|
184
|
+
@ns.read("#'x").
|
185
|
+
should eq Rouge::Seq::Cons[Rouge::Symbol[:var], Rouge::Symbol[:x]]
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should read #'#'(#'X) as (VAR (VAR ((VAR X))))" do
|
189
|
+
@ns.read("#'#'(#'x)").
|
190
|
+
should eq Rouge::Seq::Cons[Rouge::Symbol[:var],
|
191
|
+
Rouge::Seq::Cons[Rouge::Symbol[:var],
|
192
|
+
Rouge::Seq::Cons[Rouge::Seq::Cons[Rouge::Symbol[:var],
|
193
|
+
Rouge::Symbol[:x]]]]]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
describe "maps" do
|
198
|
+
it "should read the empty map" do
|
199
|
+
@ns.read("{}").should eq({})
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should read one-element maps" do
|
203
|
+
@ns.read("{a 1}").to_s.should eq({Rouge::Symbol[:a] => 1}.to_s)
|
204
|
+
@ns.read("{\"quux\" [lambast]}").
|
205
|
+
should eq({"quux" => [Rouge::Symbol[:lambast]]})
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should read multiple-element maps" do
|
209
|
+
@ns.read("{:a 1 :b 2}").should eq({:a => 1, :b => 2})
|
210
|
+
@ns.read("{:f :f, :y :y\n:z :z}").
|
211
|
+
should eq({:f => :f, :y => :y, :z => :z})
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should read nested maps" do
|
215
|
+
@ns.read("{:a {:z 9} :b {:q q}}").should eq(
|
216
|
+
{:a => {:z => 9}, :b => {:q => Rouge::Symbol[:q]}})
|
217
|
+
@ns.read("{{9 7} 5}").should eq({{9 => 7} => 5})
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should read maps as frozen" do
|
221
|
+
@ns.read("{}").should be_frozen
|
222
|
+
@ns.read("{:a 1}").should be_frozen
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe "whitespace behaviour" do
|
227
|
+
it "should not fail with trailing whitespace" do
|
228
|
+
lambda {
|
229
|
+
@ns.read(":hello \n\n\t\t ").should eq :hello
|
230
|
+
}.should_not raise_exception
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should deal with whitespace in strange places" do
|
234
|
+
lambda {
|
235
|
+
@ns.read("[1 ]").should eq [1]
|
236
|
+
@ns.read(" [ 2 ] ").should eq [2]
|
237
|
+
}.should_not raise_exception
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe "empty reads" do
|
242
|
+
it "should fail on empty reads" do
|
243
|
+
lambda {
|
244
|
+
@ns.read("")
|
245
|
+
}.should raise_exception(Rouge::Reader::EndOfDataError)
|
246
|
+
|
247
|
+
lambda {
|
248
|
+
@ns.read(" \n ")
|
249
|
+
}.should raise_exception(Rouge::Reader::EndOfDataError)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
describe "comments" do
|
254
|
+
it "should ignore comments" do
|
255
|
+
@ns.read("42 ;what!").should eq 42
|
256
|
+
@ns.read("[42 ;what!\n15]").should eq [42, 15]
|
257
|
+
|
258
|
+
lambda {
|
259
|
+
@ns.read(";what!")
|
260
|
+
}.should raise_exception(Rouge::Reader::EndOfDataError)
|
261
|
+
|
262
|
+
@ns.read(";what!\nhmm").should eq Rouge::Symbol[:hmm]
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe "syntax-quoting" do
|
267
|
+
describe "non-cons lists" do
|
268
|
+
it "should quote non-cons lists" do
|
269
|
+
@ns.read('`3').should eq @ns.read("'3")
|
270
|
+
@ns.read('`"my my my"').should eq @ns.read(%{'"my my my"})
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should dequote within non-cons lists" do
|
274
|
+
@ns.read('`~3').should eq @ns.read("3")
|
275
|
+
@ns.read('``~3').should eq @ns.read("'3")
|
276
|
+
@ns.read('``~~3').should eq @ns.read("3")
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should qualify symbols" do
|
280
|
+
@ns.read('`a').should eq @ns.read("'user.spec/a")
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should not qualify special symbols" do
|
284
|
+
@ns.read('`.a').should eq @ns.read("'.a")
|
285
|
+
@ns.read('`&').should eq @ns.read("'&")
|
286
|
+
@ns.read('`|').should eq @ns.read("'|")
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
describe "cons-lists" do
|
291
|
+
it "should quote cons lists" do
|
292
|
+
@ns.read('`(1 2)').should eq @ns.read("(list '1 '2)")
|
293
|
+
@ns.read('`(a b)').
|
294
|
+
should eq @ns.read("(list 'user.spec/a 'user.spec/b)")
|
295
|
+
end
|
296
|
+
|
297
|
+
it "should dequote within cons lists" do
|
298
|
+
@ns.read('`(a ~b)').should eq @ns.read("(list 'user.spec/a b)")
|
299
|
+
@ns.read('`(a ~(b `(c ~d)))').
|
300
|
+
should eq @ns.read("(list 'user.spec/a (b (list 'user.spec/c d)))")
|
301
|
+
@ns.read('`(a `(b ~c))').
|
302
|
+
should eq @ns.read("(list 'user.spec/a (list 'user.spec/list " \
|
303
|
+
"(list 'quote 'user.spec/b) 'user.spec/c))")
|
304
|
+
@ns.read('`~`(x)').should eq @ns.read("(list 'user.spec/x)")
|
305
|
+
end
|
306
|
+
|
307
|
+
it "should dequote within maps" do
|
308
|
+
@ns.read('`{a ~b}').to_s.should eq @ns.read("{'user.spec/a b}").to_s
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should splice within seqs and vectors" do
|
312
|
+
@ns.read('`(a ~@b c)').
|
313
|
+
should eq @ns.read("(seq (concat (list 'user.spec/a) b " \
|
314
|
+
"(list 'user.spec/c)))")
|
315
|
+
@ns.read('`(~@(a b) ~c)').
|
316
|
+
should eq @ns.read("(seq (concat (a b) (list c)))")
|
317
|
+
@ns.read('`[a ~@b c]').should eq @ns.read(<<-ROUGE)
|
318
|
+
(apply vector (concat (list 'user.spec/a) b (list 'user.spec/c)))
|
319
|
+
ROUGE
|
320
|
+
@ns.read('`[~@(a b) ~c]').
|
321
|
+
should eq @ns.read("(apply vector (concat (a b) (list c)))")
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe "gensyms" do
|
326
|
+
it "should read as unique in each invocation" do
|
327
|
+
a1 = @ns.read('`a#')
|
328
|
+
a2 = @ns.read('`a#')
|
329
|
+
a1.to_s.should_not eq a2.to_s
|
330
|
+
end
|
331
|
+
|
332
|
+
it "should read identically within each invocation" do
|
333
|
+
as = @ns.read('`(a# a# `(a# a#))')
|
334
|
+
as = as
|
335
|
+
.map {|e| e.respond_to?(:to_a) ? e.to_a : e}.to_a.flatten
|
336
|
+
.flat_map {|e| e.respond_to?(:to_a) ? e.to_a : e}
|
337
|
+
.flat_map {|e| e.respond_to?(:to_a) ? e.to_a : e}
|
338
|
+
.find_all {|e|
|
339
|
+
e.is_a?(Rouge::Symbol) and e.name.to_s =~ /^a/
|
340
|
+
}
|
341
|
+
as.length.should eq 4
|
342
|
+
as[0].should eq as[1]
|
343
|
+
as[2].should eq as[3]
|
344
|
+
as[0].should_not eq as[2]
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
describe "anonymous functions" do
|
350
|
+
it "should read anonymous functions" do
|
351
|
+
@ns.read('#(1)').should eq @ns.read('(fn [] (1))')
|
352
|
+
@ns.read('#(do 1)').should eq @ns.read('(fn [] (do 1))')
|
353
|
+
@ns.read('#(%)').should eq @ns.read('(fn [%1] (%1))')
|
354
|
+
@ns.read('#(%2)').should eq @ns.read('(fn [%1 %2] (%2))')
|
355
|
+
@ns.read('#(%5)').should eq @ns.read('(fn [%1 %2 %3 %4 %5] (%5))')
|
356
|
+
@ns.read('#(%2 %)').should eq @ns.read('(fn [%1 %2] (%2 %1))')
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
describe "metadata" do
|
361
|
+
it "should read metadata" do
|
362
|
+
y = @ns.read('^{:x 1} y')
|
363
|
+
y.should eq Rouge::Symbol[:y]
|
364
|
+
y.meta.to_s.should eq({:x => 1}.to_s)
|
365
|
+
end
|
366
|
+
|
367
|
+
it "should stack metadata" do
|
368
|
+
y = @ns.read('^{:y 2} ^{:y 3 :z 2} y')
|
369
|
+
y.should eq Rouge::Symbol[:y]
|
370
|
+
y.meta.should include({:y => 2, :z => 2})
|
371
|
+
end
|
372
|
+
|
373
|
+
it "should assign tags" do
|
374
|
+
y = @ns.read('^"xyz" y')
|
375
|
+
y.should eq Rouge::Symbol[:y]
|
376
|
+
y.meta.should include({:tag => "xyz"})
|
377
|
+
end
|
378
|
+
|
379
|
+
it "should assign symbol markers" do
|
380
|
+
y = @ns.read('^:blargh y')
|
381
|
+
y.should eq Rouge::Symbol[:y]
|
382
|
+
y.meta.should include({:blargh => true})
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
describe "deref" do
|
387
|
+
it "should read derefs" do
|
388
|
+
@ns.read('@(boo)').should eq @ns.read('(rouge.core/deref (boo))')
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
describe "multiple reading" do
|
393
|
+
it "should read multiple forms in turn" do
|
394
|
+
r = Rouge::Reader.new(@ns, "a b c")
|
395
|
+
r.lex.should eq Rouge::Symbol[:a]
|
396
|
+
r.lex.should eq Rouge::Symbol[:b]
|
397
|
+
r.lex.should eq Rouge::Symbol[:c]
|
398
|
+
|
399
|
+
lambda {
|
400
|
+
r.lex
|
401
|
+
}.should raise_exception(Rouge::Reader::EndOfDataError)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
describe "the ns property" do
|
406
|
+
it "should return the ns the reader is in" do
|
407
|
+
Rouge::Reader.new(@ns, "").ns.should be @ns
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
describe "the comment dispatch" do
|
412
|
+
it "should be completely ignored" do
|
413
|
+
@ns.read('#_(xyz abc) :f').should eq :f
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
describe "regexp" do
|
418
|
+
it { @ns.read('#"abc"').should be_an_instance_of Regexp }
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
# vim: set sw=2 et cc=80:
|