aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/gems/json-1.5.1/lib/json/pure
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gems/json-1.5.1/lib/json/pure')
-rw-r--r--vendor/gems/json-1.5.1/lib/json/pure/generator.rb441
-rw-r--r--vendor/gems/json-1.5.1/lib/json/pure/parser.rb320
2 files changed, 761 insertions, 0 deletions
diff --git a/vendor/gems/json-1.5.1/lib/json/pure/generator.rb b/vendor/gems/json-1.5.1/lib/json/pure/generator.rb
new file mode 100644
index 000000000..44cca605f
--- /dev/null
+++ b/vendor/gems/json-1.5.1/lib/json/pure/generator.rb
@@ -0,0 +1,441 @@
+module JSON
+ MAP = {
+ "\x0" => '\u0000',
+ "\x1" => '\u0001',
+ "\x2" => '\u0002',
+ "\x3" => '\u0003',
+ "\x4" => '\u0004',
+ "\x5" => '\u0005',
+ "\x6" => '\u0006',
+ "\x7" => '\u0007',
+ "\b" => '\b',
+ "\t" => '\t',
+ "\n" => '\n',
+ "\xb" => '\u000b',
+ "\f" => '\f',
+ "\r" => '\r',
+ "\xe" => '\u000e',
+ "\xf" => '\u000f',
+ "\x10" => '\u0010',
+ "\x11" => '\u0011',
+ "\x12" => '\u0012',
+ "\x13" => '\u0013',
+ "\x14" => '\u0014',
+ "\x15" => '\u0015',
+ "\x16" => '\u0016',
+ "\x17" => '\u0017',
+ "\x18" => '\u0018',
+ "\x19" => '\u0019',
+ "\x1a" => '\u001a',
+ "\x1b" => '\u001b',
+ "\x1c" => '\u001c',
+ "\x1d" => '\u001d',
+ "\x1e" => '\u001e',
+ "\x1f" => '\u001f',
+ '"' => '\"',
+ '\\' => '\\\\',
+ } # :nodoc:
+
+ # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
+ # UTF16 big endian characters as \u????, and return it.
+ if defined?(::Encoding)
+ def utf8_to_json(string) # :nodoc:
+ string = string.dup
+ string << '' # XXX workaround: avoid buffer sharing
+ string.force_encoding(::Encoding::ASCII_8BIT)
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
+ string.force_encoding(::Encoding::UTF_8)
+ string
+ end
+
+ def utf8_to_json_ascii(string) # :nodoc:
+ string = string.dup
+ string << '' # XXX workaround: avoid buffer sharing
+ string.force_encoding(::Encoding::ASCII_8BIT)
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
+ string.gsub!(/(
+ (?:
+ [\xc2-\xdf][\x80-\xbf] |
+ [\xe0-\xef][\x80-\xbf]{2} |
+ [\xf0-\xf4][\x80-\xbf]{3}
+ )+ |
+ [\x80-\xc1\xf5-\xff] # invalid
+ )/nx) { |c|
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
+ s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
+ s.gsub!(/.{4}/n, '\\\\u\&')
+ }
+ string.force_encoding(::Encoding::UTF_8)
+ string
+ rescue => e
+ raise GeneratorError, "Caught #{e.class}: #{e}"
+ end
+ else
+ def utf8_to_json(string) # :nodoc:
+ string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
+ end
+
+ def utf8_to_json_ascii(string) # :nodoc:
+ string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
+ string.gsub!(/(
+ (?:
+ [\xc2-\xdf][\x80-\xbf] |
+ [\xe0-\xef][\x80-\xbf]{2} |
+ [\xf0-\xf4][\x80-\xbf]{3}
+ )+ |
+ [\x80-\xc1\xf5-\xff] # invalid
+ )/nx) { |c|
+ c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
+ s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
+ s.gsub!(/.{4}/n, '\\\\u\&')
+ }
+ string
+ rescue => e
+ raise GeneratorError, "Caught #{e.class}: #{e}"
+ end
+ end
+ module_function :utf8_to_json, :utf8_to_json_ascii
+
+ module Pure
+ module Generator
+ # This class is used to create State instances, that are use to hold data
+ # while generating a JSON text from a a Ruby data structure.
+ class State
+ # Creates a State object from _opts_, which ought to be Hash to create
+ # a new State instance configured by _opts_, something else to create
+ # an unconfigured instance. If _opts_ is a State object, it is just
+ # returned.
+ def self.from_state(opts)
+ case
+ when self === opts
+ opts
+ when opts.respond_to?(:to_hash)
+ new(opts.to_hash)
+ when opts.respond_to?(:to_h)
+ new(opts.to_h)
+ else
+ SAFE_STATE_PROTOTYPE.dup
+ end
+ end
+
+ # Instantiates a new State object, configured by _opts_.
+ #
+ # _opts_ can have the following keys:
+ #
+ # * *indent*: a string used to indent levels (default: ''),
+ # * *space*: a string that is put after, a : or , delimiter (default: ''),
+ # * *space_before*: a string that is put before a : pair delimiter (default: ''),
+ # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
+ # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
+ # * *check_circular*: is deprecated now, use the :max_nesting option instead,
+ # * *max_nesting*: sets the maximum level of data structure nesting in
+ # the generated JSON, max_nesting = 0 if no maximum should be checked.
+ # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
+ # generated, otherwise an exception is thrown, if these values are
+ # encountered. This options defaults to false.
+ def initialize(opts = {})
+ @indent = ''
+ @space = ''
+ @space_before = ''
+ @object_nl = ''
+ @array_nl = ''
+ @allow_nan = false
+ @ascii_only = false
+ configure opts
+ end
+
+ # This string is used to indent levels in the JSON text.
+ attr_accessor :indent
+
+ # This string is used to insert a space between the tokens in a JSON
+ # string.
+ attr_accessor :space
+
+ # This string is used to insert a space before the ':' in JSON objects.
+ attr_accessor :space_before
+
+ # This string is put at the end of a line that holds a JSON object (or
+ # Hash).
+ attr_accessor :object_nl
+
+ # This string is put at the end of a line that holds a JSON array.
+ attr_accessor :array_nl
+
+ # This integer returns the maximum level of data structure nesting in
+ # the generated JSON, max_nesting = 0 if no maximum is checked.
+ attr_accessor :max_nesting
+
+ # This integer returns the current depth data structure nesting in the
+ # generated JSON.
+ attr_accessor :depth
+
+ def check_max_nesting # :nodoc:
+ return if @max_nesting.zero?
+ current_nesting = depth + 1
+ current_nesting > @max_nesting and
+ raise NestingError, "nesting of #{current_nesting} is too deep"
+ end
+
+ # Returns true, if circular data structures are checked,
+ # otherwise returns false.
+ def check_circular?
+ !@max_nesting.zero?
+ end
+
+ # Returns true if NaN, Infinity, and -Infinity should be considered as
+ # valid JSON and output.
+ def allow_nan?
+ @allow_nan
+ end
+
+ def ascii_only?
+ @ascii_only
+ end
+
+ # Configure this State instance with the Hash _opts_, and return
+ # itself.
+ def configure(opts)
+ @indent = opts[:indent] if opts.key?(:indent)
+ @space = opts[:space] if opts.key?(:space)
+ @space_before = opts[:space_before] if opts.key?(:space_before)
+ @object_nl = opts[:object_nl] if opts.key?(:object_nl)
+ @array_nl = opts[:array_nl] if opts.key?(:array_nl)
+ @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
+ @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
+ @depth = opts[:depth] || 0
+ if !opts.key?(:max_nesting) # defaults to 19
+ @max_nesting = 19
+ elsif opts[:max_nesting]
+ @max_nesting = opts[:max_nesting]
+ else
+ @max_nesting = 0
+ end
+ self
+ end
+
+ # Returns the configuration instance variables as a hash, that can be
+ # passed to the configure method.
+ def to_h
+ result = {}
+ for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only depth]
+ result[iv.intern] = instance_variable_get("@#{iv}")
+ end
+ result
+ end
+
+ # Generates a valid JSON document from object +obj+ and returns the
+ # result. If no valid JSON document can be created this method raises a
+ # GeneratorError exception.
+ def generate(obj)
+ result = obj.to_json(self)
+ if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
+ raise GeneratorError, "only generation of JSON objects or arrays allowed"
+ end
+ result
+ end
+
+ # Return the value returned by method +name+.
+ def [](name)
+ __send__ name
+ end
+ end
+
+ module GeneratorMethods
+ module Object
+ # Converts this object to a string (calling #to_s), converts
+ # it to a JSON string, and returns the result. This is a fallback, if no
+ # special method #to_json was defined for some object.
+ def to_json(*) to_s.to_json end
+ end
+
+ module Hash
+ # Returns a JSON string containing a JSON object, that is unparsed from
+ # this Hash instance.
+ # _state_ is a JSON::State object, that can also be used to configure the
+ # produced JSON string output further.
+ # _depth_ is used to find out nesting depth, to indent accordingly.
+ def to_json(state = nil, *)
+ state = State.from_state(state)
+ state.check_max_nesting
+ json_transform(state)
+ end
+
+ private
+
+ def json_shift(state)
+ state.object_nl.empty? or return ''
+ state.indent * state.depth
+ end
+
+ def json_transform(state)
+ delim = ','
+ delim << state.object_nl
+ result = '{'
+ result << state.object_nl
+ depth = state.depth += 1
+ first = true
+ indent = !state.object_nl.empty?
+ each { |key,value|
+ result << delim unless first
+ result << state.indent * depth if indent
+ result << key.to_s.to_json(state)
+ result << state.space_before
+ result << ':'
+ result << state.space
+ result << value.to_json(state)
+ first = false
+ }
+ depth = state.depth -= 1
+ result << state.object_nl
+ result << state.indent * depth if indent if indent
+ result << '}'
+ result
+ end
+ end
+
+ module Array
+ # Returns a JSON string containing a JSON array, that is unparsed from
+ # this Array instance.
+ # _state_ is a JSON::State object, that can also be used to configure the
+ # produced JSON string output further.
+ def to_json(state = nil, *)
+ state = State.from_state(state)
+ state.check_max_nesting
+ json_transform(state)
+ end
+
+ private
+
+ def json_transform(state)
+ delim = ','
+ delim << state.array_nl
+ result = '['
+ result << state.array_nl
+ depth = state.depth += 1
+ first = true
+ indent = !state.array_nl.empty?
+ each { |value|
+ result << delim unless first
+ result << state.indent * depth if indent
+ result << value.to_json(state)
+ first = false
+ }
+ depth = state.depth -= 1
+ result << state.array_nl
+ result << state.indent * depth if indent
+ result << ']'
+ end
+ end
+
+ module Integer
+ # Returns a JSON string representation for this Integer number.
+ def to_json(*) to_s end
+ end
+
+ module Float
+ # Returns a JSON string representation for this Float number.
+ def to_json(state = nil, *)
+ state = State.from_state(state)
+ case
+ when infinite?
+ if state.allow_nan?
+ to_s
+ else
+ raise GeneratorError, "#{self} not allowed in JSON"
+ end
+ when nan?
+ if state.allow_nan?
+ to_s
+ else
+ raise GeneratorError, "#{self} not allowed in JSON"
+ end
+ else
+ to_s
+ end
+ end
+ end
+
+ module String
+ if defined?(::Encoding)
+ # This string should be encoded with UTF-8 A call to this method
+ # returns a JSON string encoded with UTF16 big endian characters as
+ # \u????.
+ def to_json(state = nil, *args)
+ state = State.from_state(state)
+ if encoding == ::Encoding::UTF_8
+ string = self
+ else
+ string = encode(::Encoding::UTF_8)
+ end
+ if state.ascii_only?
+ '"' << JSON.utf8_to_json_ascii(string) << '"'
+ else
+ '"' << JSON.utf8_to_json(string) << '"'
+ end
+ end
+ else
+ # This string should be encoded with UTF-8 A call to this method
+ # returns a JSON string encoded with UTF16 big endian characters as
+ # \u????.
+ def to_json(state = nil, *args)
+ state = State.from_state(state)
+ if state.ascii_only?
+ '"' << JSON.utf8_to_json_ascii(self) << '"'
+ else
+ '"' << JSON.utf8_to_json(self) << '"'
+ end
+ end
+ end
+
+ # Module that holds the extinding methods if, the String module is
+ # included.
+ module Extend
+ # Raw Strings are JSON Objects (the raw bytes are stored in an
+ # array for the key "raw"). The Ruby String can be created by this
+ # module method.
+ def json_create(o)
+ o['raw'].pack('C*')
+ end
+ end
+
+ # Extends _modul_ with the String::Extend module.
+ def self.included(modul)
+ modul.extend Extend
+ end
+
+ # This method creates a raw object hash, that can be nested into
+ # other data structures and will be unparsed as a raw string. This
+ # method should be used, if you want to convert raw strings to JSON
+ # instead of UTF-8 strings, e. g. binary data.
+ def to_json_raw_object
+ {
+ JSON.create_id => self.class.name,
+ 'raw' => self.unpack('C*'),
+ }
+ end
+
+ # This method creates a JSON text from the result of
+ # a call to to_json_raw_object of this String.
+ def to_json_raw(*args)
+ to_json_raw_object.to_json(*args)
+ end
+ end
+
+ module TrueClass
+ # Returns a JSON string for true: 'true'.
+ def to_json(*) 'true' end
+ end
+
+ module FalseClass
+ # Returns a JSON string for false: 'false'.
+ def to_json(*) 'false' end
+ end
+
+ module NilClass
+ # Returns a JSON string for nil: 'null'.
+ def to_json(*) 'null' end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/gems/json-1.5.1/lib/json/pure/parser.rb b/vendor/gems/json-1.5.1/lib/json/pure/parser.rb
new file mode 100644
index 000000000..8043e675a
--- /dev/null
+++ b/vendor/gems/json-1.5.1/lib/json/pure/parser.rb
@@ -0,0 +1,320 @@
+require 'strscan'
+
+module JSON
+ module Pure
+ # This class implements the JSON parser that is used to parse a JSON string
+ # into a Ruby data structure.
+ class Parser < StringScanner
+ STRING = /" ((?:[^\x0-\x1f"\\] |
+ # escaped special characters:
+ \\["\\\/bfnrt] |
+ \\u[0-9a-fA-F]{4} |
+ # match all but escaped special characters:
+ \\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*)
+ "/nx
+ INTEGER = /(-?0|-?[1-9]\d*)/
+ FLOAT = /(-?
+ (?:0|[1-9]\d*)
+ (?:
+ \.\d+(?i:e[+-]?\d+) |
+ \.\d+ |
+ (?i:e[+-]?\d+)
+ )
+ )/x
+ NAN = /NaN/
+ INFINITY = /Infinity/
+ MINUS_INFINITY = /-Infinity/
+ OBJECT_OPEN = /\{/
+ OBJECT_CLOSE = /\}/
+ ARRAY_OPEN = /\[/
+ ARRAY_CLOSE = /\]/
+ PAIR_DELIMITER = /:/
+ COLLECTION_DELIMITER = /,/
+ TRUE = /true/
+ FALSE = /false/
+ NULL = /null/
+ IGNORE = %r(
+ (?:
+ //[^\n\r]*[\n\r]| # line comments
+ /\* # c-style comments
+ (?:
+ [^*/]| # normal chars
+ /[^*]| # slashes that do not start a nested comment
+ \*[^/]| # asterisks that do not end this comment
+ /(?=\*/) # single slash before this comment's end
+ )*
+ \*/ # the End of this comment
+ |[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr
+ )+
+ )mx
+
+ UNPARSED = Object.new
+
+ # Creates a new JSON::Pure::Parser instance for the string _source_.
+ #
+ # It will be configured by the _opts_ hash. _opts_ can have the following
+ # keys:
+ # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
+ # structures. Disable depth checking with :max_nesting => false|nil|0,
+ # it defaults to 19.
+ # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
+ # defiance of RFC 4627 to be parsed by the Parser. This option defaults
+ # to false.
+ # * *symbolize_names*: If set to true, returns symbols for the names
+ # (keys) in a JSON object. Otherwise strings are returned, which is also
+ # the default.
+ # * *create_additions*: If set to false, the Parser doesn't create
+ # additions even if a matchin class and create_id was found. This option
+ # defaults to true.
+ # * *object_class*: Defaults to Hash
+ # * *array_class*: Defaults to Array
+ def initialize(source, opts = {})
+ opts ||= {}
+ if defined?(::Encoding)
+ if source.encoding == ::Encoding::ASCII_8BIT
+ b = source[0, 4].bytes.to_a
+ source = case
+ when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
+ source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8)
+ when b.size >= 4 && b[0] == 0 && b[2] == 0
+ source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8)
+ when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
+ source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8)
+
+ when b.size >= 4 && b[1] == 0 && b[3] == 0
+ source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8)
+ else
+ source.dup
+ end
+ else
+ source = source.encode(::Encoding::UTF_8)
+ end
+ source.force_encoding(::Encoding::ASCII_8BIT)
+ else
+ b = source
+ source = case
+ when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
+ JSON.iconv('utf-8', 'utf-32be', b)
+ when b.size >= 4 && b[0] == 0 && b[2] == 0
+ JSON.iconv('utf-8', 'utf-16be', b)
+ when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
+ JSON.iconv('utf-8', 'utf-32le', b)
+ when b.size >= 4 && b[1] == 0 && b[3] == 0
+ JSON.iconv('utf-8', 'utf-16le', b)
+ else
+ b
+ end
+ end
+ super source
+ if !opts.key?(:max_nesting) # defaults to 19
+ @max_nesting = 19
+ elsif opts[:max_nesting]
+ @max_nesting = opts[:max_nesting]
+ else
+ @max_nesting = 0
+ end
+ @allow_nan = !!opts[:allow_nan]
+ @symbolize_names = !!opts[:symbolize_names]
+ @create_additions = opts.key?(:create_additions) ? !!opts[:create_additions] : true
+ @create_id = opts[:create_id] || JSON.create_id
+ @object_class = opts[:object_class] || Hash
+ @array_class = opts[:array_class] || Array
+ @match_string = opts[:match_string]
+ end
+
+ alias source string
+
+ # Parses the current JSON string _source_ and returns the complete data
+ # structure as a result.
+ def parse
+ reset
+ obj = nil
+ until eos?
+ case
+ when scan(OBJECT_OPEN)
+ obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
+ @current_nesting = 1
+ obj = parse_object
+ when scan(ARRAY_OPEN)
+ obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
+ @current_nesting = 1
+ obj = parse_array
+ when skip(IGNORE)
+ ;
+ else
+ raise ParserError, "source '#{peek(20)}' not in JSON!"
+ end
+ end
+ obj or raise ParserError, "source did not contain any JSON!"
+ obj
+ end
+
+ private
+
+ # Unescape characters in strings.
+ UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
+ UNESCAPE_MAP.update({
+ ?" => '"',
+ ?\\ => '\\',
+ ?/ => '/',
+ ?b => "\b",
+ ?f => "\f",
+ ?n => "\n",
+ ?r => "\r",
+ ?t => "\t",
+ ?u => nil,
+ })
+
+ EMPTY_8BIT_STRING = ''
+ if ::String.method_defined?(:encode)
+ EMPTY_8BIT_STRING.force_encoding Encoding::ASCII_8BIT
+ end
+
+ def parse_string
+ if scan(STRING)
+ return '' if self[1].empty?
+ string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
+ if u = UNESCAPE_MAP[$&[1]]
+ u
+ else # \uXXXX
+ bytes = EMPTY_8BIT_STRING.dup
+ i = 0
+ while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
+ bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
+ i += 1
+ end
+ JSON.iconv('utf-8', 'utf-16be', bytes)
+ end
+ end
+ if string.respond_to?(:force_encoding)
+ string.force_encoding(::Encoding::UTF_8)
+ end
+ if @create_additions and @match_string
+ for (regexp, klass) in @match_string
+ klass.json_creatable? or next
+ string =~ regexp and return klass.json_create(string)
+ end
+ end
+ string
+ else
+ UNPARSED
+ end
+ rescue => e
+ raise ParserError, "Caught #{e.class} at '#{peek(20)}': #{e}"
+ end
+
+ def parse_value
+ case
+ when scan(FLOAT)
+ Float(self[1])
+ when scan(INTEGER)
+ Integer(self[1])
+ when scan(TRUE)
+ true
+ when scan(FALSE)
+ false
+ when scan(NULL)
+ nil
+ when (string = parse_string) != UNPARSED
+ string
+ when scan(ARRAY_OPEN)
+ @current_nesting += 1
+ ary = parse_array
+ @current_nesting -= 1
+ ary
+ when scan(OBJECT_OPEN)
+ @current_nesting += 1
+ obj = parse_object
+ @current_nesting -= 1
+ obj
+ when @allow_nan && scan(NAN)
+ NaN
+ when @allow_nan && scan(INFINITY)
+ Infinity
+ when @allow_nan && scan(MINUS_INFINITY)
+ MinusInfinity
+ else
+ UNPARSED
+ end
+ end
+
+ def parse_array
+ raise NestingError, "nesting of #@current_nesting is too deep" if
+ @max_nesting.nonzero? && @current_nesting > @max_nesting
+ result = @array_class.new
+ delim = false
+ until eos?
+ case
+ when (value = parse_value) != UNPARSED
+ delim = false
+ result << value
+ skip(IGNORE)
+ if scan(COLLECTION_DELIMITER)
+ delim = true
+ elsif match?(ARRAY_CLOSE)
+ ;
+ else
+ raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
+ end
+ when scan(ARRAY_CLOSE)
+ if delim
+ raise ParserError, "expected next element in array at '#{peek(20)}'!"
+ end
+ break
+ when skip(IGNORE)
+ ;
+ else
+ raise ParserError, "unexpected token in array at '#{peek(20)}'!"
+ end
+ end
+ result
+ end
+
+ def parse_object
+ raise NestingError, "nesting of #@current_nesting is too deep" if
+ @max_nesting.nonzero? && @current_nesting > @max_nesting
+ result = @object_class.new
+ delim = false
+ until eos?
+ case
+ when (string = parse_string) != UNPARSED
+ skip(IGNORE)
+ unless scan(PAIR_DELIMITER)
+ raise ParserError, "expected ':' in object at '#{peek(20)}'!"
+ end
+ skip(IGNORE)
+ unless (value = parse_value).equal? UNPARSED
+ result[@symbolize_names ? string.to_sym : string] = value
+ delim = false
+ skip(IGNORE)
+ if scan(COLLECTION_DELIMITER)
+ delim = true
+ elsif match?(OBJECT_CLOSE)
+ ;
+ else
+ raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!"
+ end
+ else
+ raise ParserError, "expected value in object at '#{peek(20)}'!"
+ end
+ when scan(OBJECT_CLOSE)
+ if delim
+ raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
+ end
+ if @create_additions and klassname = result[@create_id]
+ klass = JSON.deep_const_get klassname
+ break unless klass and klass.json_creatable?
+ result = klass.json_create(result)
+ end
+ break
+ when skip(IGNORE)
+ ;
+ else
+ raise ParserError, "unexpected token in object at '#{peek(20)}'!"
+ end
+ end
+ result
+ end
+ end
+ end
+end