edn 0.9.4 → 1.0.0
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/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
|