edn 1.0.3 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +94 -21
- data/Rakefile +5 -0
- data/edn.gemspec +4 -4
- data/lib/edn.rb +4 -20
- data/lib/edn/char_stream.rb +84 -0
- data/lib/edn/parser.rb +333 -201
- data/lib/edn/reader.rb +11 -24
- data/lib/edn/version.rb +1 -1
- data/spec/edn/char_stream_spec.rb +111 -0
- data/spec/edn/parser_spec.rb +31 -79
- data/spec/edn/reader_spec.rb +9 -9
- data/spec/edn_spec.rb +39 -5
- data/spec/spec_helper.rb +16 -4
- metadata +11 -26
- data/lib/edn/string_transformer.rb +0 -46
- data/lib/edn/transform.rb +0 -59
- data/lib/parslet/ignore.rb +0 -24
- data/spec/edn/transform_spec.rb +0 -176
data/lib/edn/reader.rb
CHANGED
@@ -1,36 +1,23 @@
|
|
1
1
|
module EDN
|
2
2
|
class Reader
|
3
|
-
include Enumerable
|
4
3
|
|
5
|
-
def initialize(
|
6
|
-
@parser = Parser.new
|
7
|
-
@transform = Transform.new
|
8
|
-
@original_text = text
|
9
|
-
@text = text
|
4
|
+
def initialize(source)
|
5
|
+
@parser = Parser.new(source)
|
10
6
|
end
|
11
7
|
|
12
|
-
def
|
13
|
-
|
8
|
+
def read(eof_value = NOTHING)
|
9
|
+
result = @parser.read
|
10
|
+
if result == EOF
|
11
|
+
raise "Unexpected end of file" if eof_value == NOTHING
|
12
|
+
return eof_value
|
13
|
+
end
|
14
|
+
result
|
14
15
|
end
|
15
16
|
|
16
17
|
def each
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
until eof?
|
21
|
-
yield read
|
18
|
+
until (result = @parser.read) == EOF
|
19
|
+
yield result
|
22
20
|
end
|
23
21
|
end
|
24
|
-
|
25
|
-
def reset!
|
26
|
-
@text = @original_text
|
27
|
-
end
|
28
|
-
|
29
|
-
def read
|
30
|
-
raise "EDN::Reader is out of string!" if eof?
|
31
|
-
element, rest = @parser.parse_prefix(@text)
|
32
|
-
@text = rest
|
33
|
-
@transform.apply(element)
|
34
|
-
end
|
35
22
|
end
|
36
23
|
end
|
data/lib/edn/version.rb
CHANGED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EDN::CharStream do
|
4
|
+
it "reads a stream in order" do
|
5
|
+
s = EDN::CharStream.new(io_for("abc"))
|
6
|
+
s.current.should == "a"
|
7
|
+
s.advance.should == "b"
|
8
|
+
s.advance.should == "c"
|
9
|
+
s.advance.should == :eof
|
10
|
+
s.current.should == :eof
|
11
|
+
end
|
12
|
+
|
13
|
+
it "keeps returning the current until your advance" do
|
14
|
+
s = EDN::CharStream.new(io_for("abc"))
|
15
|
+
s.current.should == "a"
|
16
|
+
s.current.should == "a"
|
17
|
+
s.advance.should == "b"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "knows if the current char is a digit" do
|
21
|
+
s = EDN::CharStream.new(io_for("4f"))
|
22
|
+
s.digit?.should be_true
|
23
|
+
s.advance
|
24
|
+
s.digit?.should be_false
|
25
|
+
end
|
26
|
+
|
27
|
+
it "knows if the current char is an alpha" do
|
28
|
+
s = EDN::CharStream.new(io_for("a9"))
|
29
|
+
s.alpha?.should be_true
|
30
|
+
s.advance
|
31
|
+
s.alpha?.should be_false
|
32
|
+
end
|
33
|
+
|
34
|
+
it "knows if the current char is whitespace" do
|
35
|
+
s = EDN::CharStream.new(io_for("a b\nc\td,"))
|
36
|
+
s.ws?.should be_false # a
|
37
|
+
|
38
|
+
s.advance
|
39
|
+
s.ws?.should be_true # " "
|
40
|
+
|
41
|
+
s.advance
|
42
|
+
s.ws?.should be_false # b
|
43
|
+
|
44
|
+
s.advance
|
45
|
+
s.ws?.should be_true # \n
|
46
|
+
|
47
|
+
s.advance
|
48
|
+
s.ws?.should be_false # c
|
49
|
+
|
50
|
+
s.advance
|
51
|
+
s.ws?.should be_true # \t
|
52
|
+
|
53
|
+
s.advance
|
54
|
+
s.ws?.should be_false # d
|
55
|
+
|
56
|
+
s.advance
|
57
|
+
s.ws?.should be_true # ,
|
58
|
+
end
|
59
|
+
|
60
|
+
it "knows if the current char is a newline" do
|
61
|
+
s = EDN::CharStream.new(io_for("a\nb\rc"))
|
62
|
+
s.newline?.should be_false # a
|
63
|
+
|
64
|
+
s.advance
|
65
|
+
s.newline?.should be_true # \n
|
66
|
+
|
67
|
+
s.advance
|
68
|
+
s.newline?.should be_false # b
|
69
|
+
|
70
|
+
s.advance
|
71
|
+
s.newline?.should be_true # \r
|
72
|
+
|
73
|
+
s.advance
|
74
|
+
s.newline?.should be_false # c
|
75
|
+
end
|
76
|
+
|
77
|
+
it "knows if it is at the eof" do
|
78
|
+
s = EDN::CharStream.new(io_for("abc"))
|
79
|
+
s.eof?.should be_false # a
|
80
|
+
s.advance
|
81
|
+
s.eof?.should be_false # b
|
82
|
+
s.advance
|
83
|
+
s.eof?.should be_false # c
|
84
|
+
s.advance
|
85
|
+
s.eof?.should be_true
|
86
|
+
end
|
87
|
+
|
88
|
+
it "knows how to skip past a char" do
|
89
|
+
s = EDN::CharStream.new(io_for("abc"))
|
90
|
+
s.skip_past("a").should == "a"
|
91
|
+
s.current.should == "b"
|
92
|
+
end
|
93
|
+
|
94
|
+
it "knows how not to skip a char" do
|
95
|
+
s = EDN::CharStream.new(io_for("abc"))
|
96
|
+
s.skip_past("X").should be_nil
|
97
|
+
end
|
98
|
+
|
99
|
+
it "knows how skip to the end of a line" do
|
100
|
+
s = EDN::CharStream.new(io_for("abc\ndef"))
|
101
|
+
s.skip_to_eol
|
102
|
+
s.current.should == "\n"
|
103
|
+
s.advance.should == "d"
|
104
|
+
end
|
105
|
+
|
106
|
+
it "knows how skip whitespace" do
|
107
|
+
s = EDN::CharStream.new(io_for(" \n \t,,,,abc"))
|
108
|
+
s.skip_ws
|
109
|
+
s.current.should == "a"
|
110
|
+
end
|
111
|
+
end
|
data/spec/edn/parser_spec.rb
CHANGED
@@ -7,60 +7,32 @@ describe EDN::Parser do
|
|
7
7
|
|
8
8
|
it "can contain comments" do
|
9
9
|
edn = ";; This is some sample data\n[1 2 ;; the first two values\n3]"
|
10
|
-
|
11
|
-
parser.parse(edn).should ==
|
12
|
-
{:vector=>[{:integer=>"1", :precision=>nil},
|
13
|
-
{:integer=>"2", :precision=>nil},
|
14
|
-
{:integer=>"3", :precision=>nil}]}
|
10
|
+
EDN.read(edn).should == [1, 2, 3]
|
15
11
|
end
|
16
12
|
|
17
13
|
it "can discard using the discard reader macro" do
|
18
|
-
edn = "[1 2 #_3 {:foo #_bar baz}]"
|
19
|
-
|
20
|
-
parser.parse(edn).should ==
|
21
|
-
{:vector=>[{:integer=>"1", :precision=>nil},
|
22
|
-
{:integer=>"2", :precision=>nil},
|
23
|
-
{:map => [{:key=>{:keyword=>{:symbol=>"foo"}}, :value=>{:symbol=>"baz"}}]}]}
|
14
|
+
edn = "[1 2 #_3 {:foo #_bar :baz}]"
|
15
|
+
EDN.read(edn).should == [1, 2, {:foo => :baz}]
|
24
16
|
end
|
25
17
|
|
26
18
|
context "element" do
|
27
|
-
it "should consume nil" do
|
28
|
-
parser.element.should parse("nil")
|
29
|
-
end
|
30
|
-
|
31
19
|
it "should consume metadata with the element" do
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
context "comment" do
|
37
|
-
it "should consume and throw away comments" do
|
38
|
-
comment = "; this is a comment"
|
39
|
-
parser.comment.should parse(comment)
|
40
|
-
parser.comment.parse(comment).should == "; this is a comment"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
context "boolean" do
|
45
|
-
it "should consume true" do
|
46
|
-
parser.boolean.should parse("true")
|
47
|
-
end
|
48
|
-
|
49
|
-
it "should consume false" do
|
50
|
-
parser.boolean.should parse("false")
|
20
|
+
x = EDN.read('^{:doc "test"} [1 2]')
|
21
|
+
x.should == [1, 2]
|
22
|
+
x.metadata.should == {doc: "test"}
|
51
23
|
end
|
52
24
|
end
|
53
25
|
|
54
26
|
context "integer" do
|
55
27
|
it "should consume integers" do
|
56
28
|
rant(RantlyHelpers::INTEGER).each do |int|
|
57
|
-
|
29
|
+
(EDN.read int.to_s).should == int.to_i
|
58
30
|
end
|
59
31
|
end
|
60
32
|
|
61
33
|
it "should consume integers prefixed with a +" do
|
62
34
|
rant(RantlyHelpers::INTEGER).each do |int|
|
63
|
-
|
35
|
+
(EDN.read "+#{int.to_i.abs.to_s}").should == int.to_i.abs
|
64
36
|
end
|
65
37
|
end
|
66
38
|
end
|
@@ -68,41 +40,35 @@ describe EDN::Parser do
|
|
68
40
|
context "float" do
|
69
41
|
it "should consume simple floats" do
|
70
42
|
rant(RantlyHelpers::FLOAT).each do |float|
|
71
|
-
|
43
|
+
EDN.read(float.to_s).should == float.to_f
|
72
44
|
end
|
73
45
|
end
|
74
46
|
|
75
47
|
it "should consume floats with exponents" do
|
76
48
|
rant(RantlyHelpers::FLOAT_WITH_EXP).each do |float|
|
77
|
-
|
49
|
+
EDN.read(float.to_s).should == float.to_f
|
78
50
|
end
|
79
51
|
end
|
80
52
|
|
81
53
|
it "should consume floats prefixed with a +" do
|
82
54
|
rant(RantlyHelpers::FLOAT).each do |float|
|
83
|
-
|
55
|
+
EDN.read("+#{float.to_f.abs.to_s}").should == float.to_f.abs
|
84
56
|
end
|
85
57
|
end
|
86
58
|
end
|
87
59
|
|
88
60
|
context "symbol" do
|
89
|
-
it "should consume any symbols" do
|
90
|
-
rant(RantlyHelpers::SYMBOL).each do |symbol|
|
91
|
-
parser.symbol.should parse("#{symbol}")
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
61
|
context "special cases" do
|
96
62
|
it "should consume '/'" do
|
97
|
-
|
63
|
+
EDN.read('/').should == EDN::Type::Symbol.new(:"/")
|
98
64
|
end
|
99
65
|
|
100
66
|
it "should consume '.'" do
|
101
|
-
|
67
|
+
EDN.read('.').should == EDN::Type::Symbol.new(:".")
|
102
68
|
end
|
103
69
|
|
104
70
|
it "should consume '-'" do
|
105
|
-
|
71
|
+
EDN.read('-').should == EDN::Type::Symbol.new(:"-")
|
106
72
|
end
|
107
73
|
end
|
108
74
|
end
|
@@ -110,84 +76,70 @@ describe EDN::Parser do
|
|
110
76
|
context "keyword" do
|
111
77
|
it "should consume any keywords" do
|
112
78
|
rant(RantlyHelpers::SYMBOL).each do |symbol|
|
113
|
-
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
context "tag" do
|
119
|
-
it "should consume any tags" do
|
120
|
-
rant(RantlyHelpers::TAG).each do |tag|
|
121
|
-
parser.tag.should parse(tag)
|
79
|
+
EDN.read(":#{symbol}").should == symbol.to_sym
|
122
80
|
end
|
123
81
|
end
|
124
82
|
end
|
125
83
|
|
126
84
|
context "string" do
|
127
85
|
it "should consume any string" do
|
128
|
-
rant(RantlyHelpers::
|
129
|
-
|
86
|
+
rant(RantlyHelpers::RUBY_STRING).each do |string|
|
87
|
+
EDN.read(string.to_edn).should == string
|
130
88
|
end
|
131
89
|
end
|
132
90
|
end
|
133
91
|
|
134
92
|
context "character" do
|
135
93
|
it "should consume any character" do
|
136
|
-
rant(RantlyHelpers::
|
137
|
-
|
94
|
+
rant(RantlyHelpers::RUBY_CHAR).each do |char|
|
95
|
+
EDN.read(char.to_edn).should == char
|
138
96
|
end
|
139
97
|
end
|
140
98
|
end
|
141
99
|
|
142
100
|
context "vector" do
|
143
101
|
it "should consume an empty vector" do
|
144
|
-
|
145
|
-
|
102
|
+
EDN.read('[]').should == []
|
103
|
+
EDN.read('[ ]').should == []
|
146
104
|
end
|
147
105
|
|
148
106
|
it "should consume vectors of mixed elements" do
|
149
107
|
rant(RantlyHelpers::VECTOR).each do |vector|
|
150
|
-
|
108
|
+
expect { EDN.read(vector) }.to_not raise_error
|
151
109
|
end
|
152
110
|
end
|
153
111
|
end
|
154
112
|
|
155
113
|
context "list" do
|
156
114
|
it "should consume an empty list" do
|
157
|
-
|
158
|
-
|
115
|
+
EDN.read('()').should == []
|
116
|
+
EDN.read('( )').should == []
|
159
117
|
end
|
160
118
|
|
161
119
|
it "should consume lists of mixed elements" do
|
162
120
|
rant(RantlyHelpers::LIST).each do |list|
|
163
|
-
|
121
|
+
expect { EDN.read(list) }.to_not raise_error
|
164
122
|
end
|
165
123
|
end
|
166
124
|
end
|
167
125
|
|
168
126
|
context "set" do
|
169
127
|
it "should consume an empty set" do
|
170
|
-
|
171
|
-
|
172
|
-
parser.set.should parse('#{ }')
|
128
|
+
EDN.read('#{}').should == Set.new
|
129
|
+
EDN.read('#{ }').should == Set.new
|
173
130
|
end
|
174
131
|
|
175
132
|
it "should consume sets of mixed elements" do
|
176
133
|
rant(RantlyHelpers::SET).each do |set|
|
177
|
-
|
134
|
+
EDN.read(set)
|
178
135
|
end
|
179
136
|
end
|
180
137
|
end
|
181
138
|
|
182
139
|
context "map" do
|
183
|
-
it "should consume an empty map" do
|
184
|
-
parser.map.should parse('{}')
|
185
|
-
parser.map.should parse('{ }')
|
186
|
-
end
|
187
|
-
|
188
140
|
it "should consume maps of mixed elements" do
|
189
141
|
rant(RantlyHelpers::MAP).each do |map|
|
190
|
-
|
142
|
+
expect { EDN.read(map) }.not_to raise_error
|
191
143
|
end
|
192
144
|
end
|
193
145
|
end
|
@@ -196,14 +148,14 @@ describe EDN::Parser do
|
|
196
148
|
context "#inst" do
|
197
149
|
it "should consume #inst" do
|
198
150
|
rant(RantlyHelpers::INST).each do |element|
|
199
|
-
|
151
|
+
expect { EDN.read(element) }.not_to raise_error
|
200
152
|
end
|
201
153
|
end
|
202
154
|
end
|
203
155
|
|
204
156
|
it "should consume tagged elements" do
|
205
157
|
rant(RantlyHelpers::TAGGED_ELEMENT).each do |element|
|
206
|
-
|
158
|
+
expect { EDN.read(element) }.not_to raise_error
|
207
159
|
end
|
208
160
|
end
|
209
161
|
end
|
data/spec/edn/reader_spec.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe EDN::Reader do
|
4
|
-
let(:reader) { EDN::Reader.new("[1 2] 3 :a {:b c}") }
|
4
|
+
let(:reader) { EDN::Reader.new("[1 2] 3 :a {:b c} ") }
|
5
5
|
|
6
|
-
it "should
|
7
|
-
reader.
|
6
|
+
it "should read each value" do
|
7
|
+
reader.read.should == [1, 2]
|
8
|
+
reader.read.should == 3
|
9
|
+
reader.read.should == :a
|
10
|
+
reader.read.should == {:b => ~"c"}
|
8
11
|
end
|
9
12
|
|
10
13
|
it "should respond to each" do
|
@@ -13,11 +16,8 @@ describe EDN::Reader do
|
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
16
|
-
it "
|
17
|
-
reader.
|
18
|
-
|
19
|
-
|
20
|
-
it "should respond to map" do
|
21
|
-
reader.map { |x| x }.should == [[1, 2], 3, :a, {:b => ~"c"}]
|
19
|
+
it "returns a special end of file value if asked" do
|
20
|
+
4.times { reader.read(:the_end).should_not == :the_end }
|
21
|
+
reader.read(:no_more).should == :no_more
|
22
22
|
end
|
23
23
|
end
|
data/spec/edn_spec.rb
CHANGED
@@ -1,9 +1,39 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'stringio'
|
2
3
|
|
3
4
|
describe EDN do
|
4
5
|
include RantlyHelpers
|
5
6
|
|
6
7
|
context "#read" do
|
8
|
+
it "reads from a stream" do
|
9
|
+
io = StringIO.new("123")
|
10
|
+
EDN.read(io).should == 123
|
11
|
+
end
|
12
|
+
|
13
|
+
it "reads mutiple values from a stream" do
|
14
|
+
io = StringIO.new("123 456 789")
|
15
|
+
EDN.read(io).should == 123
|
16
|
+
EDN.read(io).should == 456
|
17
|
+
EDN.read(io).should == 789
|
18
|
+
end
|
19
|
+
|
20
|
+
it "raises an exception on eof by default" do
|
21
|
+
expect { EDN.read('') }.to raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
it "allows you to specify an eof value" do
|
25
|
+
io = StringIO.new("123 456")
|
26
|
+
EDN.read(io, :my_eof).should == 123
|
27
|
+
EDN.read(io, :my_eof).should == 456
|
28
|
+
EDN.read(io, :my_eof).should == :my_eof
|
29
|
+
end
|
30
|
+
|
31
|
+
it "allows you to specify nil as an eof value" do
|
32
|
+
EDN.read("", nil).should == nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "reading data" do
|
7
37
|
it "reads single elements" do
|
8
38
|
EDN.read(%q{""}).should == ""
|
9
39
|
EDN.read("1").should == 1
|
@@ -20,24 +50,27 @@ describe EDN do
|
|
20
50
|
EDN.read('\c').should == "c"
|
21
51
|
end
|
22
52
|
|
23
|
-
it "should support M suffix without decimals"
|
53
|
+
it "should support M suffix without decimals" do
|
24
54
|
EDN.read(123412341231212121241234.to_edn).should == 123412341231212121241234
|
25
55
|
EDN.read("123412341231212121241234M").should == 123412341231212121241234
|
26
56
|
end
|
27
57
|
|
28
|
-
it "reads #inst tagged elements" do
|
29
|
-
EDN.read('#inst "2012-09-10T16:16:03-04:00"').should == DateTime.new(2012, 9, 10, 16, 16, 3, '-04:00')
|
30
|
-
end
|
31
|
-
|
32
58
|
it "reads vectors" do
|
33
59
|
EDN.read('[]').should == []
|
60
|
+
EDN.read('()').should be_a(Array)
|
34
61
|
EDN.read('[1]').should == [1]
|
35
62
|
EDN.read('["hello" 1 2]').should == ['hello', 1, 2]
|
36
63
|
EDN.read('[[1 [:hi]]]').should == [[1, [:hi]]]
|
37
64
|
end
|
38
65
|
|
66
|
+
it "reads tagged elements" do
|
67
|
+
EDN.read('#inst "2012-09-10T16:16:03-04:00"').should == DateTime.rfc3339("2012-09-10T16:16:03-04:00")
|
68
|
+
EDN.read('#uuid "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"').should == "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
|
69
|
+
end
|
70
|
+
|
39
71
|
it "reads lists" do
|
40
72
|
EDN.read('()').should == []
|
73
|
+
EDN.read('()').should be_a(EDN::Type::List)
|
41
74
|
EDN.read('(1)').should == [1]
|
42
75
|
EDN.read('("hello" 1 2)').should == ['hello', 1, 2]
|
43
76
|
EDN.read('((1 (:hi)))').should == [[1, [:hi]]]
|
@@ -45,6 +78,7 @@ describe EDN do
|
|
45
78
|
|
46
79
|
it "reads maps" do
|
47
80
|
EDN.read('{}').should == {}
|
81
|
+
EDN.read('{}').should be_a(Hash)
|
48
82
|
EDN.read('{:a :b}').should == {:a => :b}
|
49
83
|
EDN.read('{:a 1, :b 2}').should == {:a => 1, :b => 2}
|
50
84
|
EDN.read('{:a {:b :c}}').should == {:a => {:b => :c}}
|