bugly 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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