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 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