From b14c060ddabfad99deff3e56d847034f7d0946be Mon Sep 17 00:00:00 2001 From: naruse Date: Tue, 30 Aug 2011 02:23:12 +0000 Subject: * ext/json: Merge json gem 1.5.4+ (2149f4185c598fb97db1). [Bug #5173] [ruby-core:38866] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@33122 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/json/generator/generator.c | 63 ++++- ext/json/generator/generator.h | 6 +- ext/json/lib/json.rb | 8 +- ext/json/lib/json/add/core.rb | 42 +++- ext/json/lib/json/common.rb | 82 ++++--- ext/json/lib/json/ext.rb | 17 +- ext/json/parser/parser.c | 513 ++++++++++++++++++++++++++++------------- ext/json/parser/parser.h | 1 + ext/json/parser/parser.rl | 177 +++++++++----- 9 files changed, 635 insertions(+), 274 deletions(-) (limited to 'ext') diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index 4e44178e02..9a90d5f182 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -13,8 +13,8 @@ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, - i_pack, i_unpack, i_create_id, i_extend, i_key_p, i_aref, i_send, - i_respond_to_p, i_match, i_keys, i_depth, i_dup; + i_quirks_mode, i_pack, i_unpack, i_create_id, i_extend, i_key_p, + i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth, i_dup; /* * Copyright 2001-2004 Unicode, Inc. @@ -349,6 +349,16 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len) } } +static void fbuffer_append_str(FBuffer *fb, VALUE str) +{ + const char *newstr = StringValuePtr(str); + unsigned long len = RSTRING_LEN(str); + + RB_GC_GUARD(str); + + fbuffer_append(fb, newstr, len); +} + static void fbuffer_append_char(FBuffer *fb, char newchr) { fbuffer_inc_capa(fb, 1); @@ -688,6 +698,8 @@ static VALUE cState_configure(VALUE self, VALUE opts) state->allow_nan = RTEST(tmp); tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only)); state->ascii_only = RTEST(tmp); + tmp = rb_hash_aref(opts, ID2SYM(i_quirks_mode)); + state->quirks_mode = RTEST(tmp); return self; } @@ -708,6 +720,7 @@ static VALUE cState_to_h(VALUE self) rb_hash_aset(result, ID2SYM(i_array_nl), rb_str_new(state->array_nl, state->array_nl_len)); rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse); + rb_hash_aset(result, ID2SYM(i_quirks_mode), state->quirks_mode ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting)); rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth)); return result; @@ -852,7 +865,7 @@ static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_S static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { VALUE tmp = rb_funcall(obj, i_to_s, 0); - fbuffer_append(buffer, RSTRING_PAIR(tmp)); + fbuffer_append_str(buffer, tmp); } static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) @@ -869,7 +882,7 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); } } - fbuffer_append(buffer, RSTRING_PAIR(tmp)); + fbuffer_append_str(buffer, tmp); } static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) @@ -897,7 +910,7 @@ static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *s } else if (rb_respond_to(obj, i_to_json)) { tmp = rb_funcall(obj, i_to_json, 1, Vstate); Check_Type(tmp, T_STRING); - fbuffer_append(buffer, RSTRING_PAIR(tmp)); + fbuffer_append_str(buffer, tmp); } else { tmp = rb_funcall(obj, i_to_s, 0); Check_Type(tmp, T_STRING); @@ -961,11 +974,14 @@ static VALUE cState_generate(VALUE self, VALUE obj) { VALUE result = cState_partial_generate(self, obj); VALUE re, args[2]; - args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z"); - args[1] = CRegexp_MULTILINE; - re = rb_class_new_instance(2, args, rb_cRegexp); - if (NIL_P(rb_funcall(re, i_match, 1, result))) { - rb_raise(eGeneratorError, "only generation of JSON objects or arrays allowed"); + GET_STATE(self); + if (!state->quirks_mode) { + args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z"); + args[1] = CRegexp_MULTILINE; + re = rb_class_new_instance(2, args, rb_cRegexp); + if (NIL_P(rb_funcall(re, i_match, 1, result))) { + rb_raise(eGeneratorError, "only generation of JSON objects or arrays allowed"); + } } return result; } @@ -1287,6 +1303,29 @@ static VALUE cState_ascii_only_p(VALUE self) return state->ascii_only ? Qtrue : Qfalse; } +/* + * call-seq: quirks_mode? + * + * Returns true, if quirks mode is enabled. Otherwise returns false. + */ +static VALUE cState_quirks_mode_p(VALUE self) +{ + GET_STATE(self); + return state->quirks_mode ? Qtrue : Qfalse; +} + +/* + * call-seq: quirks_mode=(enable) + * + * If set to true, enables the quirks_mode mode. + */ +static VALUE cState_quirks_mode_set(VALUE self, VALUE enable) +{ + GET_STATE(self); + state->quirks_mode = RTEST(enable); + return Qnil; +} + /* * call-seq: depth * @@ -1345,6 +1384,9 @@ void Init_generator() rb_define_method(cState, "check_circular?", cState_check_circular_p, 0); rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0); rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0); + rb_define_method(cState, "quirks_mode?", cState_quirks_mode_p, 0); + rb_define_method(cState, "quirks_mode", cState_quirks_mode_p, 0); + rb_define_method(cState, "quirks_mode=", cState_quirks_mode_set, 1); rb_define_method(cState, "depth", cState_depth, 0); rb_define_method(cState, "depth=", cState_depth_set, 1); rb_define_method(cState, "configure", cState_configure, 1); @@ -1392,6 +1434,7 @@ void Init_generator() i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); i_ascii_only = rb_intern("ascii_only"); + i_quirks_mode = rb_intern("quirks_mode"); i_depth = rb_intern("depth"); i_pack = rb_intern("pack"); i_unpack = rb_intern("unpack"); diff --git a/ext/json/generator/generator.h b/ext/json/generator/generator.h index ee496fe221..f882ea004b 100644 --- a/ext/json/generator/generator.h +++ b/ext/json/generator/generator.h @@ -45,7 +45,10 @@ #define RSTRING_LEN(string) RSTRING(string)->len #endif -#define RSTRING_PAIR(string) RSTRING_PTR(string), RSTRING_LEN(string) +/* We don't need to guard objects for rbx, so let's do nothing at all. */ +#ifndef RB_GC_GUARD +#define RB_GC_GUARD(object) +#endif /* fbuffer implementation */ @@ -123,6 +126,7 @@ typedef struct JSON_Generator_StateStruct { long max_nesting; char allow_nan; char ascii_only; + char quirks_mode; long depth; } JSON_Generator_State; diff --git a/ext/json/lib/json.rb b/ext/json/lib/json.rb index d7bc1a2d23..00fe4cae84 100644 --- a/ext/json/lib/json.rb +++ b/ext/json/lib/json.rb @@ -7,13 +7,13 @@ # # Built on two universally available structures: # 1. A collection of name/value pairs. Often referred to as an _object_, hash table, record, struct, keyed list, or associative array. -# 2. An orderd list of values. More commonly named as an _array_, vector, sequence, or list. +# 2. An ordered list of values. More commonly called an _array_, vector, sequence or list. # # To read more about JSON visit: http://json.org # # == Parsing JSON # -# To parse a JSON string received by another application, or generated within +# To parse a JSON string received by another application or generated within # your existing application: # # require 'json' @@ -42,8 +42,8 @@ # puts {:hello => "goodbye"}.to_json => "{\"hello\":\"goodbye\"}" # # JSON.generate only allows objects or arrays to be converted -# to JSON syntax. While to_json accepts many Ruby classes -# even though it only acts a method for serialization: +# to JSON syntax. to_json, however, accepts many Ruby classes +# even though it acts only as a method for serialization: # # require 'json' # diff --git a/ext/json/lib/json/add/core.rb b/ext/json/lib/json/add/core.rb index e9850af8f6..fde53a4d01 100644 --- a/ext/json/lib/json/add/core.rb +++ b/ext/json/lib/json/add/core.rb @@ -5,6 +5,8 @@ unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end require 'date' +require 'complex' +require 'rational' # Symbol serialization/deserialization class Symbol @@ -230,8 +232,8 @@ class Regexp def as_json(*) { JSON.create_id => self.class.name, - 'o' => options, - 's' => source, + 'o' => options, + 's' => source, } end @@ -241,3 +243,39 @@ class Regexp as_json.to_json end end + +class Rational + def self.json_create(object) + Rational(object['n'], object['d']) + end + + def as_json(*) + { + JSON.create_id => self.class.name, + 'n' => numerator, + 'd' => denominator, + } + end + + def to_json(*) + as_json.to_json + end +end + +class Complex + def self.json_create(object) + Complex(object['r'], object['i']) + end + + def as_json(*) + { + JSON.create_id => self.class.name, + 'r' => real, + 'i' => imag, + } + end + + def to_json(*) + as_json.to_json + end +end diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb index 1a5f0481e8..43e249c954 100644 --- a/ext/json/lib/json/common.rb +++ b/ext/json/lib/json/common.rb @@ -2,11 +2,11 @@ require 'json/version' module JSON class << self - # If _object_ is string-like parse the string and return the parsed result + # If _object_ is string-like, parse the string and return the parsed result # as a Ruby data structure. Otherwise generate a JSON text from the Ruby # data structure object and return it. # - # The _opts_ argument is passed through to generate/parse respectively, see + # The _opts_ argument is passed through to generate/parse respectively. See # generate and parse for their documentation. def [](object, opts = {}) if object.respond_to? :to_str @@ -16,7 +16,7 @@ module JSON end end - # Returns the JSON parser class, that is used by JSON. This might be either + # Returns the JSON parser class that is used by JSON. This is either # JSON::Ext::Parser or JSON::Pure::Parser. attr_reader :parser @@ -28,7 +28,7 @@ module JSON end # Return the constant located at _path_. The format of _path_ has to be - # either ::A::B::C or A::B::C. In any case A has to be located at the top + # either ::A::B::C or A::B::C. In any case, A has to be located at the top # level (absolute namespace path?). If there doesn't exist a constant at # the given path, an ArgumentError is raised. def deep_const_get(path) # :nodoc: @@ -81,15 +81,15 @@ module JSON $VERBOSE = old end - # Returns the JSON generator modul, that is used by JSON. This might be + # Returns the JSON generator module that is used by JSON. This is # either JSON::Ext::Generator or JSON::Pure::Generator. attr_reader :generator - # Returns the JSON generator state class, that is used by JSON. This might - # be either JSON::Ext::Generator::State or JSON::Pure::Generator::State. + # Returns the JSON generator state class that is used by JSON. This is + # either JSON::Ext::Generator::State or JSON::Pure::Generator::State. attr_accessor :state - # This is create identifier, that is used to decide, if the _json_create_ + # This is create identifier, which is used to decide if the _json_create_ # hook of a class should be called. It defaults to 'json_class'. attr_accessor :create_id end @@ -104,10 +104,10 @@ module JSON # The base exception for JSON errors. class JSONError < StandardError; end - # This exception is raised, if a parser error occurs. + # This exception is raised if a parser error occurs. class ParserError < JSONError; end - # This exception is raised, if the nesting of parsed datastructures is too + # This exception is raised if the nesting of parsed data structures is too # deep. class NestingError < ParserError; end @@ -115,13 +115,13 @@ module JSON class CircularDatastructure < NestingError; end # :startdoc: - # This exception is raised, if a generator or unparser error occurs. + # This exception is raised if a generator or unparser error occurs. class GeneratorError < JSONError; end # For backwards compatibility UnparserError = GeneratorError - # This exception is raised, if the required unicode support is missing on the - # system. Usually this means, that the iconv library is not installed. + # This exception is raised if the required unicode support is missing on the + # system. Usually this means that the iconv library is not installed. class MissingUnicodeSupport < JSONError; end module_function @@ -131,16 +131,16 @@ module JSON # _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, it defaults + # structures. Disable depth checking with :max_nesting => false. 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 + # (keys) in a JSON object. Otherwise strings are returned. Strings are # 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 + # additions even if a matching class and create_id was found. This option # defaults to true. # * *object_class*: Defaults to Hash # * *array_class*: Defaults to Array @@ -149,19 +149,19 @@ module JSON end # Parse the JSON document _source_ into a Ruby data structure and return it. - # The bang version of the parse method, defaults to the more dangerous values + # The bang version of the parse method defaults to the more dangerous values # for the _opts_ hash, so be sure only to parse trusted _source_ documents. # # _opts_ can have the following keys: # * *max_nesting*: The maximum depth of nesting allowed in the parsed data # structures. Enable depth checking with :max_nesting => anInteger. The parse! - # methods defaults to not doing max depth checking: This can be dangerous, + # methods defaults to not doing max depth checking: This can be dangerous # if someone wants to fill up your stack. # * *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 true. # * *create_additions*: If set to false, the Parser doesn't create - # additions even if a matchin class and create_id was found. This option + # additions even if a matching class and create_id was found. This option # defaults to true. def parse!(source, opts = {}) opts = { @@ -188,7 +188,7 @@ module JSON # * *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: ''), # * *allow_nan*: true if NaN, Infinity, and -Infinity should be - # generated, otherwise an exception is thrown, if these values are + # generated, otherwise an exception is thrown if these values are # encountered. This options defaults to false. # * *max_nesting*: The maximum depth of nesting allowed in the data # structures from which JSON is to be generated. Disable depth checking @@ -196,9 +196,13 @@ module JSON # # See also the fast_generate for the fastest creation method with the least # amount of sanity checks, and the pretty_generate method for some - # defaults for a pretty output. + # defaults for pretty output. def generate(obj, opts = nil) - state = SAFE_STATE_PROTOTYPE.dup + if State === opts + state, opts = opts, nil + else + state = SAFE_STATE_PROTOTYPE.dup + end if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -223,9 +227,13 @@ module JSON # This method disables the checks for circles in Ruby objects. # # *WARNING*: Be careful not to pass any Ruby data structures with circles as - # _obj_ argument, because this will cause JSON to go into an infinite loop. + # _obj_ argument because this will cause JSON to go into an infinite loop. def fast_generate(obj, opts = nil) - state = FAST_STATE_PROTOTYPE.dup + if State === opts + state, opts = opts, nil + else + state = FAST_STATE_PROTOTYPE.dup + end if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -249,10 +257,14 @@ module JSON # The returned document is a prettier form of the document returned by # #unparse. # - # The _opts_ argument can be used to configure the generator, see the + # The _opts_ argument can be used to configure the generator. See the # generate method for a more detailed explanation. def pretty_generate(obj, opts = nil) - state = PRETTY_STATE_PROTOTYPE.dup + if State === opts + state, opts = opts, nil + else + state = PRETTY_STATE_PROTOTYPE.dup + end if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -273,7 +285,7 @@ module JSON # :startdoc: # Load a ruby data structure from a JSON _source_ and return it. A source can - # either be a string-like object, an IO like object, or an object responding + # either be a string-like object, an IO-like object, or an object responding # to the read method. If _proc_ was given, it will be called with any nested # Ruby object as an argument recursively in depth first order. # @@ -312,10 +324,10 @@ module JSON # Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns # the result. # - # If anIO (an IO like object or an object that responds to the write method) + # If anIO (an IO-like object or an object that responds to the write method) # was given, the resulting JSON is written to it. # - # If the number of nested arrays or objects exceeds _limit_ an ArgumentError + # If the number of nested arrays or objects exceeds _limit_, an ArgumentError # exception is raised. This argument is similar (but not exactly the # same!) to the _limit_ argument in Marshal.dump. # @@ -396,11 +408,11 @@ module ::Kernel nil end - # If _object_ is string-like parse the string and return the parsed result as - # a Ruby data structure. Otherwise generate a JSON text from the Ruby data + # If _object_ is string-like, parse the string and return the parsed result as + # a Ruby data structure. Otherwise, generate a JSON text from the Ruby data # structure object and return it. # - # The _opts_ argument is passed through to generate/parse respectively, see + # The _opts_ argument is passed through to generate/parse respectively. See # generate and parse for their documentation. def JSON(object, *args) if object.respond_to? :to_str @@ -413,10 +425,10 @@ end # Extends any Class to include _json_creatable?_ method. class ::Class - # Returns true, if this class can be used to create an instance + # Returns true if this class can be used to create an instance # from a serialised JSON string. The class has to implement a class - # method _json_create_ that expects a hash as first parameter, which includes - # the required data. + # method _json_create_ that expects a hash as first parameter. The hash + # should include the required data. def json_creatable? respond_to?(:json_create) end diff --git a/ext/json/lib/json/ext.rb b/ext/json/lib/json/ext.rb index 1fbc3fd7d1..7264a857fa 100644 --- a/ext/json/lib/json/ext.rb +++ b/ext/json/lib/json/ext.rb @@ -4,21 +4,8 @@ module JSON # This module holds all the modules/classes that implement JSON's # functionality as C extensions. module Ext - begin - if defined?(RUBY_ENGINE) == 'constant' and RUBY_ENGINE == 'ruby' and RUBY_VERSION =~ /\A1\.9\./ - require 'json/ext/1.9/parser' - require 'json/ext/1.9/generator' - elsif !defined?(RUBY_ENGINE) && RUBY_VERSION =~ /\A1\.8\./ - require 'json/ext/1.8/parser' - require 'json/ext/1.8/generator' - else - require 'json/ext/parser' - require 'json/ext/generator' - end - rescue LoadError - require 'json/ext/parser' - require 'json/ext/generator' - end + require 'json/ext/parser' + require 'json/ext/generator' $DEBUG and warn "Using Ext extension for JSON." JSON.parser = Parser JSON.generator = Generator diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index af57b81c46..4ea663c721 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -78,15 +78,16 @@ static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, - i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class, - i_array_class, i_key_p, i_deep_const_get, i_match, i_match_string, i_aset, i_leftshift; + i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_quirks_mode, + i_object_class, i_array_class, i_key_p, i_deep_const_get, i_match, + i_match_string, i_aset, i_leftshift; -#line 108 "parser.rl" +#line 109 "parser.rl" -#line 90 "parser.c" +#line 91 "parser.c" static const int JSON_object_start = 1; static const int JSON_object_first_final = 27; static const int JSON_object_error = 0; @@ -94,7 +95,7 @@ static const int JSON_object_error = 0; static const int JSON_object_en_main = 1; -#line 148 "parser.rl" +#line 150 "parser.rl" static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -110,14 +111,14 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); -#line 114 "parser.c" +#line 115 "parser.c" { cs = JSON_object_start; } -#line 163 "parser.rl" +#line 165 "parser.rl" -#line 121 "parser.c" +#line 122 "parser.c" { if ( p == pe ) goto _test_eof; @@ -145,7 +146,7 @@ case 2: goto st2; goto st0; tr2: -#line 131 "parser.rl" +#line 132 "parser.rl" { char *np; json->parsing_name = 1; @@ -158,7 +159,7 @@ st3: if ( ++p == pe ) goto _test_eof3; case 3: -#line 162 "parser.c" +#line 163 "parser.c" switch( (*p) ) { case 13: goto st3; case 32: goto st3; @@ -225,7 +226,7 @@ case 8: goto st8; goto st0; tr11: -#line 116 "parser.rl" +#line 117 "parser.rl" { VALUE v = Qnil; char *np = JSON_parse_value(json, p, pe, &v); @@ -245,7 +246,7 @@ st9: if ( ++p == pe ) goto _test_eof9; case 9: -#line 249 "parser.c" +#line 250 "parser.c" switch( (*p) ) { case 13: goto st9; case 32: goto st9; @@ -334,14 +335,14 @@ case 18: goto st9; goto st18; tr4: -#line 139 "parser.rl" +#line 140 "parser.rl" { p--; {p++; cs = 27; goto _out;} } goto st27; st27: if ( ++p == pe ) goto _test_eof27; case 27: -#line 345 "parser.c" +#line 346 "parser.c" goto st0; st19: if ( ++p == pe ) @@ -439,7 +440,7 @@ case 26: _out: {} } -#line 164 "parser.rl" +#line 166 "parser.rl" if (cs >= JSON_object_first_final) { if (json->create_additions) { @@ -458,7 +459,8 @@ case 26: } -#line 462 "parser.c" + +#line 464 "parser.c" static const int JSON_value_start = 1; static const int JSON_value_first_final = 21; static const int JSON_value_error = 0; @@ -466,7 +468,7 @@ static const int JSON_value_error = 0; static const int JSON_value_en_main = 1; -#line 262 "parser.rl" +#line 265 "parser.rl" static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -474,14 +476,14 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul int cs = EVIL; -#line 478 "parser.c" +#line 480 "parser.c" { cs = JSON_value_start; } -#line 269 "parser.rl" +#line 272 "parser.rl" -#line 485 "parser.c" +#line 487 "parser.c" { if ( p == pe ) goto _test_eof; @@ -506,17 +508,17 @@ st0: cs = 0; goto _out; tr0: -#line 210 "parser.rl" +#line 213 "parser.rl" { char *np = JSON_parse_string(json, p, pe, result); if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;} } goto st21; tr2: -#line 215 "parser.rl" +#line 218 "parser.rl" { char *np; - if(pe > p + 9 && !strncmp(MinusInfinity, p, 9)) { + if(pe > p + 9 - json->quirks_mode && !strncmp(MinusInfinity, p, 9)) { if (json->allow_nan) { *result = CMinusInfinity; {p = (( p + 10))-1;} @@ -533,7 +535,7 @@ tr2: } goto st21; tr5: -#line 233 "parser.rl" +#line 236 "parser.rl" { char *np; json->current_nesting++; @@ -543,7 +545,7 @@ tr5: } goto st21; tr9: -#line 241 "parser.rl" +#line 244 "parser.rl" { char *np; json->current_nesting++; @@ -553,7 +555,7 @@ tr9: } goto st21; tr16: -#line 203 "parser.rl" +#line 206 "parser.rl" { if (json->allow_nan) { *result = CInfinity; @@ -563,7 +565,7 @@ tr16: } goto st21; tr18: -#line 196 "parser.rl" +#line 199 "parser.rl" { if (json->allow_nan) { *result = CNaN; @@ -573,19 +575,19 @@ tr18: } goto st21; tr22: -#line 190 "parser.rl" +#line 193 "parser.rl" { *result = Qfalse; } goto st21; tr25: -#line 187 "parser.rl" +#line 190 "parser.rl" { *result = Qnil; } goto st21; tr28: -#line 193 "parser.rl" +#line 196 "parser.rl" { *result = Qtrue; } @@ -594,9 +596,9 @@ st21: if ( ++p == pe ) goto _test_eof21; case 21: -#line 249 "parser.rl" +#line 252 "parser.rl" { p--; {p++; cs = 21; goto _out;} } -#line 600 "parser.c" +#line 602 "parser.c" goto st0; st2: if ( ++p == pe ) @@ -757,7 +759,7 @@ case 20: _out: {} } -#line 270 "parser.rl" +#line 273 "parser.rl" if (cs >= JSON_value_first_final) { return p; @@ -767,15 +769,15 @@ case 20: } -#line 771 "parser.c" +#line 773 "parser.c" static const int JSON_integer_start = 1; -static const int JSON_integer_first_final = 5; +static const int JSON_integer_first_final = 3; static const int JSON_integer_error = 0; static const int JSON_integer_en_main = 1; -#line 286 "parser.rl" +#line 289 "parser.rl" static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -783,15 +785,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res int cs = EVIL; -#line 787 "parser.c" +#line 789 "parser.c" { cs = JSON_integer_start; } -#line 293 "parser.rl" +#line 296 "parser.rl" json->memo = p; -#line 795 "parser.c" +#line 797 "parser.c" { if ( p == pe ) goto _test_eof; @@ -803,7 +805,7 @@ case 1: case 48: goto st3; } if ( 49 <= (*p) && (*p) <= 57 ) - goto st4; + goto st5; goto st0; st0: cs = 0; @@ -815,7 +817,7 @@ case 2: if ( (*p) == 48 ) goto st3; if ( 49 <= (*p) && (*p) <= 57 ) - goto st4; + goto st5; goto st0; st3: if ( ++p == pe ) @@ -825,33 +827,33 @@ case 3: goto st0; goto tr4; tr4: -#line 283 "parser.rl" - { p--; {p++; cs = 5; goto _out;} } - goto st5; -st5: - if ( ++p == pe ) - goto _test_eof5; -case 5: -#line 836 "parser.c" - goto st0; +#line 286 "parser.rl" + { p--; {p++; cs = 4; goto _out;} } + goto st4; st4: if ( ++p == pe ) goto _test_eof4; case 4: +#line 838 "parser.c" + goto st0; +st5: + if ( ++p == pe ) + goto _test_eof5; +case 5: if ( 48 <= (*p) && (*p) <= 57 ) - goto st4; + goto st5; goto tr4; } _test_eof2: cs = 2; goto _test_eof; _test_eof3: cs = 3; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; _test_eof: {} _out: {} } -#line 295 "parser.rl" +#line 298 "parser.rl" if (cs >= JSON_integer_first_final) { long len = p - json->memo; @@ -863,15 +865,15 @@ case 4: } -#line 867 "parser.c" +#line 869 "parser.c" static const int JSON_float_start = 1; -static const int JSON_float_first_final = 10; +static const int JSON_float_first_final = 8; static const int JSON_float_error = 0; static const int JSON_float_en_main = 1; -#line 317 "parser.rl" +#line 320 "parser.rl" static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -879,15 +881,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul int cs = EVIL; -#line 883 "parser.c" +#line 885 "parser.c" { cs = JSON_float_start; } -#line 324 "parser.rl" +#line 327 "parser.rl" json->memo = p; -#line 891 "parser.c" +#line 893 "parser.c" { if ( p == pe ) goto _test_eof; @@ -899,7 +901,7 @@ case 1: case 48: goto st3; } if ( 49 <= (*p) && (*p) <= 57 ) - goto st9; + goto st7; goto st0; st0: cs = 0; @@ -911,7 +913,7 @@ case 2: if ( (*p) == 48 ) goto st3; if ( 49 <= (*p) && (*p) <= 57 ) - goto st9; + goto st7; goto st0; st3: if ( ++p == pe ) @@ -919,8 +921,8 @@ st3: case 3: switch( (*p) ) { case 46: goto st4; - case 69: goto st6; - case 101: goto st6; + case 69: goto st5; + case 101: goto st5; } goto st0; st4: @@ -928,92 +930,92 @@ st4: goto _test_eof4; case 4: if ( 48 <= (*p) && (*p) <= 57 ) - goto st5; + goto st8; goto st0; -st5: +st8: if ( ++p == pe ) - goto _test_eof5; -case 5: + goto _test_eof8; +case 8: switch( (*p) ) { - case 69: goto st6; - case 101: goto st6; + case 69: goto st5; + case 101: goto st5; } if ( (*p) > 46 ) { if ( 48 <= (*p) && (*p) <= 57 ) - goto st5; + goto st8; } else if ( (*p) >= 45 ) goto st0; - goto tr7; -tr7: -#line 311 "parser.rl" - { p--; {p++; cs = 10; goto _out;} } - goto st10; -st10: + goto tr9; +tr9: +#line 314 "parser.rl" + { p--; {p++; cs = 9; goto _out;} } + goto st9; +st9: if ( ++p == pe ) - goto _test_eof10; -case 10: -#line 956 "parser.c" + goto _test_eof9; +case 9: +#line 958 "parser.c" goto st0; -st6: +st5: if ( ++p == pe ) - goto _test_eof6; -case 6: + goto _test_eof5; +case 5: switch( (*p) ) { - case 43: goto st7; - case 45: goto st7; + case 43: goto st6; + case 45: goto st6; } if ( 48 <= (*p) && (*p) <= 57 ) - goto st8; + goto st10; goto st0; -st7: +st6: if ( ++p == pe ) - goto _test_eof7; -case 7: + goto _test_eof6; +case 6: if ( 48 <= (*p) && (*p) <= 57 ) - goto st8; + goto st10; goto st0; -st8: +st10: if ( ++p == pe ) - goto _test_eof8; -case 8: + goto _test_eof10; +case 10: switch( (*p) ) { case 69: goto st0; case 101: goto st0; } if ( (*p) > 46 ) { if ( 48 <= (*p) && (*p) <= 57 ) - goto st8; + goto st10; } else if ( (*p) >= 45 ) goto st0; - goto tr7; -st9: + goto tr9; +st7: if ( ++p == pe ) - goto _test_eof9; -case 9: + goto _test_eof7; +case 7: switch( (*p) ) { case 46: goto st4; - case 69: goto st6; - case 101: goto st6; + case 69: goto st5; + case 101: goto st5; } if ( 48 <= (*p) && (*p) <= 57 ) - goto st9; + goto st7; goto st0; } _test_eof2: cs = 2; goto _test_eof; _test_eof3: cs = 3; goto _test_eof; _test_eof4: cs = 4; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; _test_eof5: cs = 5; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; _test_eof6: cs = 6; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; _test_eof: {} _out: {} } -#line 326 "parser.rl" +#line 329 "parser.rl" if (cs >= JSON_float_first_final) { long len = p - json->memo; @@ -1026,7 +1028,7 @@ case 9: -#line 1030 "parser.c" +#line 1032 "parser.c" static const int JSON_array_start = 1; static const int JSON_array_first_final = 17; static const int JSON_array_error = 0; @@ -1034,7 +1036,7 @@ static const int JSON_array_error = 0; static const int JSON_array_en_main = 1; -#line 366 "parser.rl" +#line 369 "parser.rl" static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -1048,14 +1050,14 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); -#line 1052 "parser.c" +#line 1054 "parser.c" { cs = JSON_array_start; } -#line 379 "parser.rl" +#line 382 "parser.rl" -#line 1059 "parser.c" +#line 1061 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1094,7 +1096,7 @@ case 2: goto st2; goto st0; tr2: -#line 343 "parser.rl" +#line 346 "parser.rl" { VALUE v = Qnil; char *np = JSON_parse_value(json, p, pe, &v); @@ -1114,7 +1116,7 @@ st3: if ( ++p == pe ) goto _test_eof3; case 3: -#line 1118 "parser.c" +#line 1120 "parser.c" switch( (*p) ) { case 13: goto st3; case 32: goto st3; @@ -1214,14 +1216,14 @@ case 12: goto st3; goto st12; tr4: -#line 358 "parser.rl" +#line 361 "parser.rl" { p--; {p++; cs = 17; goto _out;} } goto st17; st17: if ( ++p == pe ) goto _test_eof17; case 17: -#line 1225 "parser.c" +#line 1227 "parser.c" goto st0; st13: if ( ++p == pe ) @@ -1277,7 +1279,7 @@ case 16: _out: {} } -#line 380 "parser.rl" +#line 383 "parser.rl" if(cs >= JSON_array_first_final) { return p + 1; @@ -1358,7 +1360,7 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd) } -#line 1362 "parser.c" +#line 1364 "parser.c" static const int JSON_string_start = 1; static const int JSON_string_first_final = 8; static const int JSON_string_error = 0; @@ -1366,7 +1368,7 @@ static const int JSON_string_error = 0; static const int JSON_string_en_main = 1; -#line 479 "parser.rl" +#line 482 "parser.rl" static int @@ -1388,15 +1390,15 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu *result = rb_str_buf_new(0); -#line 1392 "parser.c" +#line 1394 "parser.c" { cs = JSON_string_start; } -#line 500 "parser.rl" +#line 503 "parser.rl" json->memo = p; -#line 1400 "parser.c" +#line 1402 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1421,7 +1423,7 @@ case 2: goto st0; goto st2; tr2: -#line 465 "parser.rl" +#line 468 "parser.rl" { *result = json_string_unescape(*result, json->memo + 1, p); if (NIL_P(*result)) { @@ -1432,14 +1434,14 @@ tr2: {p = (( p + 1))-1;} } } -#line 476 "parser.rl" +#line 479 "parser.rl" { p--; {p++; cs = 8; goto _out;} } goto st8; st8: if ( ++p == pe ) goto _test_eof8; case 8: -#line 1443 "parser.c" +#line 1445 "parser.c" goto st0; st3: if ( ++p == pe ) @@ -1515,7 +1517,7 @@ case 7: _out: {} } -#line 502 "parser.rl" +#line 505 "parser.rl" if (json->create_additions && RTEST(match_string = json->match_string)) { VALUE klass; @@ -1538,19 +1540,6 @@ case 7: } } - - -#line 1544 "parser.c" -static const int JSON_start = 1; -static const int JSON_first_final = 10; -static const int JSON_error = 0; - -static const int JSON_en_main = 1; - - -#line 550 "parser.rl" - - /* * Document-class: JSON::Ext::Parser * @@ -1632,8 +1621,6 @@ static VALUE convert_encoding(VALUE source) */ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) { - char *ptr; - long len; VALUE source, opts; GET_PARSER_INIT; @@ -1641,9 +1628,6 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) rb_raise(rb_eTypeError, "already initialized instance"); } rb_scan_args(argc, argv, "11", &source, &opts); - source = convert_encoding(StringValue(source)); - ptr = RSTRING_PTR(source); - len = RSTRING_LEN(source); if (!NIL_P(opts)) { opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash"); if (NIL_P(opts)) { @@ -1673,6 +1657,13 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->symbolize_names = 0; } + tmp = ID2SYM(i_quirks_mode); + if (option_given_p(opts, tmp)) { + VALUE quirks_mode = rb_hash_aref(opts, tmp); + json->quirks_mode = RTEST(quirks_mode) ? 1 : 0; + } else { + json->quirks_mode = 0; + } tmp = ID2SYM(i_create_additions); if (option_given_p(opts, tmp)) { json->create_additions = RTEST(rb_hash_aref(opts, tmp)); @@ -1713,20 +1704,29 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) json->object_class = Qnil; json->array_class = Qnil; } + if (!json->quirks_mode) { + source = convert_encoding(StringValue(source)); + } json->current_nesting = 0; - json->len = len; - json->source = ptr; + json->len = RSTRING_LEN(source); + json->source = RSTRING_PTR(source);; json->Vsource = source; return self; } -/* - * call-seq: parse() - * - * Parses the current JSON text _source_ and returns the complete data - * structure as a result. - */ -static VALUE cParser_parse(VALUE self) + +#line 1719 "parser.c" +static const int JSON_start = 1; +static const int JSON_first_final = 10; +static const int JSON_error = 0; + +static const int JSON_en_main = 1; + + +#line 726 "parser.rl" + + +static VALUE cParser_parse_strict(VALUE self) { char *p, *pe; int cs = EVIL; @@ -1739,7 +1739,7 @@ static VALUE cParser_parse(VALUE self) cs = JSON_start; } -#line 735 "parser.rl" +#line 736 "parser.rl" p = json->source; pe = p + json->len; @@ -1799,7 +1799,7 @@ case 5: goto st1; goto st5; tr3: -#line 539 "parser.rl" +#line 715 "parser.rl" { char *np; json->current_nesting = 1; @@ -1808,7 +1808,7 @@ tr3: } goto st10; tr4: -#line 532 "parser.rl" +#line 708 "parser.rl" { char *np; json->current_nesting = 1; @@ -1877,7 +1877,7 @@ case 9: _out: {} } -#line 738 "parser.rl" +#line 739 "parser.rl" if (cs >= JSON_first_final && p == pe) { return result; @@ -1887,6 +1887,197 @@ case 9: } } + + +#line 1893 "parser.c" +static const int JSON_quirks_mode_start = 1; +static const int JSON_quirks_mode_first_final = 10; +static const int JSON_quirks_mode_error = 0; + +static const int JSON_quirks_mode_en_main = 1; + + +#line 764 "parser.rl" + + +static VALUE cParser_parse_quirks_mode(VALUE self) +{ + char *p, *pe; + int cs = EVIL; + VALUE result = Qnil; + GET_PARSER; + + +#line 1912 "parser.c" + { + cs = JSON_quirks_mode_start; + } + +#line 774 "parser.rl" + p = json->source; + pe = p + json->len; + +#line 1921 "parser.c" + { + if ( p == pe ) + goto _test_eof; + switch ( cs ) + { +st1: + if ( ++p == pe ) + goto _test_eof1; +case 1: + switch( (*p) ) { + case 13: goto st1; + case 32: goto st1; + case 34: goto tr2; + case 45: goto tr2; + case 47: goto st6; + case 73: goto tr2; + case 78: goto tr2; + case 91: goto tr2; + case 102: goto tr2; + case 110: goto tr2; + case 116: goto tr2; + case 123: goto tr2; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2; + } else if ( (*p) >= 9 ) + goto st1; + goto st0; +st0: +cs = 0; + goto _out; +tr2: +#line 756 "parser.rl" + { + char *np = JSON_parse_value(json, p, pe, &result); + if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} + } + goto st10; +st10: + if ( ++p == pe ) + goto _test_eof10; +case 10: +#line 1965 "parser.c" + switch( (*p) ) { + case 13: goto st10; + case 32: goto st10; + case 47: goto st2; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st10; + goto st0; +st2: + if ( ++p == pe ) + goto _test_eof2; +case 2: + switch( (*p) ) { + case 42: goto st3; + case 47: goto st5; + } + goto st0; +st3: + if ( ++p == pe ) + goto _test_eof3; +case 3: + if ( (*p) == 42 ) + goto st4; + goto st3; +st4: + if ( ++p == pe ) + goto _test_eof4; +case 4: + switch( (*p) ) { + case 42: goto st4; + case 47: goto st10; + } + goto st3; +st5: + if ( ++p == pe ) + goto _test_eof5; +case 5: + if ( (*p) == 10 ) + goto st10; + goto st5; +st6: + if ( ++p == pe ) + goto _test_eof6; +case 6: + switch( (*p) ) { + case 42: goto st7; + case 47: goto st9; + } + goto st0; +st7: + if ( ++p == pe ) + goto _test_eof7; +case 7: + if ( (*p) == 42 ) + goto st8; + goto st7; +st8: + if ( ++p == pe ) + goto _test_eof8; +case 8: + switch( (*p) ) { + case 42: goto st8; + case 47: goto st1; + } + goto st7; +st9: + if ( ++p == pe ) + goto _test_eof9; +case 9: + if ( (*p) == 10 ) + goto st1; + goto st9; + } + _test_eof1: cs = 1; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 777 "parser.rl" + + if (cs >= JSON_quirks_mode_first_final && p == pe) { + return result; + } else { + rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p); + return Qnil; + } +} + +/* + * call-seq: parse() + * + * Parses the current JSON text _source_ and returns the complete data + * structure as a result. + */ +static VALUE cParser_parse(VALUE self) +{ + GET_PARSER; + + if (json->quirks_mode) { + return cParser_parse_quirks_mode(self); + } else { + return cParser_parse_strict(self); + } +} + + static JSON_Parser *JSON_allocate() { JSON_Parser *json = ALLOC(JSON_Parser); @@ -1926,6 +2117,18 @@ static VALUE cParser_source(VALUE self) return rb_str_dup(json->Vsource); } +/* + * call-seq: quirks_mode?() + * + * Returns a true, if this parser is in quirks_mode, false otherwise. + */ +static VALUE cParser_quirks_mode_p(VALUE self) +{ + GET_PARSER; + return json->quirks_mode ? Qtrue : Qfalse; +} + + void Init_parser() { rb_require("json/common"); @@ -1938,6 +2141,7 @@ void Init_parser() rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); rb_define_method(cParser, "source", cParser_source, 0); + rb_define_method(cParser, "quirks_mode?", cParser_quirks_mode_p, 0); CNaN = rb_const_get(mJSON, rb_intern("NaN")); CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); @@ -1951,6 +2155,7 @@ void Init_parser() i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); i_symbolize_names = rb_intern("symbolize_names"); + i_quirks_mode = rb_intern("quirks_mode"); i_object_class = rb_intern("object_class"); i_array_class = rb_intern("array_class"); i_match = rb_intern("match"); diff --git a/ext/json/parser/parser.h b/ext/json/parser/parser.h index f2ce5a4c2f..fc73810dd6 100644 --- a/ext/json/parser/parser.h +++ b/ext/json/parser/parser.h @@ -44,6 +44,7 @@ typedef struct JSON_ParserStruct { int allow_nan; int parsing_name; int symbolize_names; + int quirks_mode; VALUE object_class; VALUE array_class; int create_additions; diff --git a/ext/json/parser/parser.rl b/ext/json/parser/parser.rl index 21b445e3c0..e7d47e1589 100644 --- a/ext/json/parser/parser.rl +++ b/ext/json/parser/parser.rl @@ -76,8 +76,9 @@ static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, - i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class, - i_array_class, i_key_p, i_deep_const_get, i_match, i_match_string, i_aset, i_leftshift; + i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_quirks_mode, + i_object_class, i_array_class, i_key_p, i_deep_const_get, i_match, + i_match_string, i_aset, i_leftshift; %%{ machine JSON_common; @@ -138,13 +139,14 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, action exit { fhold; fbreak; } - a_pair = ignore* begin_name >parse_name - ignore* name_separator ignore* - begin_value >parse_value; + pair = ignore* begin_name >parse_name ignore* name_separator ignore* begin_value >parse_value; + next_pair = ignore* value_separator pair; - main := begin_object - (a_pair (ignore* value_separator a_pair)*)? - ignore* end_object @exit; + main := ( + begin_object + (pair (next_pair)*)? ignore* + end_object + ) @exit; }%% static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -178,6 +180,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu } } + %%{ machine JSON_value; include JSON_common; @@ -214,7 +217,7 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu action parse_number { char *np; - if(pe > fpc + 9 && !strncmp(MinusInfinity, fpc, 9)) { + if(pe > fpc + 9 - json->quirks_mode && !strncmp(MinusInfinity, fpc, 9)) { if (json->allow_nan) { *result = CMinusInfinity; fexec p + 10; @@ -282,7 +285,7 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul action exit { fhold; fbreak; } - main := '-'? ('0' | [1-9][0-9]*) (^[0-9] @exit); + main := '-'? ('0' | [1-9][0-9]*) (^[0-9]? @exit); }%% static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -313,7 +316,7 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res main := '-'? ( (('0' | [1-9][0-9]*) '.' [0-9]+ ([Ee] [+\-]?[0-9]+)?) | (('0' | [1-9][0-9]*) ([Ee] [+\-]?[0-9]+)) - ) (^[0-9Ee.\-] @exit ); + ) (^[0-9Ee.\-]? @exit ); }%% static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -521,34 +524,6 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu } } - -%%{ - machine JSON; - - write data; - - include JSON_common; - - action parse_object { - char *np; - json->current_nesting = 1; - np = JSON_parse_object(json, fpc, pe, &result); - if (np == NULL) { fhold; fbreak; } else fexec np; - } - - action parse_array { - char *np; - json->current_nesting = 1; - np = JSON_parse_array(json, fpc, pe, &result); - if (np == NULL) { fhold; fbreak; } else fexec np; - } - - main := ignore* ( - begin_object >parse_object | - begin_array >parse_array - ) ignore*; -}%% - /* * Document-class: JSON::Ext::Parser * @@ -630,8 +605,6 @@ static VALUE convert_encoding(VALUE source) */ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) { - char *ptr; - long len; VALUE source, opts; GET_PARSER_INIT; @@ -639,9 +612,6 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) rb_raise(rb_eTypeError, "already initialized instance"); } rb_scan_args(argc, argv, "11", &source, &opts); - source = convert_encoding(StringValue(source)); - ptr = RSTRING_PTR(source); - len = RSTRING_LEN(source); if (!NIL_P(opts)) { opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash"); if (NIL_P(opts)) { @@ -671,6 +641,13 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->symbolize_names = 0; } + tmp = ID2SYM(i_quirks_mode); + if (option_given_p(opts, tmp)) { + VALUE quirks_mode = rb_hash_aref(opts, tmp); + json->quirks_mode = RTEST(quirks_mode) ? 1 : 0; + } else { + json->quirks_mode = 0; + } tmp = ID2SYM(i_create_additions); if (option_given_p(opts, tmp)) { json->create_additions = RTEST(rb_hash_aref(opts, tmp)); @@ -711,20 +688,44 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) json->object_class = Qnil; json->array_class = Qnil; } + if (!json->quirks_mode) { + source = convert_encoding(StringValue(source)); + } json->current_nesting = 0; - json->len = len; - json->source = ptr; + json->len = RSTRING_LEN(source); + json->source = RSTRING_PTR(source);; json->Vsource = source; return self; } -/* - * call-seq: parse() - * - * Parses the current JSON text _source_ and returns the complete data - * structure as a result. - */ -static VALUE cParser_parse(VALUE self) +%%{ + machine JSON; + + write data; + + include JSON_common; + + action parse_object { + char *np; + json->current_nesting = 1; + np = JSON_parse_object(json, fpc, pe, &result); + if (np == NULL) { fhold; fbreak; } else fexec np; + } + + action parse_array { + char *np; + json->current_nesting = 1; + np = JSON_parse_array(json, fpc, pe, &result); + if (np == NULL) { fhold; fbreak; } else fexec np; + } + + main := ignore* ( + begin_object >parse_object | + begin_array >parse_array + ) ignore*; +}%% + +static VALUE cParser_parse_strict(VALUE self) { char *p, *pe; int cs = EVIL; @@ -744,6 +745,62 @@ static VALUE cParser_parse(VALUE self) } } + +%%{ + machine JSON_quirks_mode; + + write data; + + include JSON_common; + + action parse_value { + char *np = JSON_parse_value(json, fpc, pe, &result); + if (np == NULL) { fhold; fbreak; } else fexec np; + } + + main := ignore* ( + begin_value >parse_value + ) ignore*; +}%% + +static VALUE cParser_parse_quirks_mode(VALUE self) +{ + char *p, *pe; + int cs = EVIL; + VALUE result = Qnil; + GET_PARSER; + + %% write init; + p = json->source; + pe = p + json->len; + %% write exec; + + if (cs >= JSON_quirks_mode_first_final && p == pe) { + return result; + } else { + rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p); + return Qnil; + } +} + +/* + * call-seq: parse() + * + * Parses the current JSON text _source_ and returns the complete data + * structure as a result. + */ +static VALUE cParser_parse(VALUE self) +{ + GET_PARSER; + + if (json->quirks_mode) { + return cParser_parse_quirks_mode(self); + } else { + return cParser_parse_strict(self); + } +} + + static JSON_Parser *JSON_allocate() { JSON_Parser *json = ALLOC(JSON_Parser); @@ -783,6 +840,18 @@ static VALUE cParser_source(VALUE self) return rb_str_dup(json->Vsource); } +/* + * call-seq: quirks_mode?() + * + * Returns a true, if this parser is in quirks_mode, false otherwise. + */ +static VALUE cParser_quirks_mode_p(VALUE self) +{ + GET_PARSER; + return json->quirks_mode ? Qtrue : Qfalse; +} + + void Init_parser() { rb_require("json/common"); @@ -795,6 +864,7 @@ void Init_parser() rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); rb_define_method(cParser, "source", cParser_source, 0); + rb_define_method(cParser, "quirks_mode?", cParser_quirks_mode_p, 0); CNaN = rb_const_get(mJSON, rb_intern("NaN")); CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); @@ -808,6 +878,7 @@ void Init_parser() i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); i_symbolize_names = rb_intern("symbolize_names"); + i_quirks_mode = rb_intern("quirks_mode"); i_object_class = rb_intern("object_class"); i_array_class = rb_intern("array_class"); i_match = rb_intern("match"); -- cgit v1.2.3