jiffy 1.0.1

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 (83) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +18 -0
  4. data/LICENSE +20 -0
  5. data/README.md +144 -0
  6. data/bin/jiffy +46 -0
  7. data/jiffy.gemspec +104 -0
  8. data/lib/jiffy/json_outputter.rb +62 -0
  9. data/lib/jiffy/parsers/json.rb +293 -0
  10. data/lib/jiffy/parsers/json.rl +56 -0
  11. data/lib/jiffy/parsers/json_array.rb +306 -0
  12. data/lib/jiffy/parsers/json_array.rl +45 -0
  13. data/lib/jiffy/parsers/json_common.rl +24 -0
  14. data/lib/jiffy/parsers/json_float.rb +269 -0
  15. data/lib/jiffy/parsers/json_float.rl +37 -0
  16. data/lib/jiffy/parsers/json_object.rb +348 -0
  17. data/lib/jiffy/parsers/json_object.rl +58 -0
  18. data/lib/jiffy/parsers/json_string.rb +267 -0
  19. data/lib/jiffy/parsers/json_string.rl +41 -0
  20. data/lib/jiffy/parsers/json_value.rb +346 -0
  21. data/lib/jiffy/parsers/json_value.rl +78 -0
  22. data/lib/jiffy/version.rb +3 -0
  23. data/lib/jiffy.rb +50 -0
  24. data/test/jiffy_test.rb +47 -0
  25. data/test/negative-examples/hexadecimal.json +3 -0
  26. data/test/negative-examples/infinity-value.json +3 -0
  27. data/test/negative-examples/leading-comma.json +3 -0
  28. data/test/negative-examples/leading-zero.json +3 -0
  29. data/test/negative-examples/line-break.json +4 -0
  30. data/test/negative-examples/missing-colon.json +3 -0
  31. data/test/negative-examples/nan-value,json +3 -0
  32. data/test/negative-examples/positive-float.json +3 -0
  33. data/test/negative-examples/positive-integer.json +3 -0
  34. data/test/negative-examples/single-quote.json +3 -0
  35. data/test/negative-examples/string-as-root.json +1 -0
  36. data/test/negative-examples/tab-character.json +3 -0
  37. data/test/negative-examples/trailing-array-seperator.json +3 -0
  38. data/test/negative-examples/trailing-object-seperator.json +3 -0
  39. data/test/negative-examples/true-as-root.json +1 -0
  40. data/test/negative-examples/unclosed-array.json +2 -0
  41. data/test/negative-examples/unclosed-object.json +2 -0
  42. data/test/positive-examples/array-as-root.json +3 -0
  43. data/test/positive-examples/array-nested-inside-array.json +5 -0
  44. data/test/positive-examples/array-nested-inside-object.json +5 -0
  45. data/test/positive-examples/false-value.json +3 -0
  46. data/test/positive-examples/null-value.json +3 -0
  47. data/test/positive-examples/number-1.json +3 -0
  48. data/test/positive-examples/number-10.json +3 -0
  49. data/test/positive-examples/number-11.json +3 -0
  50. data/test/positive-examples/number-12.json +3 -0
  51. data/test/positive-examples/number-13.json +3 -0
  52. data/test/positive-examples/number-14.json +3 -0
  53. data/test/positive-examples/number-15.json +3 -0
  54. data/test/positive-examples/number-16.json +3 -0
  55. data/test/positive-examples/number-17.json +3 -0
  56. data/test/positive-examples/number-18.json +3 -0
  57. data/test/positive-examples/number-19.json +3 -0
  58. data/test/positive-examples/number-2.json +3 -0
  59. data/test/positive-examples/number-20.json +3 -0
  60. data/test/positive-examples/number-3.json +3 -0
  61. data/test/positive-examples/number-4.json +3 -0
  62. data/test/positive-examples/number-5.json +3 -0
  63. data/test/positive-examples/number-6.json +3 -0
  64. data/test/positive-examples/number-7.json +3 -0
  65. data/test/positive-examples/number-8.json +3 -0
  66. data/test/positive-examples/number-9.json +3 -0
  67. data/test/positive-examples/object-as-root.json +3 -0
  68. data/test/positive-examples/object-nested-inside-array.json +5 -0
  69. data/test/positive-examples/object-nested-inside-object.json +5 -0
  70. data/test/positive-examples/seperated-array-values.json +4 -0
  71. data/test/positive-examples/seperated-object-properties.json +4 -0
  72. data/test/positive-examples/string-backspace.json +3 -0
  73. data/test/positive-examples/string-carriage-return.json +3 -0
  74. data/test/positive-examples/string-formfeed.json +3 -0
  75. data/test/positive-examples/string-horizontal-tab.json +3 -0
  76. data/test/positive-examples/string-newline.json +3 -0
  77. data/test/positive-examples/string-quotation.json +3 -0
  78. data/test/positive-examples/string-reverse-solidus.json +3 -0
  79. data/test/positive-examples/string-solidus.json +3 -0
  80. data/test/positive-examples/string-trivial.json +3 -0
  81. data/test/positive-examples/string-unicode.json +3 -0
  82. data/test/positive-examples/true-value.json +3 -0
  83. metadata +155 -0
