edn 0.9.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +36 -0
- data/lib/edn.rb +23 -7
- data/lib/edn/core_ext.rb +10 -0
- data/lib/edn/metadata.rb +27 -0
- data/lib/edn/parser.rb +59 -10
- data/lib/edn/reader.rb +36 -0
- data/lib/edn/transform.rb +17 -2
- data/lib/edn/type/list.rb +2 -0
- data/lib/edn/type/symbol.rb +2 -0
- data/lib/edn/version.rb +1 -1
- data/spec/edn/metadata_spec.rb +38 -0
- data/spec/edn/parser_spec.rb +12 -8
- data/spec/edn/reader_spec.rb +23 -0
- data/spec/edn/transform_spec.rb +22 -6
- data/spec/edn_spec.rb +23 -20
- data/spec/spec_helper.rb +27 -15
- metadata +10 -4
data/README.md
CHANGED
@@ -34,6 +34,27 @@ data.to_edn
|
|
34
34
|
|
35
35
|
By default, this will work for strings, symbols, numbers, arrays, hashes, sets, nil, Time, and boolean values.
|
36
36
|
|
37
|
+
### Multiple element reading
|
38
|
+
|
39
|
+
If you have a string of **edn** that contains multiple forms, you can create an `EDN::Reader`, which extends `Enumerable`.
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
r = EDN::Reader.new('[1 2 3] {:a 1 :b 2}')
|
43
|
+
|
44
|
+
r.read #=> [1, 2, 3]
|
45
|
+
r.read #=> {:a => 1, :b => 2}
|
46
|
+
r.read #=> RuntimeError: EDN::Reader is out of string!
|
47
|
+
|
48
|
+
r.each do |form|
|
49
|
+
p form
|
50
|
+
end
|
51
|
+
|
52
|
+
#=> [1, 2, 3]
|
53
|
+
#=> {:a => 1, :b => 2}
|
54
|
+
|
55
|
+
r.count #=> 2
|
56
|
+
```
|
57
|
+
|
37
58
|
### Value Translations
|
38
59
|
|
39
60
|
**edn** has some different terminology, and some types that do not map cleanly to Ruby.
|
@@ -114,6 +135,21 @@ end
|
|
114
135
|
|
115
136
|
This method calls `.to_edn` on the second argument and joins the arguments appropriately.
|
116
137
|
|
138
|
+
## Metadata
|
139
|
+
|
140
|
+
Certain elements of **edn** can have *metadata*. Metadata is a map of values about the element, which must follow specific rules.
|
141
|
+
|
142
|
+
* Only symbols, lists, vectors, maps, and sets can have metadata. Tagged elements *cannot* have metadata.
|
143
|
+
* Metadata keys must be symbols, keywords, or strings.
|
144
|
+
|
145
|
+
Metadata can be expressed in one of the following three ways:
|
146
|
+
|
147
|
+
* Via a map. The element is prefixed with a map which has a caret (`^`) prefixed to it, like so: `^{:doc "This is my vector" :rel :temps} [98.6 99.7]`.
|
148
|
+
* Via a keyword. The element is prefixed with a keyword, also prefixed by a caret: `^:awesome #{1 2 \c}`. This results in the key `:awesome` being set to `true`, as if the metadata was: `^{:awesome true} #{1 2 \c}`.
|
149
|
+
* Via a symbol. The element is prefixed with a symbol, also prefixed by a caret: `^Boolean "true"`. This results in the key `:tag` being set to the symbol, as if the metadata was: `^{:tag Boolean} "true"`. This is used in Clojure to indicate the Java type of the element. In other **edn** implementations, it may be ignored or used differently.
|
150
|
+
|
151
|
+
More than one piece of metadata can be applied to an element. Metadata is applied to the next element appearing after it, so in the case of `^:foo ^{:bar false} [1 2]`, the metadata would be, in total, `^{:foo true, :bar false}`. Note that `^:foo` is applied to the element `[1 2]` with the metadata `^{:bar false}` applied to it. Because of this, key collisions are resolved *right-to-left*.
|
152
|
+
|
117
153
|
## Contributors
|
118
154
|
|
119
155
|
* Clinton N. Dreisbach (@crnixon)
|
data/lib/edn.rb
CHANGED
@@ -1,17 +1,33 @@
|
|
1
1
|
$:.push(File.dirname(__FILE__))
|
2
2
|
require 'edn/version'
|
3
|
-
require 'edn/types'
|
4
3
|
require 'edn/core_ext'
|
4
|
+
require 'edn/types'
|
5
5
|
require 'edn/parser'
|
6
6
|
require 'edn/transform'
|
7
|
+
require 'edn/reader'
|
7
8
|
|
8
9
|
module EDN
|
10
|
+
class ParseFailed < StandardError
|
11
|
+
attr_reader :original_exception
|
12
|
+
|
13
|
+
def initialize(message, original_exception)
|
14
|
+
super(message)
|
15
|
+
@original_exception = original_exception
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
9
19
|
@parser = EDN::Parser.new
|
10
20
|
@transform = EDN::Transform.new
|
11
21
|
@tags = Hash.new
|
12
22
|
|
13
23
|
def self.read(edn)
|
14
|
-
|
24
|
+
begin
|
25
|
+
tree = @parser.parse(edn)
|
26
|
+
rescue Parslet::ParseFailed => error
|
27
|
+
message = "Invalid EDN, cannot parse: #{edn}"
|
28
|
+
raise ParseFailed.new(message, error)
|
29
|
+
end
|
30
|
+
@transform.apply(tree)
|
15
31
|
end
|
16
32
|
|
17
33
|
def self.register(tag, func = nil, &block)
|
@@ -32,17 +48,17 @@ module EDN
|
|
32
48
|
@tags[tag] = nil
|
33
49
|
end
|
34
50
|
|
35
|
-
def self.
|
51
|
+
def self.tagged_element(tag, element)
|
36
52
|
func = @tags[tag]
|
37
53
|
if func
|
38
|
-
func.call(
|
54
|
+
func.call(element)
|
39
55
|
else
|
40
|
-
EDN::Type::Unknown.new(tag,
|
56
|
+
EDN::Type::Unknown.new(tag, element)
|
41
57
|
end
|
42
58
|
end
|
43
59
|
|
44
|
-
def self.tagout(tag,
|
45
|
-
["##{tag}",
|
60
|
+
def self.tagout(tag, element)
|
61
|
+
["##{tag}", element.to_edn].join(" ")
|
46
62
|
end
|
47
63
|
|
48
64
|
def self.symbol(text)
|
data/lib/edn/core_ext.rb
CHANGED
@@ -10,6 +10,12 @@ module EDN
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
module AllowsMetadata
|
14
|
+
def allows_metadata?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
13
19
|
module Bignum
|
14
20
|
def to_edn
|
15
21
|
self.to_s + 'M'
|
@@ -93,3 +99,7 @@ Hash.send(:include, EDN::CoreExt::Hash)
|
|
93
99
|
Set.send(:include, EDN::CoreExt::Set)
|
94
100
|
DateTime.send(:include, EDN::CoreExt::DateTime)
|
95
101
|
Time.send(:include, EDN::CoreExt::Time)
|
102
|
+
|
103
|
+
[Array, Hash, Set].each do |klass|
|
104
|
+
klass.send(:include, EDN::CoreExt::AllowsMetadata)
|
105
|
+
end
|
data/lib/edn/metadata.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module EDN
|
2
|
+
module Metadata
|
3
|
+
def self.extended(base)
|
4
|
+
base.instance_eval do
|
5
|
+
alias :to_edn_without_metadata :to_edn
|
6
|
+
alias :to_edn :to_edn_with_metadata
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :metadata
|
11
|
+
|
12
|
+
def has_metadata?
|
13
|
+
respond_to?(:allows_metadata?) and
|
14
|
+
allows_metadata? and
|
15
|
+
!metadata.nil? and
|
16
|
+
!metadata.empty?
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_edn_with_metadata
|
20
|
+
if has_metadata?
|
21
|
+
'^' + metadata.to_edn + ' ' + to_edn_without_metadata
|
22
|
+
else
|
23
|
+
to_edn_without_metadata
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/edn/parser.rb
CHANGED
@@ -3,17 +3,52 @@ require 'parslet/ignore'
|
|
3
3
|
|
4
4
|
module EDN
|
5
5
|
class Parser < Parslet::Parser
|
6
|
+
|
7
|
+
def parse_prefix(str, options={})
|
8
|
+
source = Parslet::Source.new(str.to_s)
|
9
|
+
success, value = setup_and_apply(source, nil)
|
10
|
+
|
11
|
+
unless success
|
12
|
+
reporter = options[:reporter] || Parslet::ErrorReporter::Tree.new
|
13
|
+
success, value = setup_and_apply(source, reporter)
|
14
|
+
|
15
|
+
fail "Assertion failed: success was true when parsing with reporter" if success
|
16
|
+
value.raise
|
17
|
+
end
|
18
|
+
|
19
|
+
rest = nil
|
20
|
+
if !source.eof?
|
21
|
+
rest = source.consume(source.chars_left).to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
return [flatten(value), rest]
|
25
|
+
end
|
26
|
+
|
6
27
|
root(:top)
|
7
28
|
|
8
29
|
rule(:top) {
|
9
|
-
space? >>
|
30
|
+
space? >> element >> space?
|
31
|
+
}
|
32
|
+
|
33
|
+
rule(:element) {
|
34
|
+
base_element |
|
35
|
+
tagged_element |
|
36
|
+
metadata.maybe >> metadata_capable_element.as(:element)
|
37
|
+
}
|
38
|
+
|
39
|
+
rule(:metadata_capable_element) {
|
40
|
+
vector |
|
41
|
+
list |
|
42
|
+
set |
|
43
|
+
map |
|
44
|
+
symbol
|
10
45
|
}
|
11
46
|
|
12
|
-
rule(:
|
13
|
-
|
47
|
+
rule(:tagged_element) {
|
48
|
+
tag >> space? >> base_element.as(:element)
|
14
49
|
}
|
15
50
|
|
16
|
-
rule(:
|
51
|
+
rule(:base_element) {
|
17
52
|
vector |
|
18
53
|
list |
|
19
54
|
set |
|
@@ -28,15 +63,10 @@ module EDN
|
|
28
63
|
symbol
|
29
64
|
}
|
30
65
|
|
31
|
-
rule(:tagged_value) {
|
32
|
-
tag >> space? >> base_value.as(:value)
|
33
|
-
}
|
34
|
-
|
35
66
|
# Collections
|
36
67
|
|
37
68
|
rule(:vector) {
|
38
69
|
str('[') >>
|
39
|
-
space? >>
|
40
70
|
top.repeat.as(:vector) >>
|
41
71
|
space? >>
|
42
72
|
str(']')
|
@@ -106,6 +136,25 @@ module EDN
|
|
106
136
|
|
107
137
|
# Parts
|
108
138
|
|
139
|
+
rule(:metadata) {
|
140
|
+
((metadata_map | metadata_symbol | metadata_keyword) >> space?).repeat.as(:metadata)
|
141
|
+
}
|
142
|
+
|
143
|
+
rule(:metadata_map) {
|
144
|
+
str('^{') >>
|
145
|
+
((keyword | symbol | string).as(:key) >> top.as(:value)).repeat.as(:map) >>
|
146
|
+
space? >>
|
147
|
+
str('}')
|
148
|
+
}
|
149
|
+
|
150
|
+
rule(:metadata_symbol) {
|
151
|
+
str('^') >> symbol
|
152
|
+
}
|
153
|
+
|
154
|
+
rule(:metadata_keyword) {
|
155
|
+
str('^') >> keyword
|
156
|
+
}
|
157
|
+
|
109
158
|
rule(:tag) {
|
110
159
|
str('#') >> match['[:alpha:]'].present? >> symbol.as(:tag)
|
111
160
|
}
|
@@ -140,7 +189,7 @@ module EDN
|
|
140
189
|
}
|
141
190
|
|
142
191
|
rule(:discard) {
|
143
|
-
str('#_') >> space? >> (
|
192
|
+
str('#_') >> space? >> (tagged_element | base_element).ignore
|
144
193
|
}
|
145
194
|
|
146
195
|
rule(:space) {
|
data/lib/edn/reader.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module EDN
|
2
|
+
class Reader
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(text)
|
6
|
+
@parser = Parser.new
|
7
|
+
@transform = Transform.new
|
8
|
+
@original_text = text
|
9
|
+
@text = text
|
10
|
+
end
|
11
|
+
|
12
|
+
def eof?
|
13
|
+
@text.nil? || @text.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def each
|
17
|
+
reset!
|
18
|
+
return enum_for(:select) unless block_given?
|
19
|
+
|
20
|
+
until eof?
|
21
|
+
yield read
|
22
|
+
end
|
23
|
+
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
|
+
end
|
36
|
+
end
|
data/lib/edn/transform.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'edn/string_transformer'
|
2
2
|
require 'edn/types'
|
3
|
+
require 'edn/metadata'
|
3
4
|
require 'bigdecimal'
|
4
5
|
|
5
6
|
module EDN
|
@@ -25,6 +26,7 @@ module EDN
|
|
25
26
|
rule(:character => simple(:x)) {
|
26
27
|
case x
|
27
28
|
when "newline" then "\n"
|
29
|
+
when "return" then "\r"
|
28
30
|
when "tab" then "\t"
|
29
31
|
when "space" then " "
|
30
32
|
else x.to_s
|
@@ -36,8 +38,21 @@ module EDN
|
|
36
38
|
rule(:set => subtree(:array)) { Set.new(array) }
|
37
39
|
rule(:map => subtree(:array)) { Hash[array.map { |hash| [hash[:key], hash[:value]] }] }
|
38
40
|
|
39
|
-
rule(:tag => simple(:tag), :
|
40
|
-
EDN.
|
41
|
+
rule(:tag => simple(:tag), :element => subtree(:element)) {
|
42
|
+
EDN.tagged_element(tag.to_s, element)
|
43
|
+
}
|
44
|
+
|
45
|
+
rule(:metadata => subtree(:raw_metadata), :element => subtree(:element)) {
|
46
|
+
metadata = raw_metadata.reverse.reduce({}) do |acc, m|
|
47
|
+
case m
|
48
|
+
when Symbol then acc.merge(m => true)
|
49
|
+
when EDN::Type::Symbol then acc.merge(:tag => m)
|
50
|
+
else acc.merge(m)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
element.extend EDN::Metadata
|
54
|
+
element.metadata = metadata
|
55
|
+
element
|
41
56
|
}
|
42
57
|
end
|
43
58
|
end
|
data/lib/edn/type/list.rb
CHANGED
data/lib/edn/type/symbol.rb
CHANGED
data/lib/edn/version.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EDN do
|
4
|
+
describe "metadata" do
|
5
|
+
it "reads metadata, which does not change the element's equality" do
|
6
|
+
EDN.read('[1 2 3]').should == EDN.read('^{:doc "My vec"} [1 2 3]')
|
7
|
+
end
|
8
|
+
|
9
|
+
it "reads metadata recursively from right to left" do
|
10
|
+
element = EDN.read('^String ^:foo ^{:foo false :tag Boolean :bar 2} [1 2]')
|
11
|
+
element.should == [1, 2]
|
12
|
+
element.metadata.should == {:tag => ~"String", :foo => true, :bar => 2}
|
13
|
+
end
|
14
|
+
|
15
|
+
it "writes metadata" do
|
16
|
+
element = EDN.read('^{:doc "My vec"} [1 2 3]')
|
17
|
+
element.to_edn.should == '^{:doc "My vec"} [1 2 3]'
|
18
|
+
end
|
19
|
+
|
20
|
+
it "only writes metadata for elements that can have it" do
|
21
|
+
apply_metadata = lambda { |o|
|
22
|
+
o.extend(EDN::Metadata)
|
23
|
+
o.metadata = {:foo => 1}
|
24
|
+
o
|
25
|
+
}
|
26
|
+
|
27
|
+
apply_metadata[[1, 2]].to_edn.should == '^{:foo 1} [1 2]'
|
28
|
+
apply_metadata[~[1, 2]].to_edn.should == '^{:foo 1} (1 2)'
|
29
|
+
apply_metadata[{1 => 2}].to_edn.should == '^{:foo 1} {1 2}'
|
30
|
+
apply_metadata[Set.new([1, 2])].to_edn.should == '^{:foo 1} #{1 2}'
|
31
|
+
apply_metadata[~"bar"].to_edn.should == '^{:foo 1} bar'
|
32
|
+
|
33
|
+
apply_metadata["bar"].to_edn.should == '"bar"'
|
34
|
+
|
35
|
+
# Cannot extend symbols, booleans, and nil, so no test for them.
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/spec/edn/parser_spec.rb
CHANGED
@@ -23,9 +23,13 @@ describe EDN::Parser do
|
|
23
23
|
{:map => [{:key=>{:keyword=>{:symbol=>"foo"}}, :value=>{:symbol=>"baz"}}]}]}
|
24
24
|
end
|
25
25
|
|
26
|
-
context "
|
26
|
+
context "element" do
|
27
27
|
it "should consume nil" do
|
28
|
-
parser.
|
28
|
+
parser.element.should parse("nil")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should consume metadata with the element" do
|
32
|
+
parser.element.should parse('^{:doc "test"} [1 2]')
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
@@ -188,18 +192,18 @@ describe EDN::Parser do
|
|
188
192
|
end
|
189
193
|
end
|
190
194
|
|
191
|
-
context "tagged
|
195
|
+
context "tagged element" do
|
192
196
|
context "#inst" do
|
193
197
|
it "should consume #inst" do
|
194
|
-
rant(RantlyHelpers::INST).each do |
|
195
|
-
parser.
|
198
|
+
rant(RantlyHelpers::INST).each do |element|
|
199
|
+
parser.tagged_element.should parse(element)
|
196
200
|
end
|
197
201
|
end
|
198
202
|
end
|
199
203
|
|
200
|
-
it "should consume tagged
|
201
|
-
rant(RantlyHelpers::
|
202
|
-
parser.
|
204
|
+
it "should consume tagged elements" do
|
205
|
+
rant(RantlyHelpers::TAGGED_ELEMENT).each do |element|
|
206
|
+
parser.tagged_element.should parse(element)
|
203
207
|
end
|
204
208
|
end
|
205
209
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EDN::Reader do
|
4
|
+
let(:reader) { EDN::Reader.new("[1 2] 3 :a {:b c}") }
|
5
|
+
|
6
|
+
it "should respond to count" do
|
7
|
+
reader.count.should == 4
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should respond to each" do
|
11
|
+
reader.each do |element|
|
12
|
+
element.should_not be_nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should return an Enumerator from each if no block given" do
|
17
|
+
reader.each.should be_a(Enumerator)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should respond to map" do
|
21
|
+
reader.map { |x| x }.should == [[1, 2], 3, :a, {:b => ~"c"}]
|
22
|
+
end
|
23
|
+
end
|
data/spec/edn/transform_spec.rb
CHANGED
@@ -115,21 +115,21 @@ describe EDN::Transform do
|
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
-
context "tagged
|
118
|
+
context "tagged element" do
|
119
119
|
it "should emit a EDN::Type::Unknown if the tag is not registered" do
|
120
|
-
subject.apply({:tag => {:symbol => 'uri'}, :
|
120
|
+
subject.apply({:tag => {:symbol => 'uri'}, :element => {:string => 'http://google.com'}}).should == EDN::Type::Unknown.new("uri", "http://google.com")
|
121
121
|
end
|
122
122
|
|
123
|
-
it "should emit the transformed
|
123
|
+
it "should emit the transformed element if the tag is registered" do
|
124
124
|
EDN.register("uri", lambda { |uri| URI(uri) })
|
125
|
-
subject.apply({:tag => {:symbol => 'uri'}, :
|
125
|
+
subject.apply({:tag => {:symbol => 'uri'}, :element => {:string => 'http://google.com'}}).should == URI("http://google.com")
|
126
126
|
EDN.unregister("uri") # cleanup
|
127
127
|
end
|
128
128
|
|
129
|
-
it "should work with nested
|
129
|
+
it "should work with nested elements" do
|
130
130
|
tree = {
|
131
131
|
:tag=>{:symbol=>"cnd/awesome"},
|
132
|
-
:
|
132
|
+
:element=>
|
133
133
|
{:vector=>
|
134
134
|
[{:keyword=>{:symbol=>"a"}},
|
135
135
|
{:list=>
|
@@ -153,4 +153,20 @@ describe EDN::Transform do
|
|
153
153
|
subject.apply(tree).should == expected
|
154
154
|
end
|
155
155
|
end
|
156
|
+
|
157
|
+
context "element with metadata" do
|
158
|
+
it "should tag the element with metadata" do
|
159
|
+
tree = {
|
160
|
+
:metadata =>
|
161
|
+
[:map =>
|
162
|
+
[
|
163
|
+
{:key=>{:keyword=>{:symbol=>"a"}}, :value=>{:integer=>"1", :precision => nil}},
|
164
|
+
{:key=>{:keyword=>{:symbol=>"b"}}, :value=>{:integer=>"2", :precision => nil}}]],
|
165
|
+
:element => {:vector => [{:integer => "1", :precision => nil}, {:string => "abc"}]}}
|
166
|
+
|
167
|
+
element = subject.apply(tree)
|
168
|
+
element.should == [1, "abc"]
|
169
|
+
element.metadata.should == {:a => 1, :b => 2}
|
170
|
+
end
|
171
|
+
end
|
156
172
|
end
|
data/spec/edn_spec.rb
CHANGED
@@ -4,22 +4,22 @@ describe EDN do
|
|
4
4
|
include RantlyHelpers
|
5
5
|
|
6
6
|
context "#read" do
|
7
|
-
it "reads single
|
7
|
+
it "reads single elements" do
|
8
8
|
EDN.read("1").should == 1
|
9
9
|
EDN.read("3.14").should == 3.14
|
10
10
|
EDN.read("3.14M").should == BigDecimal("3.14")
|
11
11
|
EDN.read('"hello\nworld"').should == "hello\nworld"
|
12
12
|
EDN.read(':hello').should == :hello
|
13
13
|
EDN.read(':hello/world').should == :"hello/world"
|
14
|
-
EDN.read('hello').should ==
|
15
|
-
EDN.read('hello/world').should ==
|
14
|
+
EDN.read('hello').should == EDN::Type::Symbol.new('hello')
|
15
|
+
EDN.read('hello/world').should == EDN::Type::Symbol.new('hello/world')
|
16
16
|
EDN.read('true').should == true
|
17
17
|
EDN.read('false').should == false
|
18
18
|
EDN.read('nil').should == nil
|
19
19
|
EDN.read('\c').should == "c"
|
20
20
|
end
|
21
21
|
|
22
|
-
it "reads #inst tagged
|
22
|
+
it "reads #inst tagged elements" do
|
23
23
|
EDN.read('#inst "2012-09-10T16:16:03-04:00"').should == DateTime.new(2012, 9, 10, 16, 16, 3, '-04:00')
|
24
24
|
end
|
25
25
|
|
@@ -51,17 +51,17 @@ describe EDN do
|
|
51
51
|
EDN.read('#{1 #{:abc}}').should == Set[1, Set[:abc]]
|
52
52
|
end
|
53
53
|
|
54
|
-
it "reads any valid
|
55
|
-
|
56
|
-
|
54
|
+
it "reads any valid element" do
|
55
|
+
elements = rant(RantlyHelpers::ELEMENT)
|
56
|
+
elements.each do |element|
|
57
57
|
begin
|
58
|
-
if
|
59
|
-
EDN.read(
|
58
|
+
if element == "nil"
|
59
|
+
EDN.read(element).should be_nil
|
60
60
|
else
|
61
|
-
EDN.read(
|
61
|
+
EDN.read(element).should_not be_nil
|
62
62
|
end
|
63
63
|
rescue Exception => ex
|
64
|
-
puts "Bad
|
64
|
+
puts "Bad element: #{element}"
|
65
65
|
raise ex
|
66
66
|
end
|
67
67
|
end
|
@@ -69,14 +69,14 @@ describe EDN do
|
|
69
69
|
end
|
70
70
|
|
71
71
|
context "writing" do
|
72
|
-
it "writes any valid
|
73
|
-
|
74
|
-
|
72
|
+
it "writes any valid element" do
|
73
|
+
elements = rant(RantlyHelpers::ELEMENT)
|
74
|
+
elements.each do |element|
|
75
75
|
expect {
|
76
76
|
begin
|
77
|
-
EDN.read(
|
77
|
+
EDN.read(element).to_edn
|
78
78
|
rescue Exception => ex
|
79
|
-
puts "Bad
|
79
|
+
puts "Bad element: #{element}"
|
80
80
|
raise ex
|
81
81
|
end
|
82
82
|
}.to_not raise_error
|
@@ -84,10 +84,13 @@ describe EDN do
|
|
84
84
|
end
|
85
85
|
|
86
86
|
it "writes equivalent edn to what it reads" do
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
87
|
+
elements = rant(RantlyHelpers::ELEMENT)
|
88
|
+
elements.each do |element|
|
89
|
+
ruby_element = EDN.read(element)
|
90
|
+
ruby_element.should == EDN.read(ruby_element.to_edn)
|
91
|
+
if ruby_element.respond_to?(:metadata)
|
92
|
+
ruby_element.metadata.should == EDN.read(ruby_element.to_edn).metadata
|
93
|
+
end
|
91
94
|
end
|
92
95
|
end
|
93
96
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -27,7 +27,6 @@ module RantlyHelpers
|
|
27
27
|
PLAIN_SYMBOL = lambda { |_|
|
28
28
|
sized(range(1, 100)) {
|
29
29
|
s = string(/[[:alnum:]]|[\.\*\+\!\-\?\$_%&=:#]/)
|
30
|
-
# guard s =~ /^[A-Za-z\.\*\+\!\-\?\$_%&=:#]/
|
31
30
|
guard s !~ /^[0-9]/
|
32
31
|
guard s !~ /^[\+\-\.][0-9]/
|
33
32
|
guard s !~ /^[\:\#]/
|
@@ -58,7 +57,7 @@ module RantlyHelpers
|
|
58
57
|
CHARACTER = lambda { |_|
|
59
58
|
"\\" +
|
60
59
|
sized(1) {
|
61
|
-
freq([1, [:choose, "newline", "space", "tab"]],
|
60
|
+
freq([1, [:choose, "newline", "return", "space", "tab"]],
|
62
61
|
[5, [:string, :graph]])
|
63
62
|
}
|
64
63
|
}
|
@@ -68,36 +67,37 @@ module RantlyHelpers
|
|
68
67
|
}
|
69
68
|
|
70
69
|
ARRAY = lambda { |_|
|
71
|
-
array(range(
|
70
|
+
array(range(1, 10)) { call(ELEMENT) }
|
72
71
|
}
|
73
72
|
|
74
73
|
VECTOR = lambda { |_|
|
75
|
-
call(ARRAY).
|
74
|
+
'[' + call(ARRAY).join(', ') + ']'
|
76
75
|
}
|
77
76
|
|
78
77
|
LIST = lambda { |_|
|
79
|
-
|
78
|
+
'(' + call(ARRAY).join(', ') + ')'
|
80
79
|
}
|
81
80
|
|
82
81
|
SET = lambda { |_|
|
83
|
-
|
82
|
+
'#{' + call(ARRAY).join(', ') + '}'
|
84
83
|
}
|
85
84
|
|
86
85
|
MAP = lambda { |_|
|
87
86
|
size = range(0, 10)
|
88
|
-
keys = array(size) { call(
|
89
|
-
|
90
|
-
arrays = keys.zip(
|
87
|
+
keys = array(size) { call(ELEMENT) }
|
88
|
+
elements = array(size) { call(ELEMENT) }
|
89
|
+
arrays = keys.zip(elements)
|
91
90
|
'{' + arrays.map { |array| array.join(" ") }.join(", ") + '}'
|
92
91
|
}
|
93
92
|
|
94
|
-
|
95
|
-
freq([
|
93
|
+
ELEMENT = lambda { |_|
|
94
|
+
freq([8, BASIC_ELEMENT],
|
95
|
+
[2, ELEMENT_WITH_METADATA],
|
96
96
|
[1, INST],
|
97
|
-
[1,
|
97
|
+
[1, TAGGED_ELEMENT])
|
98
98
|
}
|
99
99
|
|
100
|
-
|
100
|
+
BASIC_ELEMENT = lambda { |_|
|
101
101
|
branch(INTEGER,
|
102
102
|
FLOAT,
|
103
103
|
FLOAT_WITH_EXP,
|
@@ -112,14 +112,26 @@ module RantlyHelpers
|
|
112
112
|
MAP)
|
113
113
|
}
|
114
114
|
|
115
|
+
METADATA = lambda { |_|
|
116
|
+
size = range(1, 4)
|
117
|
+
keys = array(size) { branch(KEYWORD, SYMBOL, STRING) }
|
118
|
+
elements = array(size) { call(ELEMENT) }
|
119
|
+
arrays = keys.zip(elements)
|
120
|
+
'^{' + arrays.map { |array| array.join(" ") }.join(", ") + '}'
|
121
|
+
}
|
122
|
+
|
123
|
+
ELEMENT_WITH_METADATA = lambda { |_|
|
124
|
+
[call(METADATA), branch(SYMBOL, VECTOR, LIST, SET, MAP)].join(" ")
|
125
|
+
}
|
126
|
+
|
115
127
|
TAG = lambda { |_|
|
116
128
|
tag = call(SYMBOL)
|
117
129
|
guard tag =~ /^[A-Za-z]/
|
118
130
|
"##{tag}"
|
119
131
|
}
|
120
132
|
|
121
|
-
|
122
|
-
[call(TAG), call(
|
133
|
+
TAGGED_ELEMENT = lambda { |_|
|
134
|
+
[call(TAG), call(BASIC_ELEMENT)].join(" ")
|
123
135
|
}
|
124
136
|
|
125
137
|
INST = lambda { |_|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: edn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
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-09-
|
12
|
+
date: 2012-09-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: parslet
|
@@ -91,7 +91,9 @@ files:
|
|
91
91
|
- edn.gemspec
|
92
92
|
- lib/edn.rb
|
93
93
|
- lib/edn/core_ext.rb
|
94
|
+
- lib/edn/metadata.rb
|
94
95
|
- lib/edn/parser.rb
|
96
|
+
- lib/edn/reader.rb
|
95
97
|
- lib/edn/string_transformer.rb
|
96
98
|
- lib/edn/transform.rb
|
97
99
|
- lib/edn/type/list.rb
|
@@ -101,7 +103,9 @@ files:
|
|
101
103
|
- lib/edn/types.rb
|
102
104
|
- lib/edn/version.rb
|
103
105
|
- lib/parslet/ignore.rb
|
106
|
+
- spec/edn/metadata_spec.rb
|
104
107
|
- spec/edn/parser_spec.rb
|
108
|
+
- spec/edn/reader_spec.rb
|
105
109
|
- spec/edn/transform_spec.rb
|
106
110
|
- spec/edn_spec.rb
|
107
111
|
- spec/spec_helper.rb
|
@@ -119,7 +123,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
119
123
|
version: '0'
|
120
124
|
segments:
|
121
125
|
- 0
|
122
|
-
hash:
|
126
|
+
hash: -2141916682632630907
|
123
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
128
|
none: false
|
125
129
|
requirements:
|
@@ -128,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
132
|
version: '0'
|
129
133
|
segments:
|
130
134
|
- 0
|
131
|
-
hash:
|
135
|
+
hash: -2141916682632630907
|
132
136
|
requirements: []
|
133
137
|
rubyforge_project:
|
134
138
|
rubygems_version: 1.8.23
|
@@ -136,7 +140,9 @@ signing_key:
|
|
136
140
|
specification_version: 3
|
137
141
|
summary: ! '''edn implements a reader for Extensible Data Notation by Rich Hickey.'''
|
138
142
|
test_files:
|
143
|
+
- spec/edn/metadata_spec.rb
|
139
144
|
- spec/edn/parser_spec.rb
|
145
|
+
- spec/edn/reader_spec.rb
|
140
146
|
- spec/edn/transform_spec.rb
|
141
147
|
- spec/edn_spec.rb
|
142
148
|
- spec/spec_helper.rb
|