summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Frank <flori@ping.de>2011-01-02 23:46:25 +0100
committerFlorian Frank <flori@ping.de>2011-01-02 23:46:25 +0100
commit19bf4b3ea8869e7f1391f8c9de2a5fd4e3ddbc0b (patch)
tree1bce6e88780fec52afadb60135e4a12f7f8b5456
parent3f97501c35f00dec5dbafad8389154c67b1e4542 (diff)
parent6dc725aee27ddf514593ffafc9a98e3b20e698d3 (diff)
downloadjson-19bf4b3ea8869e7f1391f8c9de2a5fd4e3ddbc0b.tar.gz
Merge branch 'string-matching'
Conflicts: .gitignore Rakefile tests/test_json_encoding.rb
-rw-r--r--.gitignore2
-rw-r--r--Rakefile253
-rw-r--r--ext/json/ext/parser/extconf.rb1
-rw-r--r--ext/json/ext/parser/parser.c101
-rw-r--r--ext/json/ext/parser/parser.h7
-rw-r--r--ext/json/ext/parser/parser.rl73
-rw-r--r--java/src/json/ext/OptionsReader.java15
-rw-r--r--java/src/json/ext/Parser.java200
-rw-r--r--java/src/json/ext/Parser.rl56
-rw-r--r--lib/json/pure/parser.rb22
-rw-r--r--tests/setup_variant.rb11
-rwxr-xr-xtests/test_json.rb6
-rwxr-xr-xtests/test_json_addition.rb15
-rw-r--r--tests/test_json_encoding.rb6
-rwxr-xr-xtests/test_json_fixtures.rb6
-rwxr-xr-xtests/test_json_generate.rb6
-rw-r--r--tests/test_json_string_matching.rb40
-rwxr-xr-xtests/test_json_unicode.rb6
18 files changed, 505 insertions, 321 deletions
diff --git a/.gitignore b/.gitignore
index 4b2dce5..694ff3a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
coverage
pkg
.nfs.*
+.idea
+java/Json.iml
diff --git a/Rakefile b/Rakefile
index 7ce6b69..05871c9 100644
--- a/Rakefile
+++ b/Rakefile
@@ -23,11 +23,12 @@ MAKE = ENV['MAKE'] || %w[gmake make].find { |c| system(c, '-v') }
PKG_NAME = 'json'
PKG_TITLE = 'JSON Implementation for Ruby'
PKG_VERSION = File.read('VERSION').chomp
-PKG_FILES = FileList["**/*"].exclude(/CVS|pkg|tmp|coverage|Makefile|\.nfs\./).exclude(/\.(so|bundle|o|class|#{CONFIG['DLEXT']})$/)
+PKG_FILES = FileList["**/*"].exclude(/CVS|pkg|tmp|coverage|Makefile|\.nfs\.|\.iml\Z/).exclude(/\.(so|bundle|o|class|#{CONFIG['DLEXT']})$/)
EXT_ROOT_DIR = 'ext/json/ext'
EXT_PARSER_DIR = "#{EXT_ROOT_DIR}/parser"
EXT_PARSER_DL = "#{EXT_PARSER_DIR}/parser.#{CONFIG['DLEXT']}"
+RAGEL_PATH = "#{EXT_PARSER_DIR}/parser.rl"
EXT_PARSER_SRC = "#{EXT_PARSER_DIR}/parser.c"
PKG_FILES << EXT_PARSER_SRC
EXT_GENERATOR_DIR = "#{EXT_ROOT_DIR}/generator"
@@ -35,6 +36,7 @@ EXT_GENERATOR_DL = "#{EXT_GENERATOR_DIR}/generator.#{CONFIG['DLEXT']}"
EXT_GENERATOR_SRC = "#{EXT_GENERATOR_DIR}/generator.c"
JAVA_DIR = "java/src/json/ext"
+JAVA_RAGEL_PATH = "#{JAVA_DIR}/Parser.rl"
JAVA_PARSER_SRC = "#{JAVA_DIR}/Parser.java"
JAVA_SOURCES = FileList["#{JAVA_DIR}/*.java"]
JAVA_CLASSES = []
@@ -43,7 +45,6 @@ JRUBY_GENERATOR_JAR = File.expand_path("lib/json/ext/generator.jar")
RAGEL_CODEGEN = %w[rlcodegen rlgen-cd ragel].find { |c| system(c, '-v') }
RAGEL_DOTGEN = %w[rlgen-dot rlgen-cd ragel].find { |c| system(c, '-v') }
-RAGEL_PATH = "#{EXT_PARSER_DIR}/parser.rl"
def myruby(*args, &block)
@myruby ||= File.join(CONFIG['bindir'], CONFIG['ruby_install_name'])
@@ -81,110 +82,6 @@ else
task :install => :install_ext
end
-desc "Compiling extension"
-task :compile_ext => [ EXT_PARSER_DL, EXT_GENERATOR_DL ]
-
-file EXT_PARSER_DL => EXT_PARSER_SRC do
- cd EXT_PARSER_DIR do
- myruby 'extconf.rb'
- sh MAKE
- end
- cp "#{EXT_PARSER_DIR}/parser.#{CONFIG['DLEXT']}", EXT_ROOT_DIR
-end
-
-file EXT_GENERATOR_DL => EXT_GENERATOR_SRC do
- cd EXT_GENERATOR_DIR do
- myruby 'extconf.rb'
- sh MAKE
- end
- cp "#{EXT_GENERATOR_DIR}/generator.#{CONFIG['DLEXT']}", EXT_ROOT_DIR
-end
-
-desc "Generate parser with ragel"
-task :ragel => EXT_PARSER_SRC
-
-desc "Delete the ragel generated C source"
-task :ragel_clean do
- rm_rf EXT_PARSER_SRC
-end
-
-file EXT_PARSER_SRC => RAGEL_PATH do
- cd EXT_PARSER_DIR do
- if RAGEL_CODEGEN == 'ragel'
- sh "ragel parser.rl -G2 -o parser.c"
- else
- sh "ragel -x parser.rl | #{RAGEL_CODEGEN} -G2"
- end
- end
-end
-
-desc "Generate diagrams of ragel parser (ps)"
-task :ragel_dot_ps do
- root = 'diagrams'
- specs = []
- File.new(RAGEL_PATH).grep(/^\s*machine\s*(\S+);\s*$/) { specs << $1 }
- for s in specs
- if RAGEL_DOTGEN == 'ragel'
- sh "ragel #{RAGEL_PATH} -S#{s} -p -V | dot -Tps -o#{root}/#{s}.ps"
- else
- sh "ragel -x #{RAGEL_PATH} -S#{s} | #{RAGEL_DOTGEN} -p|dot -Tps -o#{root}/#{s}.ps"
- end
- end
-end
-
-desc "Generate diagrams of ragel parser (png)"
-task :ragel_dot_png do
- root = 'diagrams'
- specs = []
- File.new(RAGEL_PATH).grep(/^\s*machine\s*(\S+);\s*$/) { specs << $1 }
- for s in specs
- if RAGEL_DOTGEN == 'ragel'
- sh "ragel #{RAGEL_PATH} -S#{s} -p -V | dot -Tpng -o#{root}/#{s}.png"
- else
- sh "ragel -x #{RAGEL_PATH} -S#{s} | #{RAGEL_DOTGEN} -p|dot -Tpng -o#{root}/#{s}.png"
- end
- end
-end
-
-desc "Generate diagrams of ragel parser"
-task :ragel_dot => [ :ragel_dot_png, :ragel_dot_ps ]
-
-desc "Testing library (pure ruby)"
-task :test_pure => :clean do
- ENV['JSON'] = 'pure'
- ENV['RUBYOPT'] = "-Ilib #{ENV['RUBYOPT']}"
- myruby '-S', 'testrb', *Dir['tests/*.rb']
-end
-
-desc "Testing library (extension)"
-task :test_ext => :compile_ext do
- ENV['JSON'] = 'ext'
- ENV['RUBYOPT'] = "-Iext:lib #{ENV['RUBYOPT']}"
- myruby '-S', 'testrb', *Dir['./tests/*.rb']
-end
-
-desc "Benchmarking parser"
-task :benchmark_parser do
- ENV['RUBYOPT'] = "-Ilib:ext #{ENV['RUBYOPT']}"
- myruby 'benchmarks/parser_benchmark.rb'
- myruby 'benchmarks/parser2_benchmark.rb'
-end
-
-desc "Benchmarking generator"
-task :benchmark_generator do
- ENV['RUBYOPT'] = "-Ilib:ext #{ENV['RUBYOPT']}"
- myruby 'benchmarks/generator_benchmark.rb'
- myruby 'benchmarks/generator2_benchmark.rb'
-end
-
-desc "Benchmarking library"
-task :benchmark => [ :benchmark_parser, :benchmark_generator ]
-
-desc "Create RDOC documentation"
-task :doc => [ :version, EXT_PARSER_SRC ] do
- sh "sdoc -o doc -t '#{PKG_TITLE}' -m README README lib/json.rb #{FileList['lib/json/**/*.rb']} #{EXT_PARSER_SRC} #{EXT_GENERATOR_SRC}"
-end
-
if defined?(Gem) and defined?(Rake::GemPackageTask)
spec_pure = Gem::Specification.new do |s|
s.name = 'json_pure'
@@ -204,7 +101,7 @@ if defined?(Gem) and defined?(Rake::GemPackageTask)
s.extra_rdoc_files << 'README'
s.rdoc_options <<
'--title' << 'JSON implemention for ruby' << '--main' << 'README'
- s.test_files.concat Dir['tests/*.rb']
+ s.test_files.concat Dir['./tests/test_*.rb']
s.author = "Florian Frank"
s.email = "flori@ping.de"
@@ -241,7 +138,7 @@ if defined?(Gem) and defined?(Rake::GemPackageTask) and defined?(Rake::Extension
s.extra_rdoc_files << 'README'
s.rdoc_options <<
'--title' << 'JSON implemention for Ruby' << '--main' << 'README'
- s.test_files.concat Dir['tests/*.rb']
+ s.test_files.concat Dir['./tests/test_*.rb']
s.author = "Florian Frank"
s.email = "flori@ping.de"
@@ -290,8 +187,19 @@ EOT
end
end
+desc "Testing library (pure ruby)"
+task :test_pure => :clean do
+ ENV['JSON'] = 'pure'
+ ENV['RUBYOPT'] = "-Ilib #{ENV['RUBYOPT']}"
+ myruby '-S', 'testrb', *Dir['./tests/test_*.rb']
+end
+
+desc "Testing library (pure ruby and extension)"
+task :test => [ :test_pure, :test_ext ]
+
+
if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
- file JAVA_PARSER_SRC => RAGEL_PATH do
+ file JAVA_PARSER_SRC => JAVA_RAGEL_PATH do
cd JAVA_DIR do
if RAGEL_CODEGEN == 'ragel'
sh "ragel Parser.rl -J -o Parser.java"
@@ -301,6 +209,14 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
end
end
+ desc "Generate parser for java with ragel"
+ task :ragel => JAVA_PARSER_SRC
+
+ desc "Delete the ragel generated Java source"
+ task :ragel_clean do
+ rm_rf JAVA_PARSER_SRC
+ end
+
JRUBY_JAR = File.join(Config::CONFIG["libdir"], "jruby.jar")
if File.exist?(JRUBY_JAR)
JAVA_SOURCES.each do |src|
@@ -315,16 +231,8 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
warn "WARNING: Cannot find jruby in path => Cannot build jruby extension!"
end
- desc "Generate parser for java with ragel"
- task :ragel_java => JAVA_PARSER_SRC
-
- desc "Delete the ragel generated Java source"
- task :ragel_clean_java do
- rm_rf JAVA_PARSER_SRC
- end
-
desc "Compiling jruby extension"
- task :compile_jruby => JAVA_CLASSES
+ task :compile_ext => JAVA_CLASSES
desc "Package the jruby gem"
task :jruby_gem => :create_jar do
@@ -334,12 +242,12 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
end
desc "Testing library (jruby)"
- task :test_jruby => :create_jar do
+ task :test_ext => :create_jar do
ENV['JSON'] = 'ext'
- myruby '-S', 'testrb', '-Ilib', *Dir['tests/*.rb']
+ myruby '-S', 'testrb', '-Ilib', *Dir['./tests/test_*.rb']
end
- file JRUBY_PARSER_JAR => :compile_jruby do
+ file JRUBY_PARSER_JAR => :compile_ext do
cd 'java/src' do
parser_classes = FileList[
"json/ext/ByteListTranscoder*.class",
@@ -357,7 +265,7 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
desc "Create parser jar"
task :create_parser_jar => JRUBY_PARSER_JAR
- file JRUBY_GENERATOR_JAR => :compile_jruby do
+ file JRUBY_GENERATOR_JAR => :compile_ext do
cd 'java/src' do
generator_classes = FileList[
"json/ext/ByteListTranscoder*.class",
@@ -380,12 +288,103 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
desc "Build all gems and archives for a new release of the jruby extension."
task :release => [ :clean, :version, :jruby_gem ]
-
- desc "Testing library (jruby extension)"
- task :test => :test_jruby
else
- desc "Testing library (pure ruby and extension)"
- task :test => [ :test_pure, :test_ext ]
+ desc "Compiling extension"
+ task :compile_ext => [ EXT_PARSER_DL, EXT_GENERATOR_DL ]
+
+ file EXT_PARSER_DL => EXT_PARSER_SRC do
+ cd EXT_PARSER_DIR do
+ myruby 'extconf.rb'
+ sh MAKE
+ end
+ cp "#{EXT_PARSER_DIR}/parser.#{CONFIG['DLEXT']}", EXT_ROOT_DIR
+ end
+
+ file EXT_GENERATOR_DL => EXT_GENERATOR_SRC do
+ cd EXT_GENERATOR_DIR do
+ myruby 'extconf.rb'
+ sh MAKE
+ end
+ cp "#{EXT_GENERATOR_DIR}/generator.#{CONFIG['DLEXT']}", EXT_ROOT_DIR
+ end
+
+ desc "Testing library (extension)"
+ task :test_ext => :compile_ext do
+ ENV['JSON'] = 'ext'
+ ENV['RUBYOPT'] = "-Iext:lib #{ENV['RUBYOPT']}"
+ myruby '-S', 'testrb', *Dir['./tests/test_*.rb']
+ end
+
+ desc "Benchmarking parser"
+ task :benchmark_parser do
+ ENV['RUBYOPT'] = "-Ilib:ext #{ENV['RUBYOPT']}"
+ myruby 'benchmarks/parser_benchmark.rb'
+ myruby 'benchmarks/parser2_benchmark.rb'
+ end
+
+ desc "Benchmarking generator"
+ task :benchmark_generator do
+ ENV['RUBYOPT'] = "-Ilib:ext #{ENV['RUBYOPT']}"
+ myruby 'benchmarks/generator_benchmark.rb'
+ myruby 'benchmarks/generator2_benchmark.rb'
+ end
+
+ desc "Benchmarking library"
+ task :benchmark => [ :benchmark_parser, :benchmark_generator ]
+
+ desc "Create RDOC documentation"
+ task :doc => [ :version, EXT_PARSER_SRC ] do
+ sh "sdoc -o doc -t '#{PKG_TITLE}' -m README README lib/json.rb #{FileList['lib/json/**/*.rb']} #{EXT_PARSER_SRC} #{EXT_GENERATOR_SRC}"
+ end
+
+ desc "Generate parser with ragel"
+ task :ragel => EXT_PARSER_SRC
+
+ desc "Delete the ragel generated C source"
+ task :ragel_clean do
+ rm_rf EXT_PARSER_SRC
+ end
+
+ file EXT_PARSER_SRC => RAGEL_PATH do
+ cd EXT_PARSER_DIR do
+ if RAGEL_CODEGEN == 'ragel'
+ sh "ragel parser.rl -G2 -o parser.c"
+ else
+ sh "ragel -x parser.rl | #{RAGEL_CODEGEN} -G2"
+ end
+ end
+ end
+
+ desc "Generate diagrams of ragel parser (ps)"
+ task :ragel_dot_ps do
+ root = 'diagrams'
+ specs = []
+ File.new(RAGEL_PATH).grep(/^\s*machine\s*(\S+);\s*$/) { specs << $1 }
+ for s in specs
+ if RAGEL_DOTGEN == 'ragel'
+ sh "ragel #{RAGEL_PATH} -S#{s} -p -V | dot -Tps -o#{root}/#{s}.ps"
+ else
+ sh "ragel -x #{RAGEL_PATH} -S#{s} | #{RAGEL_DOTGEN} -p|dot -Tps -o#{root}/#{s}.ps"
+ end
+ end
+ end
+
+ desc "Generate diagrams of ragel parser (png)"
+ task :ragel_dot_png do
+ root = 'diagrams'
+ specs = []
+ File.new(RAGEL_PATH).grep(/^\s*machine\s*(\S+);\s*$/) { specs << $1 }
+ for s in specs
+ if RAGEL_DOTGEN == 'ragel'
+ sh "ragel #{RAGEL_PATH} -S#{s} -p -V | dot -Tpng -o#{root}/#{s}.png"
+ else
+ sh "ragel -x #{RAGEL_PATH} -S#{s} | #{RAGEL_DOTGEN} -p|dot -Tpng -o#{root}/#{s}.png"
+ end
+ end
+ end
+
+ desc "Generate diagrams of ragel parser"
+ task :ragel_dot => [ :ragel_dot_png, :ragel_dot_ps ]
desc "Build all gems and archives for a new release of json and json_pure."
task :release => [ :clean, :version, :cross, :native, :gem, ] do
diff --git a/ext/json/ext/parser/extconf.rb b/ext/json/ext/parser/extconf.rb
index f61fc94..d2438cd 100644
--- a/ext/json/ext/parser/extconf.rb
+++ b/ext/json/ext/parser/extconf.rb
@@ -12,4 +12,5 @@ if CONFIG['CC'] =~ /gcc/
end
have_header("re.h")
+have_header("ruby/st.h")
create_makefile 'json/ext/parser'
diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c
index a2f4601..a16d068 100644
--- a/ext/json/ext/parser/parser.c
+++ b/ext/json/ext/parser/parser.c
@@ -79,7 +79,7 @@ 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_array_class, i_key_p, i_deep_const_get, i_match;
#line 108 "parser.rl"
@@ -438,11 +438,11 @@ case 26:
#line 160 "parser.rl"
if (cs >= JSON_object_first_final) {
- if (RTEST(json->create_id)) {
+ if (json->create_additions) {
VALUE klassname = rb_hash_aref(*result, json->create_id);
if (!NIL_P(klassname)) {
VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
- if RTEST(rb_funcall(klass, i_json_creatable_p, 0)) {
+ if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
*result = rb_funcall(klass, i_json_create, 1, *result);
}
}
@@ -1361,21 +1361,34 @@ static const int JSON_string_en_main = 1;
#line 471 "parser.rl"
+static int
+match_i(VALUE regexp, VALUE klass, VALUE memo)
+{
+ if (regexp == Qundef) return ST_STOP;
+ if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) &&
+ RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) {
+ rb_ary_push(memo, klass);
+ return ST_STOP;
+ }
+ return ST_CONTINUE;
+}
+
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result)
{
int cs = EVIL;
+ VALUE match;
*result = rb_str_buf_new(0);
-#line 1371 "parser.c"
+#line 1384 "parser.c"
{
cs = JSON_string_start;
}
-#line 479 "parser.rl"
+#line 492 "parser.rl"
json->memo = p;
-#line 1379 "parser.c"
+#line 1392 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1404,13 +1417,13 @@ tr2:
{
*result = json_string_unescape(*result, json->memo + 1, p);
if (NIL_P(*result)) {
- p--;
- {p++; cs = 8; goto _out;}
- } else {
- FORCE_UTF8(*result);
- {p = (( p + 1))-1;}
- }
- }
+ p--;
+ {p++; cs = 8; goto _out;}
+ } else {
+ FORCE_UTF8(*result);
+ {p = (( p + 1))-1;}
+ }
+ }
#line 468 "parser.rl"
{ p--; {p++; cs = 8; goto _out;} }
goto st8;
@@ -1418,7 +1431,7 @@ st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
-#line 1422 "parser.c"
+#line 1435 "parser.c"
goto st0;
st3:
if ( ++p == pe )
@@ -1494,7 +1507,18 @@ case 7:
_out: {}
}
-#line 481 "parser.rl"
+#line 494 "parser.rl"
+
+ if (json->create_additions && RTEST(match = json->match)) {
+ VALUE klass;
+ VALUE memo = rb_ary_new2(2);
+ rb_ary_push(memo, *result);
+ rb_hash_foreach(match, match_i, memo);
+ klass = rb_ary_entry(memo, 1);
+ if (RTEST(klass)) {
+ *result = rb_funcall(klass, i_json_create, 1, *result);
+ }
+ }
if (json->symbolize_names && json->parsing_name) {
*result = rb_str_intern(*result);
@@ -1508,7 +1532,7 @@ case 7:
-#line 1512 "parser.c"
+#line 1536 "parser.c"
static const int JSON_start = 1;
static const int JSON_first_final = 10;
static const int JSON_error = 0;
@@ -1516,7 +1540,7 @@ static const int JSON_error = 0;
static const int JSON_en_main = 1;
-#line 518 "parser.rl"
+#line 542 "parser.rl"
/*
@@ -1634,26 +1658,25 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
}
tmp = ID2SYM(i_allow_nan);
if (option_given_p(opts, tmp)) {
- VALUE allow_nan = rb_hash_aref(opts, tmp);
- json->allow_nan = RTEST(allow_nan) ? 1 : 0;
+ json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
} else {
json->allow_nan = 0;
}
tmp = ID2SYM(i_symbolize_names);
if (option_given_p(opts, tmp)) {
- VALUE symbolize_names = rb_hash_aref(opts, tmp);
- json->symbolize_names = RTEST(symbolize_names) ? 1 : 0;
+ json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
} else {
json->symbolize_names = 0;
}
tmp = ID2SYM(i_create_additions);
if (option_given_p(opts, tmp)) {
- VALUE create_additions = rb_hash_aref(opts, tmp);
- if (RTEST(create_additions)) {
- json->create_id = rb_funcall(mJSON, i_create_id, 0);
- } else {
- json->create_id = Qnil;
- }
+ json->create_additions = RTEST(rb_hash_aref(opts, tmp));
+ } else {
+ json->create_additions = 1;
+ }
+ tmp = ID2SYM(i_create_id);
+ if (option_given_p(opts, tmp)) {
+ json->create_id = rb_hash_aref(opts, tmp);
} else {
json->create_id = rb_funcall(mJSON, i_create_id, 0);
}
@@ -1669,10 +1692,18 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
} else {
json->array_class = Qnil;
}
+ tmp = ID2SYM(i_match);
+ if (option_given_p(opts, tmp)) {
+ VALUE match = rb_hash_aref(opts, tmp);
+ json->match = RTEST(match) ? match : Qnil;
+ } else {
+ json->match = Qnil;
+ }
}
} else {
json->max_nesting = 19;
json->allow_nan = 0;
+ json->create_additions = 1;
json->create_id = rb_funcall(mJSON, i_create_id, 0);
json->object_class = Qnil;
json->array_class = Qnil;
@@ -1698,16 +1729,16 @@ static VALUE cParser_parse(VALUE self)
GET_PARSER;
-#line 1702 "parser.c"
+#line 1733 "parser.c"
{
cs = JSON_start;
}
-#line 699 "parser.rl"
+#line 730 "parser.rl"
p = json->source;
pe = p + json->len;
-#line 1711 "parser.c"
+#line 1742 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1763,7 +1794,7 @@ case 5:
goto st1;
goto st5;
tr3:
-#line 507 "parser.rl"
+#line 531 "parser.rl"
{
char *np;
json->current_nesting = 1;
@@ -1772,7 +1803,7 @@ tr3:
}
goto st10;
tr4:
-#line 500 "parser.rl"
+#line 524 "parser.rl"
{
char *np;
json->current_nesting = 1;
@@ -1784,7 +1815,7 @@ st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
-#line 1788 "parser.c"
+#line 1819 "parser.c"
switch( (*p) ) {
case 13: goto st10;
case 32: goto st10;
@@ -1841,7 +1872,7 @@ case 9:
_out: {}
}
-#line 702 "parser.rl"
+#line 733 "parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
@@ -1864,6 +1895,7 @@ static void JSON_mark(JSON_Parser *json)
rb_gc_mark_maybe(json->create_id);
rb_gc_mark_maybe(json->object_class);
rb_gc_mark_maybe(json->array_class);
+ rb_gc_mark_maybe(json->match);
}
static void JSON_free(JSON_Parser *json)
@@ -1916,6 +1948,7 @@ void Init_parser()
i_symbolize_names = rb_intern("symbolize_names");
i_object_class = rb_intern("object_class");
i_array_class = rb_intern("array_class");
+ i_match = rb_intern("match");
i_key_p = rb_intern("key?");
i_deep_const_get = rb_intern("deep_const_get");
#ifdef HAVE_RUBY_ENCODING_H
diff --git a/ext/json/ext/parser/parser.h b/ext/json/ext/parser/parser.h
index 688ffda..877ef67 100644
--- a/ext/json/ext/parser/parser.h
+++ b/ext/json/ext/parser/parser.h
@@ -13,6 +13,11 @@
#else
#define FORCE_UTF8(obj)
#endif
+#ifdef HAVE_RUBY_ST_H
+#include "ruby/st.h"
+#else
+#include "st.h"
+#endif
#define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key))
@@ -41,6 +46,8 @@ typedef struct JSON_ParserStruct {
int symbolize_names;
VALUE object_class;
VALUE array_class;
+ int create_additions;
+ VALUE match;
} JSON_Parser;
#define GET_PARSER \
diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl
index dd07485..53f1dbe 100644
--- a/ext/json/ext/parser/parser.rl
+++ b/ext/json/ext/parser/parser.rl
@@ -77,7 +77,7 @@ 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_array_class, i_key_p, i_deep_const_get, i_match;
%%{
machine JSON_common;
@@ -159,11 +159,11 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
%% write exec;
if (cs >= JSON_object_first_final) {
- if (RTEST(json->create_id)) {
+ if (json->create_additions) {
VALUE klassname = rb_hash_aref(*result, json->create_id);
if (!NIL_P(klassname)) {
VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
- if RTEST(rb_funcall(klass, i_json_creatable_p, 0)) {
+ if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
*result = rb_funcall(klass, i_json_create, 1, *result);
}
}
@@ -457,28 +457,52 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd)
action parse_string {
*result = json_string_unescape(*result, json->memo + 1, p);
if (NIL_P(*result)) {
- fhold;
- fbreak;
- } else {
- FORCE_UTF8(*result);
- fexec p + 1;
- }
- }
+ fhold;
+ fbreak;
+ } else {
+ FORCE_UTF8(*result);
+ fexec p + 1;
+ }
+ }
action exit { fhold; fbreak; }
main := '"' ((^(["\\] | 0..0x1f) | '\\'["\\/bfnrt] | '\\u'[0-9a-fA-F]{4} | '\\'^(["\\/bfnrtu]|0..0x1f))* %parse_string) '"' @exit;
}%%
+static int
+match_i(VALUE regexp, VALUE klass, VALUE memo)
+{
+ if (regexp == Qundef) return ST_STOP;
+ if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) &&
+ RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) {
+ rb_ary_push(memo, klass);
+ return ST_STOP;
+ }
+ return ST_CONTINUE;
+}
+
static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result)
{
int cs = EVIL;
+ VALUE match;
*result = rb_str_buf_new(0);
%% write init;
json->memo = p;
%% write exec;
+ if (json->create_additions && RTEST(match = json->match)) {
+ VALUE klass;
+ VALUE memo = rb_ary_new2(2);
+ rb_ary_push(memo, *result);
+ rb_hash_foreach(match, match_i, memo);
+ klass = rb_ary_entry(memo, 1);
+ if (RTEST(klass)) {
+ *result = rb_funcall(klass, i_json_create, 1, *result);
+ }
+ }
+
if (json->symbolize_names && json->parsing_name) {
*result = rb_str_intern(*result);
}
@@ -632,26 +656,25 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
}
tmp = ID2SYM(i_allow_nan);
if (option_given_p(opts, tmp)) {
- VALUE allow_nan = rb_hash_aref(opts, tmp);
- json->allow_nan = RTEST(allow_nan) ? 1 : 0;
+ json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
} else {
json->allow_nan = 0;
}
tmp = ID2SYM(i_symbolize_names);
if (option_given_p(opts, tmp)) {
- VALUE symbolize_names = rb_hash_aref(opts, tmp);
- json->symbolize_names = RTEST(symbolize_names) ? 1 : 0;
+ json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
} else {
json->symbolize_names = 0;
}
tmp = ID2SYM(i_create_additions);
if (option_given_p(opts, tmp)) {
- VALUE create_additions = rb_hash_aref(opts, tmp);
- if (RTEST(create_additions)) {
- json->create_id = rb_funcall(mJSON, i_create_id, 0);
- } else {
- json->create_id = Qnil;
- }
+ json->create_additions = RTEST(rb_hash_aref(opts, tmp));
+ } else {
+ json->create_additions = 1;
+ }
+ tmp = ID2SYM(i_create_id);
+ if (option_given_p(opts, tmp)) {
+ json->create_id = rb_hash_aref(opts, tmp);
} else {
json->create_id = rb_funcall(mJSON, i_create_id, 0);
}
@@ -667,10 +690,18 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
} else {
json->array_class = Qnil;
}
+ tmp = ID2SYM(i_match);
+ if (option_given_p(opts, tmp)) {
+ VALUE match = rb_hash_aref(opts, tmp);
+ json->match = RTEST(match) ? match : Qnil;
+ } else {
+ json->match = Qnil;
+ }
}
} else {
json->max_nesting = 19;
json->allow_nan = 0;
+ json->create_additions = 1;
json->create_id = rb_funcall(mJSON, i_create_id, 0);
json->object_class = Qnil;
json->array_class = Qnil;
@@ -721,6 +752,7 @@ static void JSON_mark(JSON_Parser *json)
rb_gc_mark_maybe(json->create_id);
rb_gc_mark_maybe(json->object_class);
rb_gc_mark_maybe(json->array_class);
+ rb_gc_mark_maybe(json->match);
}
static void JSON_free(JSON_Parser *json)
@@ -773,6 +805,7 @@ void Init_parser()
i_symbolize_names = rb_intern("symbolize_names");
i_object_class = rb_intern("object_class");
i_array_class = rb_intern("array_class");
+ i_match = rb_intern("match");
i_key_p = rb_intern("key?");
i_deep_const_get = rb_intern("deep_const_get");
#ifdef HAVE_RUBY_ENCODING_H
diff --git a/java/src/json/ext/OptionsReader.java b/java/src/json/ext/OptionsReader.java
index 3bc8d5f..018ace4 100644
--- a/java/src/json/ext/OptionsReader.java
+++ b/java/src/json/ext/OptionsReader.java
@@ -74,15 +74,20 @@ final class OptionsReader {
* converted to string
*/
ByteList getString(String key) {
+ RubyString str = getString(key, null);
+ return str == null ? null : str.getByteList().dup();
+ }
+
+ RubyString getString(String key, RubyString defaultValue) {
IRubyObject value = get(key);
- if (value == null || !value.isTrue()) return null;
+ if (value == null || !value.isTrue()) return defaultValue;
RubyString str = value.convertToString();
RuntimeInfo info = getRuntimeInfo();
if (info.encodingsSupported() && str.encoding(context) != info.utf8) {
str = (RubyString)str.encode(context, info.utf8);
}
- return str.getByteList().dup();
+ return str;
}
/**
@@ -105,4 +110,10 @@ final class OptionsReader {
throw runtime.newTypeError(key + " option must be a subclass of "
+ defaultValue);
}
+
+ public RubyHash getHash(String key) {
+ IRubyObject value = get(key);
+ if (value == null || value.isNil()) return new RubyHash(runtime);
+ return (RubyHash) value;
+ }
}
diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java
index c8f6f3d..71e2c4a 100644
--- a/java/src/json/ext/Parser.java
+++ b/java/src/json/ext/Parser.java
@@ -20,6 +20,7 @@ import org.jruby.RubyNumeric;
import org.jruby.RubyObject;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.JumpException;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
@@ -46,11 +47,13 @@ public class Parser extends RubyObject {
private final RuntimeInfo info;
private RubyString vSource;
private RubyString createId;
+ private boolean createAdditions;
private int maxNesting;
private boolean allowNaN;
private boolean symbolizeNames;
private RubyClass objectClass;
private RubyClass arrayClass;
+ private RubyHash match;
private static final int DEFAULT_MAX_NESTING = 19;
@@ -141,20 +144,18 @@ public class Parser extends RubyObject {
@JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
+ Ruby runtime = context.getRuntime();
RubyString source = convertEncoding(context, args[0].convertToString());
- OptionsReader opts =
- new OptionsReader(context, args.length > 1 ? args[1] : null);
-
- this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
- this.allowNaN = opts.getBool("allow_nan", false);
- this.symbolizeNames = opts.getBool("symbolize_names", false);
- this.createId =
- opts.getBool("create_additions", true) ? getCreateId(context)
- : null;
- this.objectClass = opts.getClass("object_class", runtime.getHash());
- this.arrayClass = opts.getClass("array_class", runtime.getArray());
+ OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null);
+ this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
+ this.allowNaN = opts.getBool("allow_nan", false);
+ this.symbolizeNames = opts.getBool("symbolize_names", false);
+ this.createId = opts.getString("create_id", getCreateId(context));
+ this.createAdditions = opts.getBool("create_additions", true);
+ this.objectClass = opts.getClass("object_class", runtime.getHash());
+ this.arrayClass = opts.getClass("array_class", runtime.getArray());
+ this.match = opts.getHash("match");
this.vSource = source;
return this;
@@ -297,11 +298,11 @@ public class Parser extends RubyObject {
}
-// line 323 "Parser.rl"
+// line 324 "Parser.rl"
-// line 305 "Parser.java"
+// line 306 "Parser.java"
private static byte[] init__JSON_value_actions_0()
{
return new byte [] {
@@ -415,7 +416,7 @@ static final int JSON_value_error = 0;
static final int JSON_value_en_main = 1;
-// line 429 "Parser.rl"
+// line 430 "Parser.rl"
ParserResult parseValue(int p, int pe) {
@@ -423,14 +424,14 @@ static final int JSON_value_en_main = 1;
IRubyObject result = null;
-// line 427 "Parser.java"
+// line 428 "Parser.java"
{
cs = JSON_value_start;
}
-// line 436 "Parser.rl"
+// line 437 "Parser.rl"
-// line 434 "Parser.java"
+// line 435 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -456,13 +457,13 @@ case 1:
while ( _nacts-- > 0 ) {
switch ( _JSON_value_actions[_acts++] ) {
case 9:
-// line 414 "Parser.rl"
+// line 415 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 466 "Parser.java"
+// line 467 "Parser.java"
}
}
@@ -525,25 +526,25 @@ case 1:
switch ( _JSON_value_actions[_acts++] )
{
case 0:
-// line 331 "Parser.rl"
+// line 332 "Parser.rl"
{
result = getRuntime().getNil();
}
break;
case 1:
-// line 334 "Parser.rl"
+// line 335 "Parser.rl"
{
result = getRuntime().getFalse();
}
break;
case 2:
-// line 337 "Parser.rl"
+// line 338 "Parser.rl"
{
result = getRuntime().getTrue();
}
break;
case 3:
-// line 340 "Parser.rl"
+// line 341 "Parser.rl"
{
if (parser.allowNaN) {
result = getConstant(CONST_NAN);
@@ -553,7 +554,7 @@ case 1:
}
break;
case 4:
-// line 347 "Parser.rl"
+// line 348 "Parser.rl"
{
if (parser.allowNaN) {
result = getConstant(CONST_INFINITY);
@@ -563,7 +564,7 @@ case 1:
}
break;
case 5:
-// line 354 "Parser.rl"
+// line 355 "Parser.rl"
{
if (pe > p + 9 &&
absSubSequence(p, p + 9).toString().equals(JSON_MINUS_INFINITY)) {
@@ -592,7 +593,7 @@ case 1:
}
break;
case 6:
-// line 380 "Parser.rl"
+// line 381 "Parser.rl"
{
ParserResult res = parseString(p, pe);
if (res == null) {
@@ -605,7 +606,7 @@ case 1:
}
break;
case 7:
-// line 390 "Parser.rl"
+// line 391 "Parser.rl"
{
currentNesting++;
ParserResult res = parseArray(p, pe);
@@ -620,7 +621,7 @@ case 1:
}
break;
case 8:
-// line 402 "Parser.rl"
+// line 403 "Parser.rl"
{
currentNesting++;
ParserResult res = parseObject(p, pe);
@@ -634,7 +635,7 @@ case 1:
}
}
break;
-// line 638 "Parser.java"
+// line 639 "Parser.java"
}
}
}
@@ -654,7 +655,7 @@ case 5:
break; }
}
-// line 437 "Parser.rl"
+// line 438 "Parser.rl"
if (cs >= JSON_value_first_final && result != null) {
return new ParserResult(result, p);
@@ -664,7 +665,7 @@ case 5:
}
-// line 668 "Parser.java"
+// line 669 "Parser.java"
private static byte[] init__JSON_integer_actions_0()
{
return new byte [] {
@@ -763,22 +764,22 @@ static final int JSON_integer_error = 0;
static final int JSON_integer_en_main = 1;
-// line 456 "Parser.rl"
+// line 457 "Parser.rl"
ParserResult parseInteger(int p, int pe) {
int cs = EVIL;
-// line 774 "Parser.java"
+// line 775 "Parser.java"
{
cs = JSON_integer_start;
}
-// line 462 "Parser.rl"
+// line 463 "Parser.rl"
int memo = p;
-// line 782 "Parser.java"
+// line 783 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -859,13 +860,13 @@ case 1:
switch ( _JSON_integer_actions[_acts++] )
{
case 0:
-// line 450 "Parser.rl"
+// line 451 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 869 "Parser.java"
+// line 870 "Parser.java"
}
}
}
@@ -885,7 +886,7 @@ case 5:
break; }
}
-// line 464 "Parser.rl"
+// line 465 "Parser.rl"
if (cs < JSON_integer_first_final) {
return null;
@@ -900,7 +901,7 @@ case 5:
}
-// line 904 "Parser.java"
+// line 905 "Parser.java"
private static byte[] init__JSON_float_actions_0()
{
return new byte [] {
@@ -1002,22 +1003,22 @@ static final int JSON_float_error = 0;
static final int JSON_float_en_main = 1;
-// line 492 "Parser.rl"
+// line 493 "Parser.rl"
ParserResult parseFloat(int p, int pe) {
int cs = EVIL;
-// line 1013 "Parser.java"
+// line 1014 "Parser.java"
{
cs = JSON_float_start;
}
-// line 498 "Parser.rl"
+// line 499 "Parser.rl"
int memo = p;
-// line 1021 "Parser.java"
+// line 1022 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1098,13 +1099,13 @@ case 1:
switch ( _JSON_float_actions[_acts++] )
{
case 0:
-// line 483 "Parser.rl"
+// line 484 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1108 "Parser.java"
+// line 1109 "Parser.java"
}
}
}
@@ -1124,7 +1125,7 @@ case 5:
break; }
}
-// line 500 "Parser.rl"
+// line 501 "Parser.rl"
if (cs < JSON_float_first_final) {
return null;
@@ -1139,7 +1140,7 @@ case 5:
}
-// line 1143 "Parser.java"
+// line 1144 "Parser.java"
private static byte[] init__JSON_string_actions_0()
{
return new byte [] {
@@ -1241,23 +1242,23 @@ static final int JSON_string_error = 0;
static final int JSON_string_en_main = 1;
-// line 544 "Parser.rl"
+// line 545 "Parser.rl"
ParserResult parseString(int p, int pe) {
int cs = EVIL;
- RubyString result = null;
+ IRubyObject result = null;
-// line 1253 "Parser.java"
+// line 1254 "Parser.java"
{
cs = JSON_string_start;
}
-// line 551 "Parser.rl"
+// line 552 "Parser.rl"
int memo = p;
-// line 1261 "Parser.java"
+// line 1262 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1338,7 +1339,7 @@ case 1:
switch ( _JSON_string_actions[_acts++] )
{
case 0:
-// line 519 "Parser.rl"
+// line 520 "Parser.rl"
{
int offset = byteList.begin();
ByteList decoded = decoder.decode(byteList, memo + 1 - offset,
@@ -1353,13 +1354,13 @@ case 1:
}
break;
case 1:
-// line 532 "Parser.rl"
+// line 533 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1363 "Parser.java"
+// line 1364 "Parser.java"
}
}
}
@@ -1379,7 +1380,34 @@ case 5:
break; }
}
-// line 553 "Parser.rl"
+// line 554 "Parser.rl"
+
+ if (parser.createAdditions) {
+ RubyHash match = parser.match;
+ if (match != null) {
+ final RubyArray memoArray = RubyArray.newArray(context.getRuntime(), 2);
+ memoArray.add(result);
+ try {
+ match.visitAll(new RubyHash.Visitor() {
+ @Override
+ public void visit(IRubyObject pattern, IRubyObject klass) {
+ if (pattern.callMethod(context, "===", memoArray.entry(0)).isTrue()) {
+ memoArray.add(klass);
+ throw JumpException.SPECIAL_JUMP;
+ }
+ }
+ });
+ } catch (JumpException e) { }
+ IRubyObject matched = memoArray.entry(1);
+ if (!matched.isNil()) {
+ RubyClass klass = (RubyClass) matched;
+ if (klass.respondsTo("json_creatable?") &&
+ klass.callMethod(context, "json_creatable?").isTrue()) {
+ result = klass.callMethod(context, "json_create", result);
+ }
+ }
+ }
+ }
if (cs >= JSON_string_first_final && result != null) {
return new ParserResult(result, p + 1);
@@ -1389,7 +1417,7 @@ case 5:
}
-// line 1393 "Parser.java"
+// line 1421 "Parser.java"
private static byte[] init__JSON_array_actions_0()
{
return new byte [] {
@@ -1502,7 +1530,7 @@ static final int JSON_array_error = 0;
static final int JSON_array_en_main = 1;
-// line 594 "Parser.rl"
+// line 622 "Parser.rl"
ParserResult parseArray(int p, int pe) {
@@ -1520,14 +1548,14 @@ static final int JSON_array_en_main = 1;
IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
-// line 1524 "Parser.java"
+// line 1552 "Parser.java"
{
cs = JSON_array_start;
}
-// line 611 "Parser.rl"
+// line 639 "Parser.rl"
-// line 1531 "Parser.java"
+// line 1559 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1608,7 +1636,7 @@ case 1:
switch ( _JSON_array_actions[_acts++] )
{
case 0:
-// line 567 "Parser.rl"
+// line 595 "Parser.rl"
{
ParserResult res = parseValue(p, pe);
if (res == null) {
@@ -1621,13 +1649,13 @@ case 1:
}
break;
case 1:
-// line 578 "Parser.rl"
+// line 606 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1631 "Parser.java"
+// line 1659 "Parser.java"
}
}
}
@@ -1647,7 +1675,7 @@ case 5:
break; }
}
-// line 612 "Parser.rl"
+// line 640 "Parser.rl"
if (cs >= JSON_array_first_final) {
return new ParserResult(result, p + 1);
@@ -1657,7 +1685,7 @@ case 5:
}
-// line 1661 "Parser.java"
+// line 1689 "Parser.java"
private static byte[] init__JSON_object_actions_0()
{
return new byte [] {
@@ -1780,7 +1808,7 @@ static final int JSON_object_error = 0;
static final int JSON_object_en_main = 1;
-// line 668 "Parser.rl"
+// line 696 "Parser.rl"
ParserResult parseObject(int p, int pe) {
@@ -1799,14 +1827,14 @@ static final int JSON_object_en_main = 1;
IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
-// line 1803 "Parser.java"
+// line 1831 "Parser.java"
{
cs = JSON_object_start;
}
-// line 686 "Parser.rl"
+// line 714 "Parser.rl"
-// line 1810 "Parser.java"
+// line 1838 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1887,7 +1915,7 @@ case 1:
switch ( _JSON_object_actions[_acts++] )
{
case 0:
-// line 626 "Parser.rl"
+// line 654 "Parser.rl"
{
ParserResult res = parseValue(p, pe);
if (res == null) {
@@ -1900,7 +1928,7 @@ case 1:
}
break;
case 1:
-// line 637 "Parser.rl"
+// line 665 "Parser.rl"
{
ParserResult res = parseString(p, pe);
if (res == null) {
@@ -1920,13 +1948,13 @@ case 1:
}
break;
case 2:
-// line 655 "Parser.rl"
+// line 683 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1930 "Parser.java"
+// line 1958 "Parser.java"
}
}
}
@@ -1946,7 +1974,7 @@ case 5:
break; }
}
-// line 687 "Parser.rl"
+// line 715 "Parser.rl"
if (cs < JSON_object_first_final) {
return null;
@@ -1955,7 +1983,7 @@ case 5:
IRubyObject returnedResult = result;
// attempt to de-serialize object
- if (parser.createId != null) {
+ if (parser.createAdditions) {
IRubyObject vKlassName = result.op_aref(context, parser.createId);
if (!vKlassName.isNil()) {
// might throw ArgumentError, we let it propagate
@@ -1972,7 +2000,7 @@ case 5:
}
-// line 1976 "Parser.java"
+// line 2004 "Parser.java"
private static byte[] init__JSON_actions_0()
{
return new byte [] {
@@ -2076,7 +2104,7 @@ static final int JSON_error = 0;
static final int JSON_en_main = 1;
-// line 745 "Parser.rl"
+// line 773 "Parser.rl"
public IRubyObject parse() {
@@ -2085,16 +2113,16 @@ static final int JSON_en_main = 1;
IRubyObject result = null;
-// line 2089 "Parser.java"
+// line 2117 "Parser.java"
{
cs = JSON_start;
}
-// line 753 "Parser.rl"
+// line 781 "Parser.rl"
p = byteList.begin();
pe = p + byteList.length();
-// line 2098 "Parser.java"
+// line 2126 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -2175,7 +2203,7 @@ case 1:
switch ( _JSON_actions[_acts++] )
{
case 0:
-// line 717 "Parser.rl"
+// line 745 "Parser.rl"
{
currentNesting = 1;
ParserResult res = parseObject(p, pe);
@@ -2189,7 +2217,7 @@ case 1:
}
break;
case 1:
-// line 729 "Parser.rl"
+// line 757 "Parser.rl"
{
currentNesting = 1;
ParserResult res = parseArray(p, pe);
@@ -2202,7 +2230,7 @@ case 1:
}
}
break;
-// line 2206 "Parser.java"
+// line 2234 "Parser.java"
}
}
}
@@ -2222,7 +2250,7 @@ case 5:
break; }
}
-// line 756 "Parser.rl"
+// line 784 "Parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl
index 00badc8..642e56c 100644
--- a/java/src/json/ext/Parser.rl
+++ b/java/src/json/ext/Parser.rl
@@ -18,6 +18,7 @@ import org.jruby.RubyNumeric;
import org.jruby.RubyObject;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.JumpException;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
@@ -44,11 +45,13 @@ public class Parser extends RubyObject {
private final RuntimeInfo info;
private RubyString vSource;
private RubyString createId;
+ private boolean createAdditions;
private int maxNesting;
private boolean allowNaN;
private boolean symbolizeNames;
private RubyClass objectClass;
private RubyClass arrayClass;
+ private RubyHash match;
private static final int DEFAULT_MAX_NESTING = 19;
@@ -139,20 +142,18 @@ public class Parser extends RubyObject {
@JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
+ Ruby runtime = context.getRuntime();
RubyString source = convertEncoding(context, args[0].convertToString());
- OptionsReader opts =
- new OptionsReader(context, args.length > 1 ? args[1] : null);
-
- this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
- this.allowNaN = opts.getBool("allow_nan", false);
- this.symbolizeNames = opts.getBool("symbolize_names", false);
- this.createId =
- opts.getBool("create_additions", true) ? getCreateId(context)
- : null;
- this.objectClass = opts.getClass("object_class", runtime.getHash());
- this.arrayClass = opts.getClass("array_class", runtime.getArray());
+ OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null);
+ this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
+ this.allowNaN = opts.getBool("allow_nan", false);
+ this.symbolizeNames = opts.getBool("symbolize_names", false);
+ this.createId = opts.getString("create_id", getCreateId(context));
+ this.createAdditions = opts.getBool("create_additions", true);
+ this.objectClass = opts.getClass("object_class", runtime.getHash());
+ this.arrayClass = opts.getClass("array_class", runtime.getArray());
+ this.match = opts.getHash("match");
this.vSource = source;
return this;
@@ -545,12 +546,39 @@ public class Parser extends RubyObject {
ParserResult parseString(int p, int pe) {
int cs = EVIL;
- RubyString result = null;
+ IRubyObject result = null;
%% write init;
int memo = p;
%% write exec;
+ if (parser.createAdditions) {
+ RubyHash match = parser.match;
+ if (match != null) {
+ final RubyArray memoArray = RubyArray.newArray(context.getRuntime(), 2);
+ memoArray.add(result);
+ try {
+ match.visitAll(new RubyHash.Visitor() {
+ @Override
+ public void visit(IRubyObject pattern, IRubyObject klass) {
+ if (pattern.callMethod(context, "===", memoArray.entry(0)).isTrue()) {
+ memoArray.add(klass);
+ throw JumpException.SPECIAL_JUMP;
+ }
+ }
+ });
+ } catch (JumpException e) { }
+ IRubyObject matched = memoArray.entry(1);
+ if (!matched.isNil()) {
+ RubyClass klass = (RubyClass) matched;
+ if (klass.respondsTo("json_creatable?") &&
+ klass.callMethod(context, "json_creatable?").isTrue()) {
+ result = klass.callMethod(context, "json_create", result);
+ }
+ }
+ }
+ }
+
if (cs >= JSON_string_first_final && result != null) {
return new ParserResult(result, p + 1);
} else {
@@ -692,7 +720,7 @@ public class Parser extends RubyObject {
IRubyObject returnedResult = result;
// attempt to de-serialize object
- if (parser.createId != null) {
+ if (parser.createAdditions) {
IRubyObject vKlassName = result.op_aref(context, parser.createId);
if (!vKlassName.isNil()) {
// might throw ArgumentError, we let it propagate
diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb
index 82a21e8..6a0192c 100644
--- a/lib/json/pure/parser.rb
+++ b/lib/json/pure/parser.rb
@@ -113,13 +113,13 @@ module JSON
else
@max_nesting = 0
end
- @allow_nan = !!opts[:allow_nan]
- @symbolize_names = !!opts[:symbolize_names]
- ca = true
- ca = opts[:create_additions] if opts.key?(:create_additions)
- @create_id = ca ? JSON.create_id : nil
- @object_class = opts[:object_class] || Hash
- @array_class = opts[:array_class] || Array
+ @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
+ @json_match = opts[:match] # @match is an ivar in rbx's strscan
end
alias source string
@@ -189,6 +189,12 @@ module JSON
if string.respond_to?(:force_encoding)
string.force_encoding(::Encoding::UTF_8)
end
+ if @create_additions and @json_match
+ for (regexp, klass) in @json_match
+ klass.json_creatable? or next
+ string =~ regexp and return klass.json_create(string)
+ end
+ end
string
else
UNPARSED
@@ -295,7 +301,7 @@ module JSON
if delim
raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
end
- if @create_id and klassname = result[@create_id]
+ 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)
diff --git a/tests/setup_variant.rb b/tests/setup_variant.rb
new file mode 100644
index 0000000..2dab184
--- /dev/null
+++ b/tests/setup_variant.rb
@@ -0,0 +1,11 @@
+case ENV['JSON']
+when 'pure'
+ $:.unshift 'lib'
+ require 'json/pure'
+when 'ext'
+ $:.unshift 'ext', 'lib'
+ require 'json/ext'
+else
+ $:.unshift 'ext', 'lib'
+ require 'json'
+end
diff --git a/tests/test_json.rb b/tests/test_json.rb
index 00e52f5..2fc3c09 100755
--- a/tests/test_json.rb
+++ b/tests/test_json.rb
@@ -2,11 +2,7 @@
# -*- coding: utf-8 -*-
require 'test/unit'
-case ENV['JSON']
-when 'pure' then require 'json/pure'
-when 'ext' then require 'json/ext'
-else require 'json'
-end
+require File.join(File.dirname(__FILE__), 'setup_variant')
require 'stringio'
unless Array.method_defined?(:permutation)
diff --git a/tests/test_json_addition.rb b/tests/test_json_addition.rb
index 34f0a71..c8bfb41 100755
--- a/tests/test_json_addition.rb
+++ b/tests/test_json_addition.rb
@@ -2,11 +2,7 @@
# -*- coding:utf-8 -*-
require 'test/unit'
-case ENV['JSON']
-when 'pure' then require 'json/pure'
-when 'ext' then require 'json/ext'
-else require 'json'
-end
+require File.join(File.dirname(__FILE__), 'setup_variant')
load 'json/add/core.rb'
require 'date'
@@ -36,6 +32,15 @@ class TC_JSONAddition < Test::Unit::TestCase
end
end
+ class A2 < A
+ def to_json(*args)
+ {
+ 'json_class' => self.class.name,
+ 'args' => [ @a ],
+ }.to_json(*args)
+ end
+ end
+
class B
def self.json_creatable?
false
diff --git a/tests/test_json_encoding.rb b/tests/test_json_encoding.rb
index cdeca58..7af5e63 100644
--- a/tests/test_json_encoding.rb
+++ b/tests/test_json_encoding.rb
@@ -2,11 +2,7 @@
# -*- coding: utf-8 -*-
require 'test/unit'
-case ENV['JSON']
-when 'pure' then require 'json/pure'
-when 'ext' then require 'json/ext'
-else require 'json'
-end
+require File.join(File.dirname(__FILE__), 'setup_variant')
class TC_JSONEncoding < Test::Unit::TestCase
include JSON
diff --git a/tests/test_json_fixtures.rb b/tests/test_json_fixtures.rb
index 378667f..e9df8f5 100755
--- a/tests/test_json_fixtures.rb
+++ b/tests/test_json_fixtures.rb
@@ -2,11 +2,7 @@
# -*- coding: utf-8 -*-
require 'test/unit'
-case ENV['JSON']
-when 'pure' then require 'json/pure'
-when 'ext' then require 'json/ext'
-else require 'json'
-end
+require File.join(File.dirname(__FILE__), 'setup_variant')
class TC_JSONFixtures < Test::Unit::TestCase
def setup
diff --git a/tests/test_json_generate.rb b/tests/test_json_generate.rb
index 5380a06..e6219df 100755
--- a/tests/test_json_generate.rb
+++ b/tests/test_json_generate.rb
@@ -2,11 +2,7 @@
# -*- coding: utf-8 -*-
require 'test/unit'
-case ENV['JSON']
-when 'pure' then require 'json/pure'
-when 'ext' then require 'json/ext'
-else require 'json'
-end
+require File.join(File.dirname(__FILE__), 'setup_variant')
class TC_JSONGenerate < Test::Unit::TestCase
include JSON
diff --git a/tests/test_json_string_matching.rb b/tests/test_json_string_matching.rb
new file mode 100644
index 0000000..6bd74e3
--- /dev/null
+++ b/tests/test_json_string_matching.rb
@@ -0,0 +1,40 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+require 'stringio'
+require 'time'
+
+class TestJsonStringMatching < Test::Unit::TestCase
+ include JSON
+
+ class TestTime < ::Time
+ def self.json_create(string)
+ Time.parse(string)
+ end
+
+ def to_json(*)
+ %{"#{strftime('%FT%T%z')}"}
+ end
+
+ def ==(other)
+ to_i == other.to_i
+ end
+ end
+
+ def test_match_date
+ t = TestTime.new
+ t_json = [ t ].to_json
+ assert_equal [ t ],
+ JSON.parse(t_json,
+ :match => { /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\Z/ => TestTime })
+ assert_equal [ t.strftime('%FT%T%z') ],
+ JSON.parse(t_json,
+ :match => { /\A\d{3}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\Z/ => TestTime })
+ assert_equal [ t.strftime('%FT%T%z') ],
+ JSON.parse(t_json,
+ :match => { /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\Z/ => TestTime },
+ :create_additions => false)
+ end
+end
diff --git a/tests/test_json_unicode.rb b/tests/test_json_unicode.rb
index 505f5d5..ace56ca 100755
--- a/tests/test_json_unicode.rb
+++ b/tests/test_json_unicode.rb
@@ -2,11 +2,7 @@
# -*- coding: utf-8 -*-
require 'test/unit'
-case ENV['JSON']
-when 'pure' then require 'json/pure'
-when 'ext' then require 'json/ext'
-else require 'json'
-end
+require File.join(File.dirname(__FILE__), 'setup_variant')
class TC_JSONUnicode < Test::Unit::TestCase
include JSON