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