summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Frank <flori@ping.de>2010-10-05 11:00:09 +0200
committerFlorian Frank <flori@ping.de>2011-01-02 21:13:46 +0100
commit8714f0a88ccee67ea7ca1a9d5a40d9cf1c6dd5fb (patch)
tree9f1d194336debc54896944b80bbf3bbea44eb4dd
parent02b20b1ec02513b7452e8585c6d45cbe565418d2 (diff)
downloadjson-8714f0a88ccee67ea7ca1a9d5a40d9cf1c6dd5fb.tar.gz
Added matching in pure/ext parser
-rw-r--r--ext/json/ext/parser/parser.c103
-rw-r--r--ext/json/ext/parser/parser.h3
-rw-r--r--ext/json/ext/parser/parser.rl75
-rw-r--r--lib/json/pure/parser.rb22
-rwxr-xr-xtests/test_json_addition.rb9
-rw-r--r--tests/test_json_string_matching.rb44
6 files changed, 202 insertions, 54 deletions
diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c
index a2f4601..43a1426 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"
@@ -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, memo;
*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,21 @@ case 7:
_out: {}
}
-#line 481 "parser.rl"
+#line 494 "parser.rl"
+
+ if (json->create_additions) {
+ match = json->match;
+ memo = rb_ary_new2(2);
+ rb_ary_push(memo, *result);
+ if (RTEST(match)) {
+ VALUE klass;
+ 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 +1535,7 @@ case 7:
-#line 1512 "parser.c"
+#line 1539 "parser.c"
static const int JSON_start = 1;
static const int JSON_first_final = 10;
static const int JSON_error = 0;
@@ -1516,7 +1543,7 @@ static const int JSON_error = 0;
static const int JSON_en_main = 1;
-#line 518 "parser.rl"
+#line 545 "parser.rl"
/*
@@ -1649,14 +1676,17 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
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;
- }
- } else {
- json->create_id = rb_funcall(mJSON, i_create_id, 0);
- }
+ json->create_additions = RTEST(create_additions);
+ } else {
+ json->create_additions = 1;
+ }
+ tmp = ID2SYM(i_create_id);
+ if (option_given_p(opts, tmp)) {
+ VALUE create_id = rb_hash_aref(opts, tmp);
+ json->create_id = create_id;
+ } else {
+ json->create_id = rb_funcall(mJSON, i_create_id, 0);
+ }
tmp = ID2SYM(i_object_class);
if (option_given_p(opts, tmp)) {
json->object_class = rb_hash_aref(opts, tmp);
@@ -1669,6 +1699,17 @@ 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);
+ if (RTEST(match)) {
+ json->match = match;
+ } else {
+ json->match = Qnil;
+ }
+ } else {
+ json->match = Qnil;
+ }
}
} else {
json->max_nesting = 19;
@@ -1698,16 +1739,16 @@ static VALUE cParser_parse(VALUE self)
GET_PARSER;
-#line 1702 "parser.c"
+#line 1743 "parser.c"
{
cs = JSON_start;
}
-#line 699 "parser.rl"
+#line 740 "parser.rl"
p = json->source;
pe = p + json->len;
-#line 1711 "parser.c"
+#line 1752 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1763,7 +1804,7 @@ case 5:
goto st1;
goto st5;
tr3:
-#line 507 "parser.rl"
+#line 534 "parser.rl"
{
char *np;
json->current_nesting = 1;
@@ -1772,7 +1813,7 @@ tr3:
}
goto st10;
tr4:
-#line 500 "parser.rl"
+#line 527 "parser.rl"
{
char *np;
json->current_nesting = 1;
@@ -1784,7 +1825,7 @@ st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
-#line 1788 "parser.c"
+#line 1829 "parser.c"
switch( (*p) ) {
case 13: goto st10;
case 32: goto st10;
@@ -1841,7 +1882,7 @@ case 9:
_out: {}
}
-#line 702 "parser.rl"
+#line 743 "parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
@@ -1864,6 +1905,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 +1958,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..ab02b51 100644
--- a/ext/json/ext/parser/parser.h
+++ b/ext/json/ext/parser/parser.h
@@ -13,6 +13,7 @@
#else
#define FORCE_UTF8(obj)
#endif
+#include "ruby/st.h"
#define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key))
@@ -41,6 +42,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..f59038b 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;
@@ -457,28 +457,55 @@ 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, memo;
*result = rb_str_buf_new(0);
%% write init;
json->memo = p;
%% write exec;
+ if (json->create_additions) {
+ match = json->match;
+ memo = rb_ary_new2(2);
+ rb_ary_push(memo, *result);
+ if (RTEST(match)) {
+ VALUE klass;
+ 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);
}
@@ -647,14 +674,17 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
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;
- }
- } else {
- json->create_id = rb_funcall(mJSON, i_create_id, 0);
- }
+ json->create_additions = RTEST(create_additions);
+ } else {
+ json->create_additions = 1;
+ }
+ tmp = ID2SYM(i_create_id);
+ if (option_given_p(opts, tmp)) {
+ VALUE create_id = rb_hash_aref(opts, tmp);
+ json->create_id = create_id;
+ } else {
+ json->create_id = rb_funcall(mJSON, i_create_id, 0);
+ }
tmp = ID2SYM(i_object_class);
if (option_given_p(opts, tmp)) {
json->object_class = rb_hash_aref(opts, tmp);
@@ -667,6 +697,17 @@ 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);
+ if (RTEST(match)) {
+ json->match = match;
+ } else {
+ json->match = Qnil;
+ }
+ } else {
+ json->match = Qnil;
+ }
}
} else {
json->max_nesting = 19;
@@ -721,6 +762,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 +815,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/lib/json/pure/parser.rb b/lib/json/pure/parser.rb
index 82a21e8..cb531f4 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
+ @match = opts[:match]
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 @match
+ for (regexp, klass) in @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/test_json_addition.rb b/tests/test_json_addition.rb
index 34f0a71..edf8699 100755
--- a/tests/test_json_addition.rb
+++ b/tests/test_json_addition.rb
@@ -36,6 +36,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_string_matching.rb b/tests/test_json_string_matching.rb
new file mode 100644
index 0000000..0ea12ed
--- /dev/null
+++ b/tests/test_json_string_matching.rb
@@ -0,0 +1,44 @@
+#!/usr/bin/env ruby
+# -*- 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 '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