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 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
- @transform.apply(@parser.parse(edn))
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.tagged_value(tag, value)
51
+ def self.tagged_element(tag, element)
36
52
  func = @tags[tag]
37
53
  if func
38
- func.call(value)
54
+ func.call(element)
39
55
  else
40
- EDN::Type::Unknown.new(tag, value)
56
+ EDN::Type::Unknown.new(tag, element)
41
57
  end
42
58
  end
43
59
 
44
- def self.tagout(tag, value)
45
- ["##{tag}", value.to_edn].join(" ")
60
+ def self.tagout(tag, element)
61
+ ["##{tag}", element.to_edn].join(" ")
46
62
  end
47
63
 
48
64
  def self.symbol(text)
@@ -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
@@ -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
@@ -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? >> value >> 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(:value) {
13
- tagged_value | base_value
47
+ rule(:tagged_element) {
48
+ tag >> space? >> base_element.as(:element)
14
49
  }
15
50
 
16
- rule(:base_value) {
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? >> (tagged_value | base_value).ignore
192
+ str('#_') >> space? >> (tagged_element | base_element).ignore
144
193
  }
145
194
 
146
195
  rule(:space) {
@@ -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
@@ -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), :value => subtree(:value)) {
40
- EDN.tagged_value(tag.to_s, value)
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
@@ -1,5 +1,7 @@
1
1
  module EDN
2
2
  module Type
3
+ include EDN::CoreExt::AllowsMetadata
4
+
3
5
  class List < ::Array
4
6
  def self.new(*values)
5
7
  self.[](*values)
@@ -1,6 +1,8 @@
1
1
  module EDN
2
2
  module Type
3
3
  class Symbol
4
+ include EDN::CoreExt::AllowsMetadata
5
+
4
6
  attr_reader :symbol
5
7
 
6
8
  def initialize(sym)
@@ -1,3 +1,3 @@
1
1
  module EDN
2
- VERSION = "0.9.4"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -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
@@ -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 "value" do
26
+ context "element" do
27
27
  it "should consume nil" do
28
- parser.value.should parse("nil")
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 value" do
195
+ context "tagged element" do
192
196
  context "#inst" do
193
197
  it "should consume #inst" do
194
- rant(RantlyHelpers::INST).each do |value|
195
- parser.tagged_value.should parse(value)
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 values" do
201
- rant(RantlyHelpers::TAGGED_VALUE).each do |value|
202
- parser.tagged_value.should parse(value)
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
@@ -115,21 +115,21 @@ describe EDN::Transform do
115
115
  end
116
116
  end
117
117
 
118
- context "tagged value" do
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'}, :value => {:string => 'http://google.com'}}).should == EDN::Type::Unknown.new("uri", "http://google.com")
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 value if the tag is registered" do
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'}, :value => {:string => 'http://google.com'}}).should == URI("http://google.com")
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 values" do
129
+ it "should work with nested elements" do
130
130
  tree = {
131
131
  :tag=>{:symbol=>"cnd/awesome"},
132
- :value=>
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
@@ -4,22 +4,22 @@ describe EDN do
4
4
  include RantlyHelpers
5
5
 
6
6
  context "#read" do
7
- it "reads single values" do
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 == ~'hello'
15
- EDN.read('hello/world').should == ~'hello/world'
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 values" do
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 value" do
55
- values = rant(RantlyHelpers::VALUE)
56
- values.each do |value|
54
+ it "reads any valid element" do
55
+ elements = rant(RantlyHelpers::ELEMENT)
56
+ elements.each do |element|
57
57
  begin
58
- if value == "nil"
59
- EDN.read(value).should be_nil
58
+ if element == "nil"
59
+ EDN.read(element).should be_nil
60
60
  else
61
- EDN.read(value).should_not be_nil
61
+ EDN.read(element).should_not be_nil
62
62
  end
63
63
  rescue Exception => ex
64
- puts "Bad value: #{value}"
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 value" do
73
- values = rant(RantlyHelpers::VALUE)
74
- values.each do |value|
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(value).to_edn
77
+ EDN.read(element).to_edn
78
78
  rescue Exception => ex
79
- puts "Bad value: #{value}"
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
- values = rant(RantlyHelpers::VALUE)
88
- values.each do |value|
89
- ruby_value = EDN.read(value)
90
- ruby_value.should == EDN.read(ruby_value.to_edn)
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
@@ -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(0, 10)) { call(VALUE) }
70
+ array(range(1, 10)) { call(ELEMENT) }
72
71
  }
73
72
 
74
73
  VECTOR = lambda { |_|
75
- call(ARRAY).to_edn
74
+ '[' + call(ARRAY).join(', ') + ']'
76
75
  }
77
76
 
78
77
  LIST = lambda { |_|
79
- EDN::Type::List.new(*call(ARRAY)).to_edn
78
+ '(' + call(ARRAY).join(', ') + ')'
80
79
  }
81
80
 
82
81
  SET = lambda { |_|
83
- Set.new(call(ARRAY)).to_edn
82
+ '#{' + call(ARRAY).join(', ') + '}'
84
83
  }
85
84
 
86
85
  MAP = lambda { |_|
87
86
  size = range(0, 10)
88
- keys = array(size) { call(VALUE) }
89
- values = array(size) { call(VALUE) }
90
- arrays = keys.zip(values)
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
- VALUE = lambda { |_|
95
- freq([10, BASIC_VALUE],
93
+ ELEMENT = lambda { |_|
94
+ freq([8, BASIC_ELEMENT],
95
+ [2, ELEMENT_WITH_METADATA],
96
96
  [1, INST],
97
- [1, TAGGED_VALUE])
97
+ [1, TAGGED_ELEMENT])
98
98
  }
99
99
 
100
- BASIC_VALUE = lambda { |_|
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
- TAGGED_VALUE = lambda { |_|
122
- [call(TAG), call(BASIC_VALUE)].join(" ")
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.9.4
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-18 00:00:00.000000000 Z
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: 3816899269916079900
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: 3816899269916079900
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