edn2023 1.1.4

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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.travis.yml +7 -0
  4. data/CHANGELOG.md +29 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE +22 -0
  7. data/README.md +268 -0
  8. data/Rakefile +12 -0
  9. data/edn2023.gemspec +22 -0
  10. data/lib/edn/char_stream.rb +84 -0
  11. data/lib/edn/core_ext.rb +112 -0
  12. data/lib/edn/metadata.rb +27 -0
  13. data/lib/edn/parser.rb +92 -0
  14. data/lib/edn/reader.rb +23 -0
  15. data/lib/edn/ruby_edn_parser.rb +266 -0
  16. data/lib/edn/type/list.rb +13 -0
  17. data/lib/edn/type/symbol.rb +39 -0
  18. data/lib/edn/type/unknown.rb +9 -0
  19. data/lib/edn/type/uuid.rb +9 -0
  20. data/lib/edn/types.rb +3 -0
  21. data/lib/edn/version.rb +3 -0
  22. data/lib/edn.rb +80 -0
  23. data/spec/edn/char_stream_spec.rb +111 -0
  24. data/spec/edn/metadata_spec.rb +38 -0
  25. data/spec/edn/parser_spec.rb +162 -0
  26. data/spec/edn/reader_spec.rb +23 -0
  27. data/spec/edn_spec.rb +114 -0
  28. data/spec/exemplars/big_decimal.edn +1 -0
  29. data/spec/exemplars/big_decimal.rb +1 -0
  30. data/spec/exemplars/big_int.edn +1 -0
  31. data/spec/exemplars/big_int.rb +1 -0
  32. data/spec/exemplars/char.edn +1 -0
  33. data/spec/exemplars/char.rb +1 -0
  34. data/spec/exemplars/empty_string.edn +1 -0
  35. data/spec/exemplars/empty_string.rb +1 -0
  36. data/spec/exemplars/false.edn +1 -0
  37. data/spec/exemplars/false.rb +1 -0
  38. data/spec/exemplars/float.edn +1 -0
  39. data/spec/exemplars/float.rb +1 -0
  40. data/spec/exemplars/float_neg_exp.edn +1 -0
  41. data/spec/exemplars/float_neg_exp.rb +1 -0
  42. data/spec/exemplars/float_point_dec_neg_exp.edn +1 -0
  43. data/spec/exemplars/float_point_dec_neg_exp.rb +1 -0
  44. data/spec/exemplars/float_point_plus_exp.edn +1 -0
  45. data/spec/exemplars/float_point_plus_exp.rb +1 -0
  46. data/spec/exemplars/float_pointneg_exp.edn +1 -0
  47. data/spec/exemplars/float_pointneg_exp.rb +1 -0
  48. data/spec/exemplars/float_pos_exp.edn +1 -0
  49. data/spec/exemplars/float_pos_exp.rb +1 -0
  50. data/spec/exemplars/float_sign_exp.edn +1 -0
  51. data/spec/exemplars/float_sign_exp.rb +1 -0
  52. data/spec/exemplars/float_with_exp.edn +1 -0
  53. data/spec/exemplars/float_with_exp.rb +1 -0
  54. data/spec/exemplars/float_with_exp_no_dec.edn +1 -0
  55. data/spec/exemplars/float_with_exp_no_dec.rb +1 -0
  56. data/spec/exemplars/gt_symbol.edn +1 -0
  57. data/spec/exemplars/gt_symbol.rb +1 -0
  58. data/spec/exemplars/integer.edn +1 -0
  59. data/spec/exemplars/integer.rb +1 -0
  60. data/spec/exemplars/keyword.edn +1 -0
  61. data/spec/exemplars/keyword.rb +1 -0
  62. data/spec/exemplars/keyword_with_namespace.edn +1 -0
  63. data/spec/exemplars/keyword_with_namespace.rb +1 -0
  64. data/spec/exemplars/list_empty.edn +1 -0
  65. data/spec/exemplars/list_empty.rb +1 -0
  66. data/spec/exemplars/list_mixed.edn +1 -0
  67. data/spec/exemplars/list_mixed.rb +1 -0
  68. data/spec/exemplars/list_nested.edn +1 -0
  69. data/spec/exemplars/list_nested.rb +1 -0
  70. data/spec/exemplars/list_one_element.edn +1 -0
  71. data/spec/exemplars/list_one_element.rb +1 -0
  72. data/spec/exemplars/long_string.edn +1 -0
  73. data/spec/exemplars/long_string.rb +1 -0
  74. data/spec/exemplars/map_empty.edn +1 -0
  75. data/spec/exemplars/map_empty.rb +1 -0
  76. data/spec/exemplars/map_nested.edn +1 -0
  77. data/spec/exemplars/map_nested.rb +1 -0
  78. data/spec/exemplars/map_one_element.edn +1 -0
  79. data/spec/exemplars/map_one_element.rb +1 -0
  80. data/spec/exemplars/map_two_entry.edn +1 -0
  81. data/spec/exemplars/map_two_entry.rb +1 -0
  82. data/spec/exemplars/mmv +4 -0
  83. data/spec/exemplars/nil.edn +1 -0
  84. data/spec/exemplars/nil.rb +1 -0
  85. data/spec/exemplars/set_empty.edn +1 -0
  86. data/spec/exemplars/set_empty.rb +1 -0
  87. data/spec/exemplars/set_nested.edn +1 -0
  88. data/spec/exemplars/set_nested.rb +1 -0
  89. data/spec/exemplars/set_one_element.edn +1 -0
  90. data/spec/exemplars/set_one_element.rb +1 -0
  91. data/spec/exemplars/set_two_elements.edn +1 -0
  92. data/spec/exemplars/set_two_elements.rb +1 -0
  93. data/spec/exemplars/simple_skip.edn +1 -0
  94. data/spec/exemplars/simple_skip.rb +1 -0
  95. data/spec/exemplars/skip_more_space.edn +1 -0
  96. data/spec/exemplars/skip_more_space.rb +1 -0
  97. data/spec/exemplars/skip_more_vector_elements.edn +1 -0
  98. data/spec/exemplars/skip_more_vector_elements.rb +1 -0
  99. data/spec/exemplars/skip_some_vector_elements.edn +1 -0
  100. data/spec/exemplars/skip_some_vector_elements.rb +1 -0
  101. data/spec/exemplars/skip_two_vector_elements.edn +1 -0
  102. data/spec/exemplars/skip_two_vector_elements.rb +1 -0
  103. data/spec/exemplars/skip_vector_element.edn +1 -0
  104. data/spec/exemplars/skip_vector_element.rb +1 -0
  105. data/spec/exemplars/skip_with_spaces.edn +1 -0
  106. data/spec/exemplars/skip_with_spaces.rb +1 -0
  107. data/spec/exemplars/skip_yet_more_space.edn +1 -0
  108. data/spec/exemplars/skip_yet_more_space.rb +1 -0
  109. data/spec/exemplars/special_symbol.edn +1 -0
  110. data/spec/exemplars/special_symbol.rb +1 -0
  111. data/spec/exemplars/string_with_newline.edn +1 -0
  112. data/spec/exemplars/string_with_newline.rb +1 -0
  113. data/spec/exemplars/symbol.edn +1 -0
  114. data/spec/exemplars/symbol.rb +1 -0
  115. data/spec/exemplars/symbol_begins_with_false.edn +1 -0
  116. data/spec/exemplars/symbol_begins_with_false.rb +1 -0
  117. data/spec/exemplars/symbol_begins_with_nil.edn +1 -0
  118. data/spec/exemplars/symbol_begins_with_nil.rb +1 -0
  119. data/spec/exemplars/symbol_with_dash.edn +1 -0
  120. data/spec/exemplars/symbol_with_dash.rb +1 -0
  121. data/spec/exemplars/symbol_with_namespace.edn +1 -0
  122. data/spec/exemplars/symbol_with_namespace.rb +1 -0
  123. data/spec/exemplars/tagged_instant.edn +1 -0
  124. data/spec/exemplars/tagged_instant.rb +1 -0
  125. data/spec/exemplars/tagged_uuid.edn +1 -0
  126. data/spec/exemplars/tagged_uuid.rb +1 -0
  127. data/spec/exemplars/true.edn +1 -0
  128. data/spec/exemplars/true.rb +1 -0
  129. data/spec/exemplars/utf8.edn +1 -0
  130. data/spec/exemplars/utf8.rb +1 -0
  131. data/spec/exemplars/vector_empty.edn +1 -0
  132. data/spec/exemplars/vector_empty.rb +1 -0
  133. data/spec/exemplars/vector_mixed.edn +1 -0
  134. data/spec/exemplars/vector_mixed.rb +1 -0
  135. data/spec/exemplars/vector_nested.edn +1 -0
  136. data/spec/exemplars/vector_nested.rb +1 -0
  137. data/spec/exemplars/vector_one_element.edn +1 -0
  138. data/spec/exemplars/vector_one_element.rb +1 -0
  139. data/spec/exemplars/whole_number_with_exp.edn +1 -0
  140. data/spec/exemplars/whole_number_with_exp.rb +1 -0
  141. data/spec/spec_helper.rb +164 -0
  142. metadata +357 -0
