bugly 0.1.0

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.
@@ -0,0 +1,442 @@
1
+ module JSON
2
+ MAP = {
3
+ "\x0" => '\u0000',
4
+ "\x1" => '\u0001',
5
+ "\x2" => '\u0002',
6
+ "\x3" => '\u0003',
7
+ "\x4" => '\u0004',
8
+ "\x5" => '\u0005',
9
+ "\x6" => '\u0006',
10
+ "\x7" => '\u0007',
11
+ "\b" => '\b',
12
+ "\t" => '\t',
13
+ "\n" => '\n',
14
+ "\xb" => '\u000b',
15
+ "\f" => '\f',
16
+ "\r" => '\r',
17
+ "\xe" => '\u000e',
18
+ "\xf" => '\u000f',
19
+ "\x10" => '\u0010',
20
+ "\x11" => '\u0011',
21
+ "\x12" => '\u0012',
22
+ "\x13" => '\u0013',
23
+ "\x14" => '\u0014',
24
+ "\x15" => '\u0015',
25
+ "\x16" => '\u0016',
26
+ "\x17" => '\u0017',
27
+ "\x18" => '\u0018',
28
+ "\x19" => '\u0019',
29
+ "\x1a" => '\u001a',
30
+ "\x1b" => '\u001b',
31
+ "\x1c" => '\u001c',
32
+ "\x1d" => '\u001d',
33
+ "\x1e" => '\u001e',
34
+ "\x1f" => '\u001f',
35
+ '"' => '\"',
36
+ '\\' => '\\\\',
37
+ } # :nodoc:
38
+
39
+ # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
40
+ # UTF16 big endian characters as \u????, and return it.
41
+ if defined?(::Encoding)
42
+ def utf8_to_json(string) # :nodoc:
43
+ string = string.dup
44
+ string << '' # XXX workaround: avoid buffer sharing
45
+ string.force_encoding(::Encoding::ASCII_8BIT)
46
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
47
+ string.force_encoding(::Encoding::UTF_8)
48
+ string
49
+ end
50
+
51
+ def utf8_to_json_ascii(string) # :nodoc:
52
+ string = string.dup
53
+ string << '' # XXX workaround: avoid buffer sharing
54
+ string.force_encoding(::Encoding::ASCII_8BIT)
55
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
56
+ string.gsub!(/(
57
+ (?:
58
+ [\xc2-\xdf][\x80-\xbf] |
59
+ [\xe0-\xef][\x80-\xbf]{2} |
60
+ [\xf0-\xf4][\x80-\xbf]{3}
61
+ )+ |
62
+ [\x80-\xc1\xf5-\xff] # invalid
63
+ )/nx) { |c|
64
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
65
+ s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
66
+ s.gsub!(/.{4}/n, '\\\\u\&')
67
+ }
68
+ string.force_encoding(::Encoding::UTF_8)
69
+ string
70
+ rescue => e
71
+ raise GeneratorError, "Caught #{e.class}: #{e}"
72
+ end
73
+ else
74
+ def utf8_to_json(string) # :nodoc:
75
+ string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
76
+ end
77
+
78
+ def utf8_to_json_ascii(string) # :nodoc:
79
+ string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
80
+ string.gsub!(/(
81
+ (?:
82
+ [\xc2-\xdf][\x80-\xbf] |
83
+ [\xe0-\xef][\x80-\xbf]{2} |
84
+ [\xf0-\xf4][\x80-\xbf]{3}
85
+ )+ |
86
+ [\x80-\xc1\xf5-\xff] # invalid
87
+ )/nx) { |c|
88
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
89
+ s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
90
+ s.gsub!(/.{4}/n, '\\\\u\&')
91
+ }
92
+ string
93
+ rescue => e
94
+ raise GeneratorError, "Caught #{e.class}: #{e}"
95
+ end
96
+ end
97
+ module_function :utf8_to_json, :utf8_to_json_ascii
98
+
99
+ module Pure
100
+ module Generator
101
+ # This class is used to create State instances, that are use to hold data
102
+ # while generating a JSON text from a Ruby data structure.
103
+ class State
104
+ # Creates a State object from _opts_, which ought to be Hash to create
105
+ # a new State instance configured by _opts_, something else to create
106
+ # an unconfigured instance. If _opts_ is a State object, it is just
107
+ # returned.
108
+ def self.from_state(opts)
109
+ case
110
+ when self === opts
111
+ opts
112
+ when opts.respond_to?(:to_hash)
113
+ new(opts.to_hash)
114
+ when opts.respond_to?(:to_h)
115
+ new(opts.to_h)
116
+ else
117
+ SAFE_STATE_PROTOTYPE.dup
118
+ end
119
+ end
120
+
121
+ # Instantiates a new State object, configured by _opts_.
122
+ #
123
+ # _opts_ can have the following keys:
124
+ #
125
+ # * *indent*: a string used to indent levels (default: ''),
126
+ # * *space*: a string that is put after, a : or , delimiter (default: ''),
127
+ # * *space_before*: a string that is put before a : pair delimiter (default: ''),
128
+ # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
129
+ # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
130
+ # * *check_circular*: is deprecated now, use the :max_nesting option instead,
131
+ # * *max_nesting*: sets the maximum level of data structure nesting in
132
+ # the generated JSON, max_nesting = 0 if no maximum should be checked.
133
+ # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
134
+ # generated, otherwise an exception is thrown, if these values are
135
+ # encountered. This options defaults to false.
136
+ def initialize(opts = {})
137
+ @indent = ''
138
+ @space = ''
139
+ @space_before = ''
140
+ @object_nl = ''
141
+ @array_nl = ''
142
+ @allow_nan = false
143
+ @ascii_only = false
144
+ configure opts
145
+ end
146
+
147
+ # This string is used to indent levels in the JSON text.
148
+ attr_accessor :indent
149
+
150
+ # This string is used to insert a space between the tokens in a JSON
151
+ # string.
152
+ attr_accessor :space
153
+
154
+ # This string is used to insert a space before the ':' in JSON objects.
155
+ attr_accessor :space_before
156
+
157
+ # This string is put at the end of a line that holds a JSON object (or
158
+ # Hash).
159
+ attr_accessor :object_nl
160
+
161
+ # This string is put at the end of a line that holds a JSON array.
162
+ attr_accessor :array_nl
163
+
164
+ # This integer returns the maximum level of data structure nesting in
165
+ # the generated JSON, max_nesting = 0 if no maximum is checked.
166
+ attr_accessor :max_nesting
167
+
168
+ # This integer returns the current depth data structure nesting in the
169
+ # generated JSON.
170
+ attr_accessor :depth
171
+
172
+ def check_max_nesting # :nodoc:
173
+ return if @max_nesting.zero?
174
+ current_nesting = depth + 1
175
+ current_nesting > @max_nesting and
176
+ raise NestingError, "nesting of #{current_nesting} is too deep"
177
+ end
178
+
179
+ # Returns true, if circular data structures are checked,
180
+ # otherwise returns false.
181
+ def check_circular?
182
+ !@max_nesting.zero?
183
+ end
184
+
185
+ # Returns true if NaN, Infinity, and -Infinity should be considered as
186
+ # valid JSON and output.
187
+ def allow_nan?
188
+ @allow_nan
189
+ end
190
+
191
+ def ascii_only?
192
+ @ascii_only
193
+ end
194
+
195
+ # Configure this State instance with the Hash _opts_, and return
196
+ # itself.
197
+ def configure(opts)
198
+ @indent = opts[:indent] if opts.key?(:indent)
199
+ @space = opts[:space] if opts.key?(:space)
200
+ @space_before = opts[:space_before] if opts.key?(:space_before)
201
+ @object_nl = opts[:object_nl] if opts.key?(:object_nl)
202
+ @array_nl = opts[:array_nl] if opts.key?(:array_nl)
203
+ @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
204
+ @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
205
+ @depth = opts[:depth] || 0
206
+ if !opts.key?(:max_nesting) # defaults to 19
207
+ @max_nesting = 19
208
+ elsif opts[:max_nesting]
209
+ @max_nesting = opts[:max_nesting]
210
+ else
211
+ @max_nesting = 0
212
+ end
213
+ self
214
+ end
215
+ alias merge configure
216
+
217
+ # Returns the configuration instance variables as a hash, that can be
218
+ # passed to the configure method.
219
+ def to_h
220
+ result = {}
221
+ for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only depth]
222
+ result[iv.intern] = instance_variable_get("@#{iv}")
223
+ end
224
+ result
225
+ end
226
+
227
+ # Generates a valid JSON document from object +obj+ and returns the
228
+ # result. If no valid JSON document can be created this method raises a
229
+ # GeneratorError exception.
230
+ def generate(obj)
231
+ result = obj.to_json(self)
232
+ if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
233
+ raise GeneratorError, "only generation of JSON objects or arrays allowed"
234
+ end
235
+ result
236
+ end
237
+
238
+ # Return the value returned by method +name+.
239
+ def [](name)
240
+ __send__ name
241
+ end
242
+ end
243
+
244
+ module GeneratorMethods
245
+ module Object
246
+ # Converts this object to a string (calling #to_s), converts
247
+ # it to a JSON string, and returns the result. This is a fallback, if no
248
+ # special method #to_json was defined for some object.
249
+ def to_json(*) to_s.to_json end
250
+ end
251
+
252
+ module Hash
253
+ # Returns a JSON string containing a JSON object, that is unparsed from
254
+ # this Hash instance.
255
+ # _state_ is a JSON::State object, that can also be used to configure the
256
+ # produced JSON string output further.
257
+ # _depth_ is used to find out nesting depth, to indent accordingly.
258
+ def to_json(state = nil, *)
259
+ state = State.from_state(state)
260
+ state.check_max_nesting
261
+ json_transform(state)
262
+ end
263
+
264
+ private
265
+
266
+ def json_shift(state)
267
+ state.object_nl.empty? or return ''
268
+ state.indent * state.depth
269
+ end
270
+
271
+ def json_transform(state)
272
+ delim = ','
273
+ delim << state.object_nl
274
+ result = '{'
275
+ result << state.object_nl
276
+ depth = state.depth += 1
277
+ first = true
278
+ indent = !state.object_nl.empty?
279
+ each { |key,value|
280
+ result << delim unless first
281
+ result << state.indent * depth if indent
282
+ result << key.to_s.to_json(state)
283
+ result << state.space_before
284
+ result << ':'
285
+ result << state.space
286
+ result << value.to_json(state)
287
+ first = false
288
+ }
289
+ depth = state.depth -= 1
290
+ result << state.object_nl
291
+ result << state.indent * depth if indent if indent
292
+ result << '}'
293
+ result
294
+ end
295
+ end
296
+
297
+ module Array
298
+ # Returns a JSON string containing a JSON array, that is unparsed from
299
+ # this Array instance.
300
+ # _state_ is a JSON::State object, that can also be used to configure the
301
+ # produced JSON string output further.
302
+ def to_json(state = nil, *)
303
+ state = State.from_state(state)
304
+ state.check_max_nesting
305
+ json_transform(state)
306
+ end
307
+
308
+ private
309
+
310
+ def json_transform(state)
311
+ delim = ','
312
+ delim << state.array_nl
313
+ result = '['
314
+ result << state.array_nl
315
+ depth = state.depth += 1
316
+ first = true
317
+ indent = !state.array_nl.empty?
318
+ each { |value|
319
+ result << delim unless first
320
+ result << state.indent * depth if indent
321
+ result << value.to_json(state)
322
+ first = false
323
+ }
324
+ depth = state.depth -= 1
325
+ result << state.array_nl
326
+ result << state.indent * depth if indent
327
+ result << ']'
328
+ end
329
+ end
330
+
331
+ module Integer
332
+ # Returns a JSON string representation for this Integer number.
333
+ def to_json(*) to_s end
334
+ end
335
+
336
+ module Float
337
+ # Returns a JSON string representation for this Float number.
338
+ def to_json(state = nil, *)
339
+ state = State.from_state(state)
340
+ case
341
+ when infinite?
342
+ if state.allow_nan?
343
+ to_s
344
+ else
345
+ raise GeneratorError, "#{self} not allowed in JSON"
346
+ end
347
+ when nan?
348
+ if state.allow_nan?
349
+ to_s
350
+ else
351
+ raise GeneratorError, "#{self} not allowed in JSON"
352
+ end
353
+ else
354
+ to_s
355
+ end
356
+ end
357
+ end
358
+
359
+ module String
360
+ if defined?(::Encoding)
361
+ # This string should be encoded with UTF-8 A call to this method
362
+ # returns a JSON string encoded with UTF16 big endian characters as
363
+ # \u????.
364
+ def to_json(state = nil, *args)
365
+ state = State.from_state(state)
366
+ if encoding == ::Encoding::UTF_8
367
+ string = self
368
+ else
369
+ string = encode(::Encoding::UTF_8)
370
+ end
371
+ if state.ascii_only?
372
+ '"' << JSON.utf8_to_json_ascii(string) << '"'
373
+ else
374
+ '"' << JSON.utf8_to_json(string) << '"'
375
+ end
376
+ end
377
+ else
378
+ # This string should be encoded with UTF-8 A call to this method
379
+ # returns a JSON string encoded with UTF16 big endian characters as
380
+ # \u????.
381
+ def to_json(state = nil, *args)
382
+ state = State.from_state(state)
383
+ if state.ascii_only?
384
+ '"' << JSON.utf8_to_json_ascii(self) << '"'
385
+ else
386
+ '"' << JSON.utf8_to_json(self) << '"'
387
+ end
388
+ end
389
+ end
390
+
391
+ # Module that holds the extinding methods if, the String module is
392
+ # included.
393
+ module Extend
394
+ # Raw Strings are JSON Objects (the raw bytes are stored in an
395
+ # array for the key "raw"). The Ruby String can be created by this
396
+ # module method.
397
+ def json_create(o)
398
+ o['raw'].pack('C*')
399
+ end
400
+ end
401
+
402
+ # Extends _modul_ with the String::Extend module.
403
+ def self.included(modul)
404
+ modul.extend Extend
405
+ end
406
+
407
+ # This method creates a raw object hash, that can be nested into
408
+ # other data structures and will be unparsed as a raw string. This
409
+ # method should be used, if you want to convert raw strings to JSON
410
+ # instead of UTF-8 strings, e. g. binary data.
411
+ def to_json_raw_object
412
+ {
413
+ JSON.create_id => self.class.name,
414
+ 'raw' => self.unpack('C*'),
415
+ }
416
+ end
417
+
418
+ # This method creates a JSON text from the result of
419
+ # a call to to_json_raw_object of this String.
420
+ def to_json_raw(*args)
421
+ to_json_raw_object.to_json(*args)
422
+ end
423
+ end
424
+
425
+ module TrueClass
426
+ # Returns a JSON string for true: 'true'.
427
+ def to_json(*) 'true' end
428
+ end
429
+
430
+ module FalseClass
431
+ # Returns a JSON string for false: 'false'.
432
+ def to_json(*) 'false' end
433
+ end
434
+
435
+ module NilClass
436
+ # Returns a JSON string for nil: 'null'.
437
+ def to_json(*) 'null' end
438
+ end
439
+ end
440
+ end
441
+ end
442
+ end
@@ -0,0 +1,320 @@
1
+ require 'strscan'
2
+
3
+ module JSON
4
+ module Pure
5
+ # This class implements the JSON parser that is used to parse a JSON string
6
+ # into a Ruby data structure.
7
+ class Parser < StringScanner
8
+ STRING = /" ((?:[^\x0-\x1f"\\] |
9
+ # escaped special characters:
10
+ \\["\\\/bfnrt] |
11
+ \\u[0-9a-fA-F]{4} |
12
+ # match all but escaped special characters:
13
+ \\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*)
14
+ "/nx
15
+ INTEGER = /(-?0|-?[1-9]\d*)/
16
+ FLOAT = /(-?
17
+ (?:0|[1-9]\d*)
18
+ (?:
19
+ \.\d+(?i:e[+-]?\d+) |
20
+ \.\d+ |
21
+ (?i:e[+-]?\d+)
22
+ )
23
+ )/x
24
+ NAN = /NaN/
25
+ INFINITY = /Infinity/
26
+ MINUS_INFINITY = /-Infinity/
27
+ OBJECT_OPEN = /\{/
28
+ OBJECT_CLOSE = /\}/
29
+ ARRAY_OPEN = /\[/
30
+ ARRAY_CLOSE = /\]/
31
+ PAIR_DELIMITER = /:/
32
+ COLLECTION_DELIMITER = /,/
33
+ TRUE = /true/
34
+ FALSE = /false/
35
+ NULL = /null/
36
+ IGNORE = %r(
37
+ (?:
38
+ //[^\n\r]*[\n\r]| # line comments
39
+ /\* # c-style comments
40
+ (?:
41
+ [^*/]| # normal chars
42
+ /[^*]| # slashes that do not start a nested comment
43
+ \*[^/]| # asterisks that do not end this comment
44
+ /(?=\*/) # single slash before this comment's end
45
+ )*
46
+ \*/ # the End of this comment
47
+ |[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr
48
+ )+
49
+ )mx
50
+
51
+ UNPARSED = Object.new
52
+
53
+ # Creates a new JSON::Pure::Parser instance for the string _source_.
54
+ #
55
+ # It will be configured by the _opts_ hash. _opts_ can have the following
56
+ # keys:
57
+ # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
58
+ # structures. Disable depth checking with :max_nesting => false|nil|0,
59
+ # it defaults to 19.
60
+ # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
61
+ # defiance of RFC 4627 to be parsed by the Parser. This option defaults
62
+ # to false.
63
+ # * *symbolize_names*: If set to true, returns symbols for the names
64
+ # (keys) in a JSON object. Otherwise strings are returned, which is also
65
+ # the default.
66
+ # * *create_additions*: If set to false, the Parser doesn't create
67
+ # additions even if a matchin class and create_id was found. This option
68
+ # defaults to true.
69
+ # * *object_class*: Defaults to Hash
70
+ # * *array_class*: Defaults to Array
71
+ def initialize(source, opts = {})
72
+ opts ||= {}
73
+ if defined?(::Encoding)
74
+ if source.encoding == ::Encoding::ASCII_8BIT
75
+ b = source[0, 4].bytes.to_a
76
+ source = case
77
+ when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
78
+ source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8)
79
+ when b.size >= 4 && b[0] == 0 && b[2] == 0
80
+ source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8)
81
+ when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
82
+ source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8)
83
+
84
+ when b.size >= 4 && b[1] == 0 && b[3] == 0
85
+ source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8)
86
+ else
87
+ source.dup
88
+ end
89
+ else
90
+ source = source.encode(::Encoding::UTF_8)
91
+ end
92
+ source.force_encoding(::Encoding::ASCII_8BIT)
93
+ else
94
+ b = source
95
+ source = case
96
+ when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
97
+ JSON.iconv('utf-8', 'utf-32be', b)
98
+ when b.size >= 4 && b[0] == 0 && b[2] == 0
99
+ JSON.iconv('utf-8', 'utf-16be', b)
100
+ when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
101
+ JSON.iconv('utf-8', 'utf-32le', b)
102
+ when b.size >= 4 && b[1] == 0 && b[3] == 0
103
+ JSON.iconv('utf-8', 'utf-16le', b)
104
+ else
105
+ b
106
+ end
107
+ end
108
+ super source
109
+ if !opts.key?(:max_nesting) # defaults to 19
110
+ @max_nesting = 19
111
+ elsif opts[:max_nesting]
112
+ @max_nesting = opts[:max_nesting]
113
+ else
114
+ @max_nesting = 0
115
+ end
116
+ @allow_nan = !!opts[:allow_nan]
117
+ @symbolize_names = !!opts[:symbolize_names]
118
+ @create_additions = opts.key?(:create_additions) ? !!opts[:create_additions] : true
119
+ @create_id = opts[:create_id] || JSON.create_id
120
+ @object_class = opts[:object_class] || Hash
121
+ @array_class = opts[:array_class] || Array
122
+ @match_string = opts[:match_string]
123
+ end
124
+
125
+ alias source string
126
+
127
+ # Parses the current JSON string _source_ and returns the complete data
128
+ # structure as a result.
129
+ def parse
130
+ reset
131
+ obj = nil
132
+ until eos?
133
+ case
134
+ when scan(OBJECT_OPEN)
135
+ obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
136
+ @current_nesting = 1
137
+ obj = parse_object
138
+ when scan(ARRAY_OPEN)
139
+ obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
140
+ @current_nesting = 1
141
+ obj = parse_array
142
+ when skip(IGNORE)
143
+ ;
144
+ else
145
+ raise ParserError, "source '#{peek(20)}' not in JSON!"
146
+ end
147
+ end
148
+ obj or raise ParserError, "source did not contain any JSON!"
149
+ obj
150
+ end
151
+
152
+ private
153
+
154
+ # Unescape characters in strings.
155
+ UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
156
+ UNESCAPE_MAP.update({
157
+ ?" => '"',
158
+ ?\\ => '\\',
159
+ ?/ => '/',
160
+ ?b => "\b",
161
+ ?f => "\f",
162
+ ?n => "\n",
163
+ ?r => "\r",
164
+ ?t => "\t",
165
+ ?u => nil,
166
+ })
167
+
168
+ EMPTY_8BIT_STRING = ''
169
+ if ::String.method_defined?(:encode)
170
+ EMPTY_8BIT_STRING.force_encoding Encoding::ASCII_8BIT
171
+ end
172
+
173
+ def parse_string
174
+ if scan(STRING)
175
+ return '' if self[1].empty?
176
+ string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
177
+ if u = UNESCAPE_MAP[$&[1]]
178
+ u
179
+ else # \uXXXX
180
+ bytes = EMPTY_8BIT_STRING.dup
181
+ i = 0
182
+ while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
183
+ bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
184
+ i += 1
185
+ end
186
+ JSON.iconv('utf-8', 'utf-16be', bytes)
187
+ end
188
+ end
189
+ if string.respond_to?(:force_encoding)
190
+ string.force_encoding(::Encoding::UTF_8)
191
+ end
192
+ if @create_additions and @match_string
193
+ for (regexp, klass) in @match_string
194
+ klass.json_creatable? or next
195
+ string =~ regexp and return klass.json_create(string)
196
+ end
197
+ end
198
+ string
199
+ else
200
+ UNPARSED
201
+ end
202
+ rescue => e
203
+ raise ParserError, "Caught #{e.class} at '#{peek(20)}': #{e}"
204
+ end
205
+
206
+ def parse_value
207
+ case
208
+ when scan(FLOAT)
209
+ Float(self[1])
210
+ when scan(INTEGER)
211
+ Integer(self[1])
212
+ when scan(TRUE)
213
+ true
214
+ when scan(FALSE)
215
+ false
216
+ when scan(NULL)
217
+ nil
218
+ when (string = parse_string) != UNPARSED
219
+ string
220
+ when scan(ARRAY_OPEN)
221
+ @current_nesting += 1
222
+ ary = parse_array
223
+ @current_nesting -= 1
224
+ ary
225
+ when scan(OBJECT_OPEN)
226
+ @current_nesting += 1
227
+ obj = parse_object
228
+ @current_nesting -= 1
229
+ obj
230
+ when @allow_nan && scan(NAN)
231
+ NaN
232
+ when @allow_nan && scan(INFINITY)
233
+ Infinity
234
+ when @allow_nan && scan(MINUS_INFINITY)
235
+ MinusInfinity
236
+ else
237
+ UNPARSED
238
+ end
239
+ end
240
+
241
+ def parse_array
242
+ raise NestingError, "nesting of #@current_nesting is too deep" if
243
+ @max_nesting.nonzero? && @current_nesting > @max_nesting
244
+ result = @array_class.new
245
+ delim = false
246
+ until eos?
247
+ case
248
+ when (value = parse_value) != UNPARSED
249
+ delim = false
250
+ result << value
251
+ skip(IGNORE)
252
+ if scan(COLLECTION_DELIMITER)
253
+ delim = true
254
+ elsif match?(ARRAY_CLOSE)
255
+ ;
256
+ else
257
+ raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
258
+ end
259
+ when scan(ARRAY_CLOSE)
260
+ if delim
261
+ raise ParserError, "expected next element in array at '#{peek(20)}'!"
262
+ end
263
+ break
264
+ when skip(IGNORE)
265
+ ;
266
+ else
267
+ raise ParserError, "unexpected token in array at '#{peek(20)}'!"
268
+ end
269
+ end
270
+ result
271
+ end
272
+
273
+ def parse_object
274
+ raise NestingError, "nesting of #@current_nesting is too deep" if
275
+ @max_nesting.nonzero? && @current_nesting > @max_nesting
276
+ result = @object_class.new
277
+ delim = false
278
+ until eos?
279
+ case
280
+ when (string = parse_string) != UNPARSED
281
+ skip(IGNORE)
282
+ unless scan(PAIR_DELIMITER)
283
+ raise ParserError, "expected ':' in object at '#{peek(20)}'!"
284
+ end
285
+ skip(IGNORE)
286
+ unless (value = parse_value).equal? UNPARSED
287
+ result[@symbolize_names ? string.to_sym : string] = value
288
+ delim = false
289
+ skip(IGNORE)
290
+ if scan(COLLECTION_DELIMITER)
291
+ delim = true
292
+ elsif match?(OBJECT_CLOSE)
293
+ ;
294
+ else
295
+ raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!"
296
+ end
297
+ else
298
+ raise ParserError, "expected value in object at '#{peek(20)}'!"
299
+ end
300
+ when scan(OBJECT_CLOSE)
301
+ if delim
302
+ raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
303
+ end
304
+ if @create_additions and klassname = result[@create_id]
305
+ klass = JSON.deep_const_get klassname
306
+ break unless klass and klass.json_creatable?
307
+ result = klass.json_create(result)
308
+ end
309
+ break
310
+ when skip(IGNORE)
311
+ ;
312
+ else
313
+ raise ParserError, "unexpected token in object at '#{peek(20)}'!"
314
+ end
315
+ end
316
+ result
317
+ end
318
+ end
319
+ end
320
+ end