edn2023 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +29 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +268 -0
- data/Rakefile +12 -0
- data/edn2023.gemspec +22 -0
- data/lib/edn/char_stream.rb +84 -0
- data/lib/edn/core_ext.rb +112 -0
- data/lib/edn/metadata.rb +27 -0
- data/lib/edn/parser.rb +92 -0
- data/lib/edn/reader.rb +23 -0
- data/lib/edn/ruby_edn_parser.rb +266 -0
- data/lib/edn/type/list.rb +13 -0
- data/lib/edn/type/symbol.rb +39 -0
- data/lib/edn/type/unknown.rb +9 -0
- data/lib/edn/type/uuid.rb +9 -0
- data/lib/edn/types.rb +3 -0
- data/lib/edn/version.rb +3 -0
- data/lib/edn.rb +80 -0
- data/spec/edn/char_stream_spec.rb +111 -0
- data/spec/edn/metadata_spec.rb +38 -0
- data/spec/edn/parser_spec.rb +162 -0
- data/spec/edn/reader_spec.rb +23 -0
- data/spec/edn_spec.rb +114 -0
- data/spec/exemplars/big_decimal.edn +1 -0
- data/spec/exemplars/big_decimal.rb +1 -0
- data/spec/exemplars/big_int.edn +1 -0
- data/spec/exemplars/big_int.rb +1 -0
- data/spec/exemplars/char.edn +1 -0
- data/spec/exemplars/char.rb +1 -0
- data/spec/exemplars/empty_string.edn +1 -0
- data/spec/exemplars/empty_string.rb +1 -0
- data/spec/exemplars/false.edn +1 -0
- data/spec/exemplars/false.rb +1 -0
- data/spec/exemplars/float.edn +1 -0
- data/spec/exemplars/float.rb +1 -0
- data/spec/exemplars/float_neg_exp.edn +1 -0
- data/spec/exemplars/float_neg_exp.rb +1 -0
- data/spec/exemplars/float_point_dec_neg_exp.edn +1 -0
- data/spec/exemplars/float_point_dec_neg_exp.rb +1 -0
- data/spec/exemplars/float_point_plus_exp.edn +1 -0
- data/spec/exemplars/float_point_plus_exp.rb +1 -0
- data/spec/exemplars/float_pointneg_exp.edn +1 -0
- data/spec/exemplars/float_pointneg_exp.rb +1 -0
- data/spec/exemplars/float_pos_exp.edn +1 -0
- data/spec/exemplars/float_pos_exp.rb +1 -0
- data/spec/exemplars/float_sign_exp.edn +1 -0
- data/spec/exemplars/float_sign_exp.rb +1 -0
- data/spec/exemplars/float_with_exp.edn +1 -0
- data/spec/exemplars/float_with_exp.rb +1 -0
- data/spec/exemplars/float_with_exp_no_dec.edn +1 -0
- data/spec/exemplars/float_with_exp_no_dec.rb +1 -0
- data/spec/exemplars/gt_symbol.edn +1 -0
- data/spec/exemplars/gt_symbol.rb +1 -0
- data/spec/exemplars/integer.edn +1 -0
- data/spec/exemplars/integer.rb +1 -0
- data/spec/exemplars/keyword.edn +1 -0
- data/spec/exemplars/keyword.rb +1 -0
- data/spec/exemplars/keyword_with_namespace.edn +1 -0
- data/spec/exemplars/keyword_with_namespace.rb +1 -0
- data/spec/exemplars/list_empty.edn +1 -0
- data/spec/exemplars/list_empty.rb +1 -0
- data/spec/exemplars/list_mixed.edn +1 -0
- data/spec/exemplars/list_mixed.rb +1 -0
- data/spec/exemplars/list_nested.edn +1 -0
- data/spec/exemplars/list_nested.rb +1 -0
- data/spec/exemplars/list_one_element.edn +1 -0
- data/spec/exemplars/list_one_element.rb +1 -0
- data/spec/exemplars/long_string.edn +1 -0
- data/spec/exemplars/long_string.rb +1 -0
- data/spec/exemplars/map_empty.edn +1 -0
- data/spec/exemplars/map_empty.rb +1 -0
- data/spec/exemplars/map_nested.edn +1 -0
- data/spec/exemplars/map_nested.rb +1 -0
- data/spec/exemplars/map_one_element.edn +1 -0
- data/spec/exemplars/map_one_element.rb +1 -0
- data/spec/exemplars/map_two_entry.edn +1 -0
- data/spec/exemplars/map_two_entry.rb +1 -0
- data/spec/exemplars/mmv +4 -0
- data/spec/exemplars/nil.edn +1 -0
- data/spec/exemplars/nil.rb +1 -0
- data/spec/exemplars/set_empty.edn +1 -0
- data/spec/exemplars/set_empty.rb +1 -0
- data/spec/exemplars/set_nested.edn +1 -0
- data/spec/exemplars/set_nested.rb +1 -0
- data/spec/exemplars/set_one_element.edn +1 -0
- data/spec/exemplars/set_one_element.rb +1 -0
- data/spec/exemplars/set_two_elements.edn +1 -0
- data/spec/exemplars/set_two_elements.rb +1 -0
- data/spec/exemplars/simple_skip.edn +1 -0
- data/spec/exemplars/simple_skip.rb +1 -0
- data/spec/exemplars/skip_more_space.edn +1 -0
- data/spec/exemplars/skip_more_space.rb +1 -0
- data/spec/exemplars/skip_more_vector_elements.edn +1 -0
- data/spec/exemplars/skip_more_vector_elements.rb +1 -0
- data/spec/exemplars/skip_some_vector_elements.edn +1 -0
- data/spec/exemplars/skip_some_vector_elements.rb +1 -0
- data/spec/exemplars/skip_two_vector_elements.edn +1 -0
- data/spec/exemplars/skip_two_vector_elements.rb +1 -0
- data/spec/exemplars/skip_vector_element.edn +1 -0
- data/spec/exemplars/skip_vector_element.rb +1 -0
- data/spec/exemplars/skip_with_spaces.edn +1 -0
- data/spec/exemplars/skip_with_spaces.rb +1 -0
- data/spec/exemplars/skip_yet_more_space.edn +1 -0
- data/spec/exemplars/skip_yet_more_space.rb +1 -0
- data/spec/exemplars/special_symbol.edn +1 -0
- data/spec/exemplars/special_symbol.rb +1 -0
- data/spec/exemplars/string_with_newline.edn +1 -0
- data/spec/exemplars/string_with_newline.rb +1 -0
- data/spec/exemplars/symbol.edn +1 -0
- data/spec/exemplars/symbol.rb +1 -0
- data/spec/exemplars/symbol_begins_with_false.edn +1 -0
- data/spec/exemplars/symbol_begins_with_false.rb +1 -0
- data/spec/exemplars/symbol_begins_with_nil.edn +1 -0
- data/spec/exemplars/symbol_begins_with_nil.rb +1 -0
- data/spec/exemplars/symbol_with_dash.edn +1 -0
- data/spec/exemplars/symbol_with_dash.rb +1 -0
- data/spec/exemplars/symbol_with_namespace.edn +1 -0
- data/spec/exemplars/symbol_with_namespace.rb +1 -0
- data/spec/exemplars/tagged_instant.edn +1 -0
- data/spec/exemplars/tagged_instant.rb +1 -0
- data/spec/exemplars/tagged_uuid.edn +1 -0
- data/spec/exemplars/tagged_uuid.rb +1 -0
- data/spec/exemplars/true.edn +1 -0
- data/spec/exemplars/true.rb +1 -0
- data/spec/exemplars/utf8.edn +1 -0
- data/spec/exemplars/utf8.rb +1 -0
- data/spec/exemplars/vector_empty.edn +1 -0
- data/spec/exemplars/vector_empty.rb +1 -0
- data/spec/exemplars/vector_mixed.edn +1 -0
- data/spec/exemplars/vector_mixed.rb +1 -0
- data/spec/exemplars/vector_nested.edn +1 -0
- data/spec/exemplars/vector_nested.rb +1 -0
- data/spec/exemplars/vector_one_element.edn +1 -0
- data/spec/exemplars/vector_one_element.rb +1 -0
- data/spec/exemplars/whole_number_with_exp.edn +1 -0
- data/spec/exemplars/whole_number_with_exp.rb +1 -0
- data/spec/spec_helper.rb +164 -0
- 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,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
|
data/lib/edn/types.rb
ADDED
data/lib/edn/version.rb
ADDED
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
|