data/lib/edn/parser.rb ADDED
@@ -0,0 +1,92 @@
1
+ require 'stringio'
2
+ require 'set'
3
+
4
+
5
+ module EDN
6
+ Parser = RubyEdnParser
7
+ @parser_class = RubyEdnParser
8
+
9
+ def self.parser=(p)
10
+ @parser_class = p
11
+ end
12
+
13
+ def self.new_parser(*args)
14
+ @parser_class.new(*args)
15
+ end
16
+
17
+ # Object returned when there is nothing to return
18
+
19
+ NOTHING = Object.new
20
+
21
+ # Object to return when we hit end of file. Cant be nil or :eof
22
+ # because either of those could be something in the EDN data.
23
+
24
+ EOF = Object.new
25
+
26
+ # Reader table
27
+
28
+ READERS = {}
29
+ SYMBOL_INTERIOR_CHARS =
30
+ Set.new(%w{. # * ! - _ + ? $ % & = < > :} + ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a)
31
+
32
+ SYMBOL_INTERIOR_CHARS.each {|n| READERS[n.to_s] = :read_symbol}
33
+
34
+ DIGITS = Set.new(('0'..'9').to_a)
35
+
36
+ DIGITS.each {|n| READERS[n.to_s] = :read_number}
37
+
38
+ READERS.default = :unknown
39
+
40
+ READERS['{'] = :read_map
41
+ READERS['['] = :read_vector
42
+ READERS['('] = :read_list
43
+ READERS['\\'] = :read_char
44
+ READERS['"'] = :read_string
45
+ READERS['.'] = :read_number_or_symbol
46
+ READERS['+'] = :read_number_or_symbol
47
+ READERS['-'] = :read_number_or_symbol
48
+ READERS[''] = :read_number_or_symbol
49
+ READERS['/'] = :read_slash
50
+ READERS[':'] = :read_keyword
51
+ READERS['#'] = :read_extension
52
+ READERS[:eof] = :read_eof
53
+
54
+ def self.register_reader(ch, handler=nil, &block)
55
+ if handler
56
+ READERS[ch] = handler
57
+ else
58
+ READERS[ch] = block
59
+ end
60
+ end
61
+
62
+ TAGS = {}
63
+
64
+ def self.register(tag, func = nil, &block)
65
+ if block_given?
66
+ func = block
67
+ end
68
+
69
+ if func.nil?
70
+ func = lambda { |x| x }
71
+ end
72
+
73
+ if func.is_a?(Class)
74
+ TAGS[tag] = lambda { |*args| func.new(*args) }
75
+ else
76
+ TAGS[tag] = func
77
+ end
78
+ end
79
+
80
+ def self.unregister(tag)
81
+ TAGS[tag] = nil
82
+ end
83
+
84
+ def self.tagged_element(tag, element)
85
+ func = TAGS[tag]
86
+ if func
87
+ func.call(element)
88
+ else
89
+ EDN::Type::Unknown.new(tag, element)
90
+ end
91
+ end
92
+ end
data/lib/edn/reader.rb ADDED
@@ -0,0 +1,23 @@
1
+ module EDN
2
+ class Reader
3
+
4
+ def initialize(source)
5
+ @parser = EDN.new_parser(source)
6
+ end
7
+
8
+ def read(eof_value = NOTHING)
9
+ result = @parser.read
10
+ if result == EOF
11
+ raise "Unexpected end of file" if eof_value == NOTHING
12
+ return eof_value
13
+ end
14
+ result
15
+ end
16
+
17
+ def each
18
+ until (result = @parser.read) == EOF
19
+ yield result
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,266 @@
1
+ require 'stringio'
2
+ require 'set'
3
+
4
+
5
+ module EDN
6
+ class RubyEdnParser
7
+ def initialize(source, *extra)
8
+ io = source.instance_of?(String) ? StringIO.new(source) : source
9
+ @s = CharStream.new(io)
10
+ end
11
+
12
+ def read(return_nothing=false)
13
+ meta = read_meta
14
+ value = read_basic(return_nothing)
15
+ if meta && value != NOTHING
16
+ value.extend EDN::Metadata
17
+ value.metadata = meta
18
+ end
19
+ value
20
+ end
21
+
22
+ def eof?
23
+ @s.eof?
24
+ end
25
+
26
+ def unknown
27
+ raise "Don't know what to do with #{@s.current} #{@s.current.class}"
28
+ end
29
+
30
+ def read_eof
31
+ EOF
32
+ end
33
+
34
+ def read_char
35
+ result = @s.advance
36
+ @s.advance
37
+ until @s.eof?
38
+ break unless @s.digit? || @s.alpha?
39
+ result += @s.current
40
+ @s.advance
41
+ end
42
+
43
+ return result if result.size == 1
44
+
45
+ case result
46
+ when 'newline'
47
+ "\n"
48
+ when 'return'
49
+ "\r"
50
+ when 'tab'
51
+ "\t"
52
+ when 'space'
53
+ " "
54
+ else
55
+ raise "Unknown char #{result}"
56
+ end
57
+ end
58
+
59
+ def read_slash
60
+ @s.advance
61
+ Type::Symbol.new('/')
62
+ end
63
+
64
+ def read_number_or_symbol
65
+ leading = @s.current
66
+ @s.advance
67
+ return read_number(leading) if @s.digit?
68
+ read_symbol(leading)
69
+ end
70
+
71
+ def read_symbol_chars
72
+ result = ''
73
+
74
+ ch = @s.current
75
+ while SYMBOL_INTERIOR_CHARS.include?(ch)
76
+ result << ch
77
+ ch = @s.advance
78
+ end
79
+ return result unless @s.skip_past('/')
80
+
81
+ result << '/'
82
+ ch = @s.current
83
+ while SYMBOL_INTERIOR_CHARS.include?(ch)
84
+ result << ch
85
+ ch = @s.advance
86
+ end
87
+
88
+ result
89
+ end
90
+
91
+ def read_extension
92
+ @s.advance
93
+ if @s.current == '{'
94
+ @s.advance
95
+ read_collection(Set, '}')
96
+ elsif @s.current == "_"
97
+ @s.advance
98
+ x = read
99
+ NOTHING
100
+ else
101
+ tag = read_symbol_chars
102
+ value = read
103
+ EDN.tagged_element(tag, value)
104
+ end
105
+ end
106
+
107
+ def read_symbol(leading='')
108
+ token = leading + read_symbol_chars
109
+ return true if token == "true"
110
+ return false if token == "false"
111
+ return nil if token == "nil"
112
+ Type::Symbol.new(token)
113
+ end
114
+
115
+ def read_keyword
116
+ @s.advance
117
+ read_symbol_chars.to_sym
118
+ end
119
+
120
+ def escape_char(ch)
121
+ return '\\' if ch == '\\'
122
+ return "\n" if ch == 'n'
123
+ return "\t" if ch == 't'
124
+ return "\r" if ch == 'r'
125
+ ch
126
+ end
127
+
128
+ def read_string
129
+ @s.advance
130
+
131
+ result = ''
132
+ until @s.current == '"'
133
+ raise "Unexpected eof" if @s.eof?
134
+ if @s.current == '\\'
135
+ @s.advance
136
+ result << escape_char(@s.current)
137
+ else
138
+ result << @s.current
139
+ end
140
+ @s.advance
141
+ end
142
+ @s.advance
143
+ result
144
+ end
145
+
146
+ def call_reader(reader)
147
+ if reader.instance_of? Symbol
148
+ self.send(reader)
149
+ else
150
+ self.instance_exec(&reader)
151
+ end
152
+ end
153
+
154
+ def read_basic(return_nothing=false)
155
+ @s.skip_ws
156
+ ch = @s.current
157
+ result = call_reader(READERS[ch])
158
+ while NOTHING.equal?(result) && !return_nothing
159
+ @s.skip_ws
160
+ result = call_reader(READERS[@s.current])
161
+ end
162
+
163
+ result
164
+ end
165
+
166
+ def read_digits(min_digits=0)
167
+ result = ''
168
+
169
+ if @s.current == '+' || @s.current == '-'
170
+ result << @s.current
171
+ @s.advance
172
+ end
173
+
174
+ n_digits = 0
175
+ while @s.current =~ /[0-9]/
176
+ n_digits += 1
177
+ result << @s.current
178
+ @s.advance
179
+ end
180
+
181
+ raise "Expected at least #{min_digits} digits, found #{result}" unless n_digits >= min_digits
182
+ result
183
+ end
184
+
185
+ def finish_float(whole_part)
186
+ result = whole_part
187
+
188
+ if @s.current == '.'
189
+ result += '.'
190
+ @s.advance
191
+ result = @s.digit? ? result + read_digits : result + '0'
192
+ #puts "aaa: #{result}"
193
+ end
194
+
195
+ if @s.current == 'e' || @s.current == 'E'
196
+ @s.advance
197
+ result = result + 'e' + read_digits
198
+ #puts "bbb: #{result}"
199
+ end
200
+ #puts result
201
+ result.to_f
202
+ end
203
+
204
+ def read_number(leading='')
205
+ result = leading + read_digits
206
+
207
+ if %w{. e E}.include? @s.current
208
+ return finish_float(result)
209
+ elsif @s.skip_past('M') || @s.skip_past('N')
210
+ result.to_i
211
+ else
212
+ result.to_i
213
+ end
214
+ end
215
+
216
+ def read_meta
217
+ raw_metadata = []
218
+ @s.skip_ws
219
+ while @s.current == '^'
220
+ @s.advance
221
+ raw_metadata << read_basic
222
+ @s.skip_ws
223
+ end
224
+
225
+ metadata = raw_metadata.reverse.reduce({}) do |acc, m|
226
+ case m
227
+ when Symbol then acc.merge(m => true)
228
+ when EDN::Type::Symbol then acc.merge(:tag => m)
229
+ else acc.merge(m)
230
+ end
231
+ end
232
+ metadata.empty? ? nil : metadata
233
+ end
234
+
235
+ def read_list
236
+ @s.advance
237
+ read_collection(EDN::Type::List, ')')
238
+ end
239
+
240
+ def read_vector
241
+ @s.advance
242
+ read_collection(Array, ']')
243
+ end
244
+
245
+ def read_map
246
+ @s.advance
247
+ array = read_collection(Array, '}')
248
+ raise "Need an even number of items for a map" unless array.count.even?
249
+ Hash[*array]
250
+ end
251
+
252
+ def read_collection(clazz, closing)
253
+ result = clazz.new
254
+
255
+ while true
256
+ @s.skip_ws
257
+ raise "Unexpected eof" if @s.eof?
258
+ break if @s.current == closing
259
+ next_value = read(true)
260
+ result << next_value unless next_value == NOTHING
261
+ end
262
+ @s.advance
263
+ result
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,13 @@
1
+ module EDN
2
+ module Type
3
+ class List < ::Array
4
+ def self.new(*values)
5
+ self.[](*values)
6
+ end
7
+
8
+ def to_edn
9
+ '(' + self.map(&:to_edn).join(" ") + ')'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ module EDN
2
+ module Type
3
+ class Symbol
4
+ include EDN::CoreExt::AllowsMetadata
5
+
6
+ attr_reader :symbol
7
+
8
+ def initialize(sym)
9
+ @symbol = sym.to_sym
10
+ end
11
+
12
+ def ==(other)
13
+ return false unless other.is_a?(Symbol)
14
+ to_sym == other.to_sym
15
+ end
16
+
17
+ def eql?(other)
18
+ return false unless other.is_a?(Symbol)
19
+ to_sym == other.to_sym
20
+ end
21
+
22
+ def hash
23
+ @symbol.hash
24
+ end
25
+
26
+ def to_sym
27
+ @symbol
28
+ end
29
+
30
+ def to_s
31
+ @symbol.to_s
32
+ end
33
+
34
+ def to_edn
35
+ @symbol.to_s
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,9 @@
1
+ module EDN
2
+ module Type
3
+ class Unknown < Struct.new(:tag, :value)
4
+ def to_edn
5
+ EDN.tagout(tag, value)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module EDN
2
+ module Type
3
+ class UUID < String
4
+ def to_edn
5
+ "#uuid #{self.inspect}"
6
+ end
7
+ end
8
+ end
9
+ end
data/lib/edn/types.rb ADDED
@@ -0,0 +1,3 @@
1
+ Dir[File.join(File.dirname(__FILE__), 'type', '*.rb')].each do |file|
2
+ require file
3
+ end
@@ -0,0 +1,3 @@
1
+ module EDN
2
+ VERSION = "1.1.4"
3
+ end
data/lib/edn.rb ADDED
@@ -0,0 +1,80 @@
1
+ $:.push(File.dirname(__FILE__))
2
+ require 'set'
3
+ require 'edn/version'
4
+ require 'edn/core_ext'
5
+ require 'edn/types'
6
+ require 'edn/metadata'
7
+ require 'edn/char_stream'
8
+ require 'edn/ruby_edn_parser'
9
+ require 'edn/parser'
10
+ require 'edn/reader'
11
+
12
+ module EDN
13
+ @tags = Hash.new
14
+
15
+ def self.read(edn, eof_value=NOTHING)
16
+ EDN::Reader.new(edn).read(eof_value)
17
+ end
18
+
19
+ def self.register(tag, func = nil, &block)
20
+ if block_given?
21
+ func = block
22
+ end
23
+
24
+ if func.nil?
25
+ func = lambda { |x| x }
26
+ end
27
+
28
+ if func.is_a?(Class)
29
+ @tags[tag] = lambda { |*args| func.new(*args) }
30
+ else
31
+ @tags[tag] = func
32
+ end
33
+ end
34
+
35
+ def self.unregister(tag)
36
+ @tags[tag] = nil
37
+ end
38
+
39
+ def self.tagged_element(tag, element)
40
+ func = @tags[tag]
41
+ if func
42
+ func.call(element)
43
+ else
44
+ EDN::Type::Unknown.new(tag, element)
45
+ end
46
+ end
47
+
48
+ def self.tagout(tag, element)
49
+ ["##{tag}", element.to_edn].join(" ")
50
+ end
51
+
52
+ def self.symbol(text)
53
+ EDN::Type::Symbol.new(text)
54
+ end
55
+
56
+ def self.list(values)
57
+ EDN::Type::List.new(*values)
58
+ end
59
+
60
+ def self.set(*elems)
61
+ Set.new(*elems)
62
+ end
63
+
64
+ def self.rational(value)
65
+ Rational(value)
66
+ end
67
+
68
+ def self.big_decimal(str)
69
+ BigDecimal(str)
70
+ end
71
+ end
72
+
73
+ EDN.register("inst") do |value|
74
+ DateTime.parse(value)
75
+ end
76
+
77
+ EDN.register("uuid") do |value|
78
+ EDN::Type::UUID.new(value)
79
+ end
80
+
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe EDN::CharStream do
4
+ it "reads a stream in order" do
5
+ s = EDN::CharStream.new(io_for("abc"))
6
+ s.current.should == "a"
7
+ s.advance.should == "b"
8
+ s.advance.should == "c"
9
+ s.advance.should == :eof
10
+ s.current.should == :eof
11
+ end
12
+
13
+ it "keeps returning the current until your advance" do
14
+ s = EDN::CharStream.new(io_for("abc"))
15
+ s.current.should == "a"
16
+ s.current.should == "a"
17
+ s.advance.should == "b"
18
+ end
19
+
20
+ it "knows if the current char is a digit" do
21
+ s = EDN::CharStream.new(io_for("4f"))
22
+ s.digit?.should be_truthy
23
+ s.advance
24
+ s.digit?.should be_falsey
25
+ end
26
+
27
+ it "knows if the current char is an alpha" do
28
+ s = EDN::CharStream.new(io_for("a9"))
29
+ s.alpha?.should be_truthy
30
+ s.advance
31
+ s.alpha?.should be_falsey
32
+ end
33
+
34
+ it "knows if the current char is whitespace" do
35
+ s = EDN::CharStream.new(io_for("a b\nc\td,"))
36
+ s.ws?.should be_falsey # a
37
+
38
+ s.advance
39
+ s.ws?.should be_truthy # " "
40
+
41
+ s.advance
42
+ s.ws?.should be_falsey # b
43
+
44
+ s.advance
45
+ s.ws?.should be_truthy # \n
46
+
47
+ s.advance
48
+ s.ws?.should be_falsey # c
49
+
50
+ s.advance
51
+ s.ws?.should be_truthy # \t
52
+
53
+ s.advance
54
+ s.ws?.should be_falsey # d
55
+
56
+ s.advance
57
+ s.ws?.should be_truthy # ,
58
+ end
59
+
60
+ it "knows if the current char is a newline" do
61
+ s = EDN::CharStream.new(io_for("a\nb\rc"))
62
+ s.newline?.should be_falsey # a
63
+
64
+ s.advance
65
+ s.newline?.should be_truthy # \n
66
+
67
+ s.advance
68
+ s.newline?.should be_falsey # b
69
+
70
+ s.advance
71
+ s.newline?.should be_truthy # \r
72
+
73
+ s.advance
74
+ s.newline?.should be_falsey # c
75
+ end
76
+
77
+ it "knows if it is at the eof" do
78
+ s = EDN::CharStream.new(io_for("abc"))
79
+ s.eof?.should be_falsey # a
80
+ s.advance
81
+ s.eof?.should be_falsey # b
82
+ s.advance
83
+ s.eof?.should be_falsey # c
84
+ s.advance
85
+ s.eof?.should be_truthy
86
+ end
87
+
88
+ it "knows how to skip past a char" do
89
+ s = EDN::CharStream.new(io_for("abc"))
90
+ s.skip_past("a").should == "a"
91
+ s.current.should == "b"
92
+ end
93
+
94
+ it "knows how not to skip a char" do
95
+ s = EDN::CharStream.new(io_for("abc"))
96
+ s.skip_past("X").should be_nil
97
+ end
98
+
99
+ it "knows how skip to the end of a line" do
100
+ s = EDN::CharStream.new(io_for("abc\ndef"))
101
+ s.skip_to_eol
102
+ s.current.should == "\n"
103
+ s.advance.should == "d"
104
+ end
105
+
106
+ it "knows how skip whitespace" do
107
+ s = EDN::CharStream.new(io_for(" \n \t,,,,abc"))
108
+ s.skip_ws
109
+ s.current.should == "a"
110
+ end
111
+ end
@@ -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