@@ -0,0 +1,41 @@
1
+ %%{
2
+ machine json_string;
3
+ include json_common "json_common.rl";
4
+
5
+ action exit { fhold; fbreak; }
6
+
7
+ action char { o.t :char, [data[p]].pack("c*") }
8
+
9
+ character = ^([\"\\] | 0..0x1f) >char;
10
+ sequence = '\\' >char [\"\\/bfnrt] >char;
11
+ unicode = '\\' >char 'u' >char [0-9a-fA-F]{4} $char;
12
+
13
+ main := '"' (
14
+ character | sequence | unicode
15
+ )*
16
+ '"' @exit;
17
+ }%%
18
+
19
+ class Jiffy
20
+ module Parsers
21
+ module JsonString
22
+ def initialize(*args)
23
+ %% write data;
24
+ super
25
+ end
26
+
27
+ def parse_json_string(p, pe)
28
+ t_p = p + 1
29
+
30
+ %% write init;
31
+ %% write exec;
32
+
33
+ if cs >= json_string_first_final
34
+ p + 1
35
+ else
36
+ raise_unparseable p
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,346 @@
1
+
2
+ # line 1 "json_value.rl"
3
+
4
+ # line 56 "json_value.rl"
5
+
6
+
7
+ class Jiffy
8
+ module Parsers
9
+ module JsonValue
10
+ def initialize(*args)
11
+
12
+ # line 13 "json_value.rb"
13
+ class << self
14
+ attr_accessor :_json_value_actions
15
+ private :_json_value_actions, :_json_value_actions=
16
+ end
17
+ self._json_value_actions = [
18
+ 0, 1, 1, 1, 2, 1, 3, 1,
19
+ 4, 1, 5, 1, 6, 1, 7, 3,
20
+ 8, 0, 9
21
+ ]
22
+
23
+ class << self
24
+ attr_accessor :_json_value_key_offsets
25
+ private :_json_value_key_offsets, :_json_value_key_offsets=
26
+ end
27
+ self._json_value_key_offsets = [
28
+ 0, 0, 9, 10, 11, 12, 13, 14,
29
+ 15, 16, 17, 18
30
+ ]
31
+
32
+ class << self
33
+ attr_accessor :_json_value_trans_keys
34
+ private :_json_value_trans_keys, :_json_value_trans_keys=
35
+ end
36
+ self._json_value_trans_keys = [
37
+ 34, 45, 91, 102, 110, 116, 123, 48,
38
+ 57, 97, 108, 115, 101, 117, 108, 108,
39
+ 114, 117, 0
40
+ ]
41
+
42
+ class << self
43
+ attr_accessor :_json_value_single_lengths
44
+ private :_json_value_single_lengths, :_json_value_single_lengths=
45
+ end
46
+ self._json_value_single_lengths = [
47
+ 0, 7, 1, 1, 1, 1, 1, 1,
48
+ 1, 1, 1, 0
49
+ ]
50
+
51
+ class << self
52
+ attr_accessor :_json_value_range_lengths
53
+ private :_json_value_range_lengths, :_json_value_range_lengths=
54
+ end
55
+ self._json_value_range_lengths = [
56
+ 0, 1, 0, 0, 0, 0, 0, 0,
57
+ 0, 0, 0, 0
58
+ ]
59
+
60
+ class << self
61
+ attr_accessor :_json_value_index_offsets
62
+ private :_json_value_index_offsets, :_json_value_index_offsets=
63
+ end
64
+ self._json_value_index_offsets = [
65
+ 0, 0, 9, 11, 13, 15, 17, 19,
66
+ 21, 23, 25, 27
67
+ ]
68
+
69
+ class << self
70
+ attr_accessor :_json_value_trans_targs
71
+ private :_json_value_trans_targs, :_json_value_trans_targs=
72
+ end
73
+ self._json_value_trans_targs = [
74
+ 11, 11, 11, 2, 6, 9, 11, 11,
75
+ 0, 3, 0, 4, 0, 5, 0, 11,
76
+ 0, 7, 0, 8, 0, 11, 0, 10,
77
+ 0, 5, 0, 0, 0
78
+ ]
79
+
80
+ class << self
81
+ attr_accessor :_json_value_trans_actions
82
+ private :_json_value_trans_actions, :_json_value_trans_actions=
83
+ end
84
+ self._json_value_trans_actions = [
85
+ 15, 1, 3, 11, 9, 13, 5, 1,
86
+ 0, 0, 0, 0, 0, 0, 0, 0,
87
+ 0, 0, 0, 0, 0, 0, 0, 0,
88
+ 0, 0, 0, 0, 0
89
+ ]
90
+
91
+ class << self
92
+ attr_accessor :_json_value_from_state_actions
93
+ private :_json_value_from_state_actions, :_json_value_from_state_actions=
94
+ end
95
+ self._json_value_from_state_actions = [
96
+ 0, 0, 0, 0, 0, 0, 0, 0,
97
+ 0, 0, 0, 7
98
+ ]
99
+
100
+ class << self
101
+ attr_accessor :json_value_start
102
+ end
103
+ self.json_value_start = 1;
104
+ class << self
105
+ attr_accessor :json_value_first_final
106
+ end
107
+ self.json_value_first_final = 11;
108
+ class << self
109
+ attr_accessor :json_value_error
110
+ end
111
+ self.json_value_error = 0;
112
+
113
+ class << self
114
+ attr_accessor :json_value_en_main
115
+ end
116
+ self.json_value_en_main = 1;
117
+
118
+
119
+ # line 63 "json_value.rl"
120
+ super
121
+ end
122
+
123
+ def parse_json_value(p, pe)
124
+
125
+ # line 126 "json_value.rb"
126
+ begin
127
+ p ||= 0
128
+ pe ||= data.length
129
+ cs = json_value_start
130
+ end
131
+
132
+ # line 68 "json_value.rl"
133
+
134
+ # line 135 "json_value.rb"
135
+ begin
136
+ _klen, _trans, _keys, _acts, _nacts = nil
137
+ _goto_level = 0
138
+ _resume = 10
139
+ _eof_trans = 15
140
+ _again = 20
141
+ _test_eof = 30
142
+ _out = 40
143
+ while true
144
+ _trigger_goto = false
145
+ if _goto_level <= 0
146
+ if p == pe
147
+ _goto_level = _test_eof
148
+ next
149
+ end
150
+ if cs == 0
151
+ _goto_level = _out
152
+ next
153
+ end
154
+ end
155
+ if _goto_level <= _resume
156
+ _acts = _json_value_from_state_actions[cs]
157
+ _nacts = _json_value_actions[_acts]
158
+ _acts += 1
159
+ while _nacts > 0
160
+ _nacts -= 1
161
+ _acts += 1
162
+ case _json_value_actions[_acts - 1]
163
+ when 4 then
164
+ # line 45 "json_value.rl"
165
+ begin
166
+ p = p - 1; begin
167
+ p += 1
168
+ _trigger_goto = true
169
+ _goto_level = _out
170
+ break
171
+ end
172
+ end
173
+ # line 174 "json_value.rb"
174
+ end # from state action switch
175
+ end
176
+ if _trigger_goto
177
+ next
178
+ end
179
+ _keys = _json_value_key_offsets[cs]
180
+ _trans = _json_value_index_offsets[cs]
181
+ _klen = _json_value_single_lengths[cs]
182
+ _break_match = false
183
+
184
+ begin
185
+ if _klen > 0
186
+ _lower = _keys
187
+ _upper = _keys + _klen - 1
188
+
189
+ loop do
190
+ break if _upper < _lower
191
+ _mid = _lower + ( (_upper - _lower) >> 1 )
192
+
193
+ if data[p].ord < _json_value_trans_keys[_mid]
194
+ _upper = _mid - 1
195
+ elsif data[p].ord > _json_value_trans_keys[_mid]
196
+ _lower = _mid + 1
197
+ else
198
+ _trans += (_mid - _keys)
199
+ _break_match = true
200
+ break
201
+ end
202
+ end # loop
203
+ break if _break_match
204
+ _keys += _klen
205
+ _trans += _klen
206
+ end
207
+ _klen = _json_value_range_lengths[cs]
208
+ if _klen > 0
209
+ _lower = _keys
210
+ _upper = _keys + (_klen << 1) - 2
211
+ loop do
212
+ break if _upper < _lower
213
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1)
214
+ if data[p].ord < _json_value_trans_keys[_mid]
215
+ _upper = _mid - 2
216
+ elsif data[p].ord > _json_value_trans_keys[_mid+1]
217
+ _lower = _mid + 2
218
+ else
219
+ _trans += ((_mid - _keys) >> 1)
220
+ _break_match = true
221
+ break
222
+ end
223
+ end # loop
224
+ break if _break_match
225
+ _trans += _klen
226
+ end
227
+ end while false
228
+ cs = _json_value_trans_targs[_trans]
229
+ if _json_value_trans_actions[_trans] != 0
230
+ _acts = _json_value_trans_actions[_trans]
231
+ _nacts = _json_value_actions[_acts]
232
+ _acts += 1
233
+ while _nacts > 0
234
+ _nacts -= 1
235
+ _acts += 1
236
+ case _json_value_actions[_acts - 1]
237
+ when 0 then
238
+ # line 5 "json_value.rl"
239
+ begin
240
+
241
+ np = parse_json_string(p, pe)
242
+
243
+ if np
244
+ begin p = (( np))-1; end
245
+
246
+ else
247
+ raise_unparseable p
248
+ end
249
+ end
250
+ when 1 then
251
+ # line 15 "json_value.rl"
252
+ begin
253
+
254
+ np = parse_json_float(p, pe)
255
+
256
+ if np
257
+ begin p = (( np))-1; end
258
+
259
+ else
260
+ raise_unparseable p
261
+ end
262
+ end
263
+ when 2 then
264
+ # line 25 "json_value.rl"
265
+ begin
266
+
267
+ np = parse_json_array(p, pe)
268
+
269
+ if np
270
+ begin p = (( np))-1; end
271
+
272
+ else
273
+ raise_unparseable p
274
+ end
275
+ end
276
+ when 3 then
277
+ # line 35 "json_value.rl"
278
+ begin
279
+
280
+ np = parse_json_object(p, pe)
281
+
282
+ if np
283
+ begin p = (( np))-1; end
284
+
285
+ else
286
+ raise_unparseable p
287
+ end
288
+ end
289
+ when 5 then
290
+ # line 48 "json_value.rl"
291
+ begin
292
+ o.t :null end
293
+ when 6 then
294
+ # line 49 "json_value.rl"
295
+ begin
296
+ o.t :false end
297
+ when 7 then
298
+ # line 50 "json_value.rl"
299
+ begin
300
+ o.t :true end
301
+ when 8 then
302
+ # line 52 "json_value.rl"
303
+ begin
304
+ o.t :begin_string end
305
+ when 9 then
306
+ # line 52 "json_value.rl"
307
+ begin
308
+ o.t :end_string end
309
+ # line 310 "json_value.rb"
310
+ end # action switch
311
+ end
312
+ end
313
+ if _trigger_goto
314
+ next
315
+ end
316
+ end
317
+ if _goto_level <= _again
318
+ if cs == 0
319
+ _goto_level = _out
320
+ next
321
+ end
322
+ p += 1
323
+ if p != pe
324
+ _goto_level = _resume
325
+ next
326
+ end
327
+ end
328
+ if _goto_level <= _test_eof
329
+ end
330
+ if _goto_level <= _out
331
+ break
332
+ end
333
+ end
334
+ end
335
+
336
+ # line 69 "json_value.rl"
337
+
338
+ if cs >= json_value_first_final
339
+ p
340
+ else
341
+ raise_unparseable p
342
+ end
343
+ end
344
+ end
345
+ end
346
+ end
@@ -0,0 +1,78 @@
1
+ %%{
2
+ machine json_value;
3
+ include json_common "json_common.rl";
4
+
5
+ action parse_string {
6
+ np = parse_json_string(fpc, pe)
7
+
8
+ if np
9
+ fexec np;
10
+ else
11
+ raise_unparseable p
12
+ end
13
+ }
14
+
15
+ action parse_number {
16
+ np = parse_json_float(fpc, pe)
17
+
18
+ if np
19
+ fexec np;
20
+ else
21
+ raise_unparseable p
22
+ end
23
+ }
24
+
25
+ action parse_array {
26
+ np = parse_json_array(fpc, pe)
27
+
28
+ if np
29
+ fexec np;
30
+ else
31
+ raise_unparseable p
32
+ end
33
+ }
34
+
35
+ action parse_object {
36
+ np = parse_json_object(fpc, pe)
37
+
38
+ if np
39
+ fexec np;
40
+ else
41
+ raise_unparseable p
42
+ end
43
+ }
44
+
45
+ action exit { fhold; fbreak; }
46
+
47
+ main := (
48
+ Vnull >{ o.t :null } |
49
+ Vfalse >{ o.t :false } |
50
+ Vtrue >{ o.t :true } |
51
+ begin_number >parse_number |
52
+ begin_string >{ o.t :begin_string } >parse_string @{ o.t :end_string } |
53
+ begin_array >parse_array |
54
+ begin_object >parse_object
55
+ ) %*exit;
56
+ }%%
57
+
58
+ class Jiffy
59
+ module Parsers
60
+ module JsonValue
61
+ def initialize(*args)
62
+ %% write data;
63
+ super
64
+ end
65
+
66
+ def parse_json_value(p, pe)
67
+ %% write init;
68
+ %% write exec;
69
+
70
+ if cs >= json_value_first_final
71
+ p
72
+ else
73
+ raise_unparseable p
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,3 @@
1
+ class Jiffy
2
+ VERSION = '1.0.1'
3
+ end
data/lib/jiffy.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'jiffy/parsers/json'
2
+ require 'jiffy/parsers/json_array'
3
+ require 'jiffy/parsers/json_float'
4
+ require 'jiffy/parsers/json_object'
5
+ require 'jiffy/parsers/json_string'
6
+ require 'jiffy/parsers/json_value'
7
+ require 'jiffy/json_outputter'
8
+
9
+ class Jiffy
10
+ class UnparseableError < StandardError; end
11
+ class UnexpectedEndError < StandardError; end
12
+
13
+ class << self
14
+ attr_accessor :json_start
15
+ end
16
+
17
+ prepend Parsers::Json
18
+ prepend Parsers::JsonArray
19
+ prepend Parsers::JsonFloat
20
+ prepend Parsers::JsonObject
21
+ prepend Parsers::JsonString
22
+ prepend Parsers::JsonValue
23
+
24
+ attr_accessor :io, :data, :outputter
25
+
26
+ alias_method :o, :outputter
27
+ alias_method :format, :parse_json
28
+
29
+ def initialize(options = {})
30
+ if options[:in].is_a?(String)
31
+ @io = File.open(options[:in])
32
+ elsif options[:in].respond_to?(:read)
33
+ @io = options[:in]
34
+ else
35
+ raise ArgumentError, 'Invalid input source'
36
+ end
37
+
38
+ @outputter = JsonOutputter.new(options)
39
+ end
40
+
41
+ private
42
+
43
+ def raise_unparseable(p)
44
+ if !@io.closed? && @io.eof? && @data.length == p
45
+ raise UnexpectedEndError, 'Unexpected end of input'
46
+ else
47
+ raise UnparseableError, "Unexpected token at position #{p}"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,47 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'stringio'
4
+ require 'minitest/autorun'
5
+ require 'jiffy'
6
+
7
+ describe Jiffy do
8
+ positive_examples = Dir[File.join(File.dirname(__FILE__), 'positive-examples', '*')]
9
+
10
+ positive_examples.each do |example|
11
+ it "should correctly format #{File.basename(example)}" do
12
+ out = StringIO.new
13
+
14
+ Jiffy.new(in: example, out: out).format
15
+
16
+ assert_equal(out.string, File.read(example).strip)
17
+ end
18
+ end
19
+
20
+ negative_examples = Dir[File.join(File.dirname(__FILE__), 'negative-examples', '*')]
21
+
22
+ negative_examples.each do |example|
23
+ it "should not format #{File.basename(example)}" do
24
+ out = StringIO.new
25
+
26
+ expected_exception = if /unclosed/ =~ example
27
+ Jiffy::UnexpectedEndError
28
+ else
29
+ Jiffy::UnparseableError
30
+ end
31
+
32
+ assert_raises expected_exception do
33
+ Jiffy.new(in: example, out: out).format
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '#initialize' do
39
+ it 'should raise an error when :in is neither an instance of Strirng nor IO' do
40
+ class Foo; end
41
+
42
+ assert_raises ArgumentError do
43
+ Jiffy.new(in: Foo.new)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ [
2
+ 0x00
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ Infinity
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ ,""
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ 01
3
+ ]
@@ -0,0 +1,4 @@
1
+ [
2
+ "line
3
+ break"
4
+ ]
@@ -0,0 +1,3 @@
1
+ {
2
+ "" ""
3
+ }
@@ -0,0 +1,3 @@
1
+ [
2
+ NaN
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ +1.123
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ +100
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ 'single quoted string'
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ 'tab character'
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ true,
3
+ ]
@@ -0,0 +1,3 @@
1
+ {
2
+ "": "",
3
+ }
@@ -0,0 +1 @@
1
+ true
@@ -0,0 +1,2 @@
1
+ [
2
+ true
@@ -0,0 +1,2 @@
1
+ {
2
+ "": ""
@@ -0,0 +1,3 @@
1
+ [
2
+
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ false
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ null
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ 100
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ 1.123E-10
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ -100
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ -100e10
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ -100E10
3
+ ]