edn 1.0.3 → 1.0.5
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.
- 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}}
|