json_pure 2.0.4 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +9 -5
  4. data/CHANGES.md +39 -0
  5. data/Gemfile +1 -3
  6. data/LICENSE +56 -0
  7. data/README.md +54 -21
  8. data/Rakefile +19 -93
  9. data/VERSION +1 -1
  10. data/ext/json/ext/generator/generator.c +214 -45
  11. data/ext/json/ext/generator/generator.h +5 -2
  12. data/ext/json/ext/parser/extconf.rb +25 -0
  13. data/ext/json/ext/parser/parser.c +155 -83
  14. data/ext/json/ext/parser/parser.h +2 -0
  15. data/ext/json/ext/parser/parser.rl +79 -7
  16. data/ext/json/extconf.rb +1 -0
  17. data/java/src/json/ext/Generator.java +28 -24
  18. data/java/src/json/ext/GeneratorState.java +30 -0
  19. data/java/src/json/ext/Parser.java +109 -82
  20. data/java/src/json/ext/Parser.rl +39 -12
  21. data/java/src/json/ext/StringEncoder.java +8 -2
  22. data/json-java.gemspec +22 -22
  23. data/json.gemspec +0 -0
  24. data/json_pure.gemspec +9 -14
  25. data/lib/json.rb +549 -29
  26. data/lib/json/add/bigdecimal.rb +2 -2
  27. data/lib/json/add/complex.rb +2 -3
  28. data/lib/json/add/ostruct.rb +1 -1
  29. data/lib/json/add/rational.rb +2 -3
  30. data/lib/json/add/regexp.rb +2 -2
  31. data/lib/json/add/set.rb +29 -0
  32. data/lib/json/common.rb +341 -115
  33. data/lib/json/pure/generator.rb +31 -10
  34. data/lib/json/pure/parser.rb +35 -5
  35. data/lib/json/version.rb +1 -1
  36. data/tests/json_addition_test.rb +6 -0
  37. data/tests/json_common_interface_test.rb +47 -4
  38. data/tests/json_encoding_test.rb +2 -2
  39. data/tests/json_fixtures_test.rb +9 -1
  40. data/tests/json_generator_test.rb +55 -0
  41. data/tests/json_parser_test.rb +43 -12
  42. data/tests/test_helper.rb +3 -7
  43. metadata +17 -13
  44. data/data/example.json +0 -1
  45. data/data/index.html +0 -38
  46. data/data/prototype.js +0 -4184
@@ -37,8 +37,10 @@ typedef struct JSON_ParserStruct {
37
37
  int allow_nan;
38
38
  int parsing_name;
39
39
  int symbolize_names;
40
+ int freeze;
40
41
  VALUE object_class;
41
42
  VALUE array_class;
43
+ VALUE decimal_class;
42
44
  int create_additions;
43
45
  VALUE match_string;
44
46
  FBuffer *fbuffer;
@@ -25,7 +25,7 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)
25
25
 
26
26
  /* unicode */
27
27
 
28
- static const char digit_values[256] = {
28
+ static const signed char digit_values[256] = {
29
29
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
30
30
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
31
31
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
@@ -44,7 +44,7 @@ static const char digit_values[256] = {
44
44
 
45
45
  static UTF32 unescape_unicode(const unsigned char *p)
46
46
  {
47
- char b;
47
+ signed char b;
48
48
  UTF32 result = 0;
49
49
  b = digit_values[p[0]];
50
50
  if (b < 0) return UNI_REPLACEMENT_CHAR;
@@ -89,11 +89,13 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
89
89
 
90
90
  static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
91
91
  static VALUE CNaN, CInfinity, CMinusInfinity;
92
+ static VALUE cBigDecimal = Qundef;
92
93
 
93
94
  static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
94
95
  i_chr, i_max_nesting, i_allow_nan, i_symbolize_names,
95
- i_object_class, i_array_class, i_key_p, i_deep_const_get, i_match,
96
- i_match_string, i_aset, i_aref, i_leftshift;
96
+ i_object_class, i_array_class, i_decimal_class, i_key_p,
97
+ i_deep_const_get, i_match, i_match_string, i_aset, i_aref,
98
+ i_leftshift, i_new, i_BigDecimal, i_freeze, i_uminus;
97
99
 
98
100
  %%{
99
101
  machine JSON_common;
@@ -136,6 +138,7 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
136
138
  fhold; fbreak;
137
139
  } else {
138
140
  if (NIL_P(json->object_class)) {
141
+ OBJ_FREEZE(last_name);
139
142
  rb_hash_aset(*result, last_name, v);
140
143
  } else {
141
144
  rb_funcall(*result, i_aset, 2, last_name, v);
@@ -287,6 +290,10 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
287
290
  %% write init;
288
291
  %% write exec;
289
292
 
293
+ if (json->freeze) {
294
+ OBJ_FREEZE(*result);
295
+ }
296
+
290
297
  if (cs >= JSON_value_first_final) {
291
298
  return p;
292
299
  } else {
@@ -338,6 +345,19 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
338
345
  ) (^[0-9Ee.\-]? @exit );
339
346
  }%%
340
347
 
348
+ static int is_bigdecimal_class(VALUE obj)
349
+ {
350
+ if (cBigDecimal == Qundef) {
351
+ if (rb_const_defined(rb_cObject, i_BigDecimal)) {
352
+ cBigDecimal = rb_const_get_at(rb_cObject, i_BigDecimal);
353
+ }
354
+ else {
355
+ return 0;
356
+ }
357
+ }
358
+ return obj == cBigDecimal;
359
+ }
360
+
341
361
  static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
342
362
  {
343
363
  int cs = EVIL;
@@ -351,7 +371,17 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
351
371
  fbuffer_clear(json->fbuffer);
352
372
  fbuffer_append(json->fbuffer, json->memo, len);
353
373
  fbuffer_append_char(json->fbuffer, '\0');
354
- *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
374
+ if (NIL_P(json->decimal_class)) {
375
+ *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
376
+ } else {
377
+ VALUE text;
378
+ text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
379
+ if (is_bigdecimal_class(json->decimal_class)) {
380
+ *result = rb_funcall(Qnil, i_BigDecimal, 1, text);
381
+ } else {
382
+ *result = rb_funcall(json->decimal_class, i_new, 1, text);
383
+ }
384
+ }
355
385
  return p + 1;
356
386
  } else {
357
387
  return NULL;
@@ -546,8 +576,23 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
546
576
 
547
577
  if (json->symbolize_names && json->parsing_name) {
548
578
  *result = rb_str_intern(*result);
549
- } else {
579
+ } else if (RB_TYPE_P(*result, T_STRING)) {
580
+ # if STR_UMINUS_DEDUPE_FROZEN
581
+ if (json->freeze) {
582
+ // Starting from MRI 2.8 it is preferable to freeze the string
583
+ // before deduplication so that it can be interned directly
584
+ // otherwise it would be duplicated first which is wasteful.
585
+ *result = rb_funcall(rb_str_freeze(*result), i_uminus, 0);
586
+ }
587
+ # elif STR_UMINUS_DEDUPE
588
+ if (json->freeze) {
589
+ // MRI 2.5 and older do not deduplicate strings that are already
590
+ // frozen.
591
+ *result = rb_funcall(*result, i_uminus, 0);
592
+ }
593
+ # else
550
594
  rb_str_resize(*result, RSTRING_LEN(*result));
595
+ # endif
551
596
  }
552
597
  if (cs >= JSON_string_first_final) {
553
598
  return p + 1;
@@ -655,6 +700,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
655
700
  } else {
656
701
  json->symbolize_names = 0;
657
702
  }
703
+ tmp = ID2SYM(i_freeze);
704
+ if (option_given_p(opts, tmp)) {
705
+ json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
706
+ } else {
707
+ json->freeze = 0;
708
+ }
658
709
  tmp = ID2SYM(i_create_additions);
659
710
  if (option_given_p(opts, tmp)) {
660
711
  json->create_additions = RTEST(rb_hash_aref(opts, tmp));
@@ -684,6 +735,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
684
735
  } else {
685
736
  json->array_class = Qnil;
686
737
  }
738
+ tmp = ID2SYM(i_decimal_class);
739
+ if (option_given_p(opts, tmp)) {
740
+ json->decimal_class = rb_hash_aref(opts, tmp);
741
+ } else {
742
+ json->decimal_class = Qnil;
743
+ }
687
744
  tmp = ID2SYM(i_match_string);
688
745
  if (option_given_p(opts, tmp)) {
689
746
  VALUE match_string = rb_hash_aref(opts, tmp);
@@ -697,10 +754,11 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
697
754
  } else {
698
755
  json->max_nesting = 100;
699
756
  json->allow_nan = 0;
700
- json->create_additions = 1;
757
+ json->create_additions = 0;
701
758
  json->create_id = rb_funcall(mJSON, i_create_id, 0);
702
759
  json->object_class = Qnil;
703
760
  json->array_class = Qnil;
761
+ json->decimal_class = Qnil;
704
762
  }
705
763
  source = convert_encoding(StringValue(source));
706
764
  StringValue(source);
@@ -760,6 +818,7 @@ static void JSON_mark(void *ptr)
760
818
  rb_gc_mark_maybe(json->create_id);
761
819
  rb_gc_mark_maybe(json->object_class);
762
820
  rb_gc_mark_maybe(json->array_class);
821
+ rb_gc_mark_maybe(json->decimal_class);
763
822
  rb_gc_mark_maybe(json->match_string);
764
823
  }
765
824
 
@@ -809,20 +868,28 @@ static VALUE cParser_source(VALUE self)
809
868
 
810
869
  void Init_parser(void)
811
870
  {
871
+ #undef rb_intern
812
872
  rb_require("json/common");
813
873
  mJSON = rb_define_module("JSON");
814
874
  mExt = rb_define_module_under(mJSON, "Ext");
815
875
  cParser = rb_define_class_under(mExt, "Parser", rb_cObject);
816
876
  eParserError = rb_path2class("JSON::ParserError");
817
877
  eNestingError = rb_path2class("JSON::NestingError");
878
+ rb_gc_register_mark_object(eParserError);
879
+ rb_gc_register_mark_object(eNestingError);
818
880
  rb_define_alloc_func(cParser, cJSON_parser_s_allocate);
819
881
  rb_define_method(cParser, "initialize", cParser_initialize, -1);
820
882
  rb_define_method(cParser, "parse", cParser_parse, 0);
821
883
  rb_define_method(cParser, "source", cParser_source, 0);
822
884
 
823
885
  CNaN = rb_const_get(mJSON, rb_intern("NaN"));
886
+ rb_gc_register_mark_object(CNaN);
887
+
824
888
  CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
889
+ rb_gc_register_mark_object(CInfinity);
890
+
825
891
  CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
892
+ rb_gc_register_mark_object(CMinusInfinity);
826
893
 
827
894
  i_json_creatable_p = rb_intern("json_creatable?");
828
895
  i_json_create = rb_intern("json_create");
@@ -834,6 +901,7 @@ void Init_parser(void)
834
901
  i_symbolize_names = rb_intern("symbolize_names");
835
902
  i_object_class = rb_intern("object_class");
836
903
  i_array_class = rb_intern("array_class");
904
+ i_decimal_class = rb_intern("decimal_class");
837
905
  i_match = rb_intern("match");
838
906
  i_match_string = rb_intern("match_string");
839
907
  i_key_p = rb_intern("key?");
@@ -841,6 +909,10 @@ void Init_parser(void)
841
909
  i_aset = rb_intern("[]=");
842
910
  i_aref = rb_intern("[]");
843
911
  i_leftshift = rb_intern("<<");
912
+ i_new = rb_intern("new");
913
+ i_BigDecimal = rb_intern("BigDecimal");
914
+ i_freeze = rb_intern("freeze");
915
+ i_uminus = rb_intern("-@");
844
916
  }
845
917
 
846
918
  /*
@@ -1,2 +1,3 @@
1
1
  require 'mkmf'
2
+
2
3
  create_makefile('json')
@@ -7,13 +7,12 @@ package json.ext;
7
7
 
8
8
  import org.jruby.Ruby;
9
9
  import org.jruby.RubyArray;
10
+ import org.jruby.RubyBasicObject;
10
11
  import org.jruby.RubyBignum;
11
12
  import org.jruby.RubyBoolean;
12
- import org.jruby.RubyClass;
13
13
  import org.jruby.RubyFixnum;
14
14
  import org.jruby.RubyFloat;
15
15
  import org.jruby.RubyHash;
16
- import org.jruby.RubyNumeric;
17
16
  import org.jruby.RubyString;
18
17
  import org.jruby.runtime.ThreadContext;
19
18
  import org.jruby.runtime.builtin.IRubyObject;
@@ -30,8 +29,7 @@ public final class Generator {
30
29
  static <T extends IRubyObject> RubyString
31
30
  generateJson(ThreadContext context, T object,
32
31
  Handler<? super T> handler, IRubyObject[] args) {
33
- Session session = new Session(context, args.length > 0 ? args[0]
34
- : null);
32
+ Session session = new Session(context, args.length > 0 ? args[0] : null);
35
33
  return session.infect(handler.generateNew(session, object));
36
34
  }
37
35
 
@@ -41,7 +39,7 @@ public final class Generator {
41
39
  */
42
40
  static <T extends IRubyObject> RubyString
43
41
  generateJson(ThreadContext context, T object, IRubyObject[] args) {
44
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
42
+ Handler<? super T> handler = getHandlerFor(context.runtime, object);
45
43
  return generateJson(context, object, handler, args);
46
44
  }
47
45
 
@@ -53,7 +51,7 @@ public final class Generator {
53
51
  generateJson(ThreadContext context, T object,
54
52
  GeneratorState config) {
55
53
  Session session = new Session(context, config);
56
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
54
+ Handler<? super T> handler = getHandlerFor(context.runtime, object);
57
55
  return handler.generateNew(session, object);
58
56
  }
59
57
 
@@ -63,18 +61,24 @@ public final class Generator {
63
61
  // Java's generics can't handle this satisfactorily, so I'll just leave
64
62
  // the best I could get and ignore the warnings
65
63
  @SuppressWarnings("unchecked")
66
- private static <T extends IRubyObject>
67
- Handler<? super T> getHandlerFor(Ruby runtime, T object) {
68
- RubyClass metaClass = object.getMetaClass();
69
- if (metaClass == runtime.getString()) return (Handler)STRING_HANDLER;
70
- if (metaClass == runtime.getFixnum()) return (Handler)FIXNUM_HANDLER;
71
- if (metaClass == runtime.getHash()) return (Handler)HASH_HANDLER;
72
- if (metaClass == runtime.getArray()) return (Handler)ARRAY_HANDLER;
73
- if (object.isNil()) return (Handler)NIL_HANDLER;
74
- if (object == runtime.getTrue()) return (Handler)TRUE_HANDLER;
75
- if (object == runtime.getFalse()) return (Handler)FALSE_HANDLER;
76
- if (metaClass == runtime.getFloat()) return (Handler)FLOAT_HANDLER;
77
- if (metaClass == runtime.getBignum()) return (Handler)BIGNUM_HANDLER;
64
+ private static <T extends IRubyObject> Handler<? super T> getHandlerFor(Ruby runtime, T object) {
65
+ switch (((RubyBasicObject) object).getNativeClassIndex()) {
66
+ case NIL : return (Handler) NIL_HANDLER;
67
+ case TRUE : return (Handler) TRUE_HANDLER;
68
+ case FALSE : return (Handler) FALSE_HANDLER;
69
+ case FLOAT : return (Handler) FLOAT_HANDLER;
70
+ case FIXNUM : return (Handler) FIXNUM_HANDLER;
71
+ case BIGNUM : return (Handler) BIGNUM_HANDLER;
72
+ case STRING :
73
+ if (((RubyBasicObject) object).getMetaClass() != runtime.getString()) break;
74
+ return (Handler) STRING_HANDLER;
75
+ case ARRAY :
76
+ if (((RubyBasicObject) object).getMetaClass() != runtime.getArray()) break;
77
+ return (Handler) ARRAY_HANDLER;
78
+ case HASH :
79
+ if (((RubyBasicObject) object).getMetaClass() != runtime.getHash()) break;
80
+ return (Handler) HASH_HANDLER;
81
+ }
78
82
  return GENERIC_HANDLER;
79
83
  }
80
84
 
@@ -135,7 +139,7 @@ public final class Generator {
135
139
 
136
140
  public StringEncoder getStringEncoder() {
137
141
  if (stringEncoder == null) {
138
- stringEncoder = new StringEncoder(context, getState().asciiOnly());
142
+ stringEncoder = new StringEncoder(context, getState().asciiOnly(), getState().escapeSlash());
139
143
  }
140
144
  return stringEncoder;
141
145
  }
@@ -330,13 +334,13 @@ public final class Generator {
330
334
 
331
335
  buffer.append((byte)'{');
332
336
  buffer.append(objectNl);
333
- object.visitAll(new RubyHash.Visitor() {
334
- private boolean firstPair = true;
335
337
 
338
+ final boolean[] firstPair = new boolean[]{true};
339
+ object.visitAll(new RubyHash.Visitor() {
336
340
  @Override
337
341
  public void visit(IRubyObject key, IRubyObject value) {
338
- if (firstPair) {
339
- firstPair = false;
342
+ if (firstPair[0]) {
343
+ firstPair[0] = false;
340
344
  } else {
341
345
  buffer.append((byte)',');
342
346
  buffer.append(objectNl);
@@ -356,7 +360,7 @@ public final class Generator {
356
360
  }
357
361
  });
358
362
  state.decreaseDepth();
359
- if (objectNl.length() != 0) {
363
+ if (!firstPair[0] && objectNl.length() != 0) {
360
364
  buffer.append(objectNl);
361
365
  buffer.append(Utils.repeat(state.getIndent(), state.getDepth()));
362
366
  }
@@ -82,6 +82,12 @@ public class GeneratorState extends RubyObject {
82
82
  */
83
83
  private boolean quirksMode = DEFAULT_QUIRKS_MODE;
84
84
  static final boolean DEFAULT_QUIRKS_MODE = false;
85
+ /**
86
+ * If set to <code>true</code> the forward slash will be escaped in
87
+ * json output.
88
+ */
89
+ private boolean escapeSlash = DEFAULT_ESCAPE_SLASH;
90
+ static final boolean DEFAULT_ESCAPE_SLASH = false;
85
91
  /**
86
92
  * The initial buffer length of this state. (This isn't really used on all
87
93
  * non-C implementations.)
@@ -171,6 +177,9 @@ public class GeneratorState extends RubyObject {
171
177
  * <code>-Infinity</code> should be generated, otherwise an exception is
172
178
  * thrown if these values are encountered.
173
179
  * This options defaults to <code>false</code>.
180
+ * <dt><code>:escape_slash</code>
181
+ * <dd>set to <code>true</code> if the forward slashes should be escaped
182
+ * in the json output (default: <code>false</code>)
174
183
  */
175
184
  @JRubyMethod(optional=1, visibility=Visibility.PRIVATE)
176
185
  public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
@@ -194,6 +203,7 @@ public class GeneratorState extends RubyObject {
194
203
  this.allowNaN = orig.allowNaN;
195
204
  this.asciiOnly = orig.asciiOnly;
196
205
  this.quirksMode = orig.quirksMode;
206
+ this.escapeSlash = orig.escapeSlash;
197
207
  this.bufferInitialLength = orig.bufferInitialLength;
198
208
  this.depth = orig.depth;
199
209
  return this;
@@ -346,6 +356,24 @@ public class GeneratorState extends RubyObject {
346
356
  return max_nesting;
347
357
  }
348
358
 
359
+ /**
360
+ * Returns true if forward slashes are escaped in the json output.
361
+ */
362
+ public boolean escapeSlash() {
363
+ return escapeSlash;
364
+ }
365
+
366
+ @JRubyMethod(name="escape_slash")
367
+ public RubyBoolean escape_slash_get(ThreadContext context) {
368
+ return context.getRuntime().newBoolean(escapeSlash);
369
+ }
370
+
371
+ @JRubyMethod(name="escape_slash=")
372
+ public IRubyObject escape_slash_set(IRubyObject escape_slash) {
373
+ escapeSlash = escape_slash.isTrue();
374
+ return escape_slash.getRuntime().newBoolean(escapeSlash);
375
+ }
376
+
349
377
  public boolean allowNaN() {
350
378
  return allowNaN;
351
379
  }
@@ -430,6 +458,7 @@ public class GeneratorState extends RubyObject {
430
458
  maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
431
459
  allowNaN = opts.getBool("allow_nan", DEFAULT_ALLOW_NAN);
432
460
  asciiOnly = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY);
461
+ escapeSlash = opts.getBool("escape_slash", DEFAULT_ESCAPE_SLASH);
433
462
  bufferInitialLength = opts.getInt("buffer_initial_length", DEFAULT_BUFFER_INITIAL_LENGTH);
434
463
 
435
464
  depth = opts.getInt("depth", 0);
@@ -457,6 +486,7 @@ public class GeneratorState extends RubyObject {
457
486
  result.op_aset(context, runtime.newSymbol("allow_nan"), allow_nan_p(context));
458
487
  result.op_aset(context, runtime.newSymbol("ascii_only"), ascii_only_p(context));
459
488
  result.op_aset(context, runtime.newSymbol("max_nesting"), max_nesting_get(context));
489
+ result.op_aset(context, runtime.newSymbol("escape_slash"), escape_slash_get(context));
460
490
  result.op_aset(context, runtime.newSymbol("depth"), depth_get(context));
461
491
  result.op_aset(context, runtime.newSymbol("buffer_initial_length"), buffer_initial_length_get(context));
462
492
  for (String name: getInstanceVariableNameList()) {
@@ -52,9 +52,11 @@ public class Parser extends RubyObject {
52
52
  private int maxNesting;
53
53
  private boolean allowNaN;
54
54
  private boolean symbolizeNames;
55
+ private boolean freeze;
55
56
  private RubyClass objectClass;
56
57
  private RubyClass arrayClass;
57
- private RubyHash matchString;
58
+ private RubyClass decimalClass;
59
+ private RubyHash match_string;
58
60
 
59
61
  private static final int DEFAULT_MAX_NESTING = 100;
60
62
 
@@ -133,6 +135,10 @@ public class Parser extends RubyObject {
133
135
  * <dt><code>:array_class</code>
134
136
  * <dd>Defaults to Array.
135
137
  *
138
+ * <dt><code>:decimal_class</code>
139
+ * <dd>Specifies which class to use instead of the default (Float) when
140
+ * parsing decimal numbers. This class must accept a single string argument
141
+ * in its constructor.
136
142
  * </dl>
137
143
  */
138
144
  @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
@@ -155,11 +161,13 @@ public class Parser extends RubyObject {
155
161
  this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
156
162
  this.allowNaN = opts.getBool("allow_nan", false);
157
163
  this.symbolizeNames = opts.getBool("symbolize_names", false);
164
+ this.freeze = opts.getBool("freeze", false);
158
165
  this.createId = opts.getString("create_id", getCreateId(context));
159
166
  this.createAdditions = opts.getBool("create_additions", false);
160
167
  this.objectClass = opts.getClass("object_class", runtime.getHash());
161
168
  this.arrayClass = opts.getClass("array_class", runtime.getArray());
162
- this.matchString = opts.getHash("match_string");
169
+ this.decimalClass = opts.getClass("decimal_class", null);
170
+ this.match_string = opts.getHash("match_string");
163
171
 
164
172
  if(symbolizeNames && createAdditions) {
165
173
  throw runtime.newArgumentError(
@@ -307,11 +315,11 @@ public class Parser extends RubyObject {
307
315
  }
308
316
 
309
317
 
310
- // line 333 "Parser.rl"
318
+ // line 341 "Parser.rl"
311
319
 
312
320
 
313
321
 
314
- // line 315 "Parser.java"
322
+ // line 323 "Parser.java"
315
323
  private static byte[] init__JSON_value_actions_0()
316
324
  {
317
325
  return new byte [] {
@@ -425,7 +433,7 @@ static final int JSON_value_error = 0;
425
433
  static final int JSON_value_en_main = 1;
426
434
 
427
435
 
428
- // line 439 "Parser.rl"
436
+ // line 447 "Parser.rl"
429
437
 
430
438
 
431
439
  void parseValue(ParserResult res, int p, int pe) {
@@ -433,14 +441,14 @@ static final int JSON_value_en_main = 1;
433
441
  IRubyObject result = null;
434
442
 
435
443
 
436
- // line 437 "Parser.java"
444
+ // line 445 "Parser.java"
437
445
  {
438
446
  cs = JSON_value_start;
439
447
  }
440
448
 
441
- // line 446 "Parser.rl"
449
+ // line 454 "Parser.rl"
442
450
 
443
- // line 444 "Parser.java"
451
+ // line 452 "Parser.java"
444
452
  {
445
453
  int _klen;
446
454
  int _trans = 0;
@@ -466,13 +474,13 @@ case 1:
466
474
  while ( _nacts-- > 0 ) {
467
475
  switch ( _JSON_value_actions[_acts++] ) {
468
476
  case 9:
469
- // line 424 "Parser.rl"
477
+ // line 432 "Parser.rl"
470
478
  {
471
479
  p--;
472
480
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
473
481
  }
474
482
  break;
475
- // line 476 "Parser.java"
483
+ // line 484 "Parser.java"
476
484
  }
477
485
  }
478
486
 
@@ -535,25 +543,25 @@ case 1:
535
543
  switch ( _JSON_value_actions[_acts++] )
536
544
  {
537
545
  case 0:
538
- // line 341 "Parser.rl"
546
+ // line 349 "Parser.rl"
539
547
  {
540
548
  result = getRuntime().getNil();
541
549
  }
542
550
  break;
543
551
  case 1:
544
- // line 344 "Parser.rl"
552
+ // line 352 "Parser.rl"
545
553
  {
546
554
  result = getRuntime().getFalse();
547
555
  }
548
556
  break;
549
557
  case 2:
550
- // line 347 "Parser.rl"
558
+ // line 355 "Parser.rl"
551
559
  {
552
560
  result = getRuntime().getTrue();
553
561
  }
554
562
  break;
555
563
  case 3:
556
- // line 350 "Parser.rl"
564
+ // line 358 "Parser.rl"
557
565
  {
558
566
  if (parser.allowNaN) {
559
567
  result = getConstant(CONST_NAN);
@@ -563,7 +571,7 @@ case 1:
563
571
  }
564
572
  break;
565
573
  case 4:
566
- // line 357 "Parser.rl"
574
+ // line 365 "Parser.rl"
567
575
  {
568
576
  if (parser.allowNaN) {
569
577
  result = getConstant(CONST_INFINITY);
@@ -573,7 +581,7 @@ case 1:
573
581
  }
574
582
  break;
575
583
  case 5:
576
- // line 364 "Parser.rl"
584
+ // line 372 "Parser.rl"
577
585
  {
578
586
  if (pe > p + 8 &&
579
587
  absSubSequence(p, p + 9).equals(JSON_MINUS_INFINITY)) {
@@ -602,7 +610,7 @@ case 1:
602
610
  }
603
611
  break;
604
612
  case 6:
605
- // line 390 "Parser.rl"
613
+ // line 398 "Parser.rl"
606
614
  {
607
615
  parseString(res, p, pe);
608
616
  if (res.result == null) {
@@ -615,7 +623,7 @@ case 1:
615
623
  }
616
624
  break;
617
625
  case 7:
618
- // line 400 "Parser.rl"
626
+ // line 408 "Parser.rl"
619
627
  {
620
628
  currentNesting++;
621
629
  parseArray(res, p, pe);
@@ -630,7 +638,7 @@ case 1:
630
638
  }
631
639
  break;
632
640
  case 8:
633
- // line 412 "Parser.rl"
641
+ // line 420 "Parser.rl"
634
642
  {
635
643
  currentNesting++;
636
644
  parseObject(res, p, pe);
@@ -644,7 +652,7 @@ case 1:
644
652
  }
645
653
  }
646
654
  break;
647
- // line 648 "Parser.java"
655
+ // line 656 "Parser.java"
648
656
  }
649
657
  }
650
658
  }
@@ -664,9 +672,12 @@ case 5:
664
672
  break; }
665
673
  }
666
674
 
667
- // line 447 "Parser.rl"
675
+ // line 455 "Parser.rl"
668
676
 
669
677
  if (cs >= JSON_value_first_final && result != null) {
678
+ if (parser.freeze) {
679
+ result.setFrozen(true);
680
+ }
670
681
  res.update(result, p);
671
682
  } else {
672
683
  res.update(null, p);
@@ -674,7 +685,7 @@ case 5:
674
685
  }
675
686
 
676
687
 
677
- // line 678 "Parser.java"
688
+ // line 689 "Parser.java"
678
689
  private static byte[] init__JSON_integer_actions_0()
679
690
  {
680
691
  return new byte [] {
@@ -773,7 +784,7 @@ static final int JSON_integer_error = 0;
773
784
  static final int JSON_integer_en_main = 1;
774
785
 
775
786
 
776
- // line 466 "Parser.rl"
787
+ // line 477 "Parser.rl"
777
788
 
778
789
 
779
790
  void parseInteger(ParserResult res, int p, int pe) {
@@ -791,15 +802,15 @@ static final int JSON_integer_en_main = 1;
791
802
  int cs = EVIL;
792
803
 
793
804
 
794
- // line 795 "Parser.java"
805
+ // line 806 "Parser.java"
795
806
  {
796
807
  cs = JSON_integer_start;
797
808
  }
798
809
 
799
- // line 483 "Parser.rl"
810
+ // line 494 "Parser.rl"
800
811
  int memo = p;
801
812
 
802
- // line 803 "Parser.java"
813
+ // line 814 "Parser.java"
803
814
  {
804
815
  int _klen;
805
816
  int _trans = 0;
@@ -880,13 +891,13 @@ case 1:
880
891
  switch ( _JSON_integer_actions[_acts++] )
881
892
  {
882
893
  case 0:
883
- // line 460 "Parser.rl"
894
+ // line 471 "Parser.rl"
884
895
  {
885
896
  p--;
886
897
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
887
898
  }
888
899
  break;
889
- // line 890 "Parser.java"
900
+ // line 901 "Parser.java"
890
901
  }
891
902
  }
892
903
  }
@@ -906,7 +917,7 @@ case 5:
906
917
  break; }
907
918
  }
908
919
 
909
- // line 485 "Parser.rl"
920
+ // line 496 "Parser.rl"
910
921
 
911
922
  if (cs < JSON_integer_first_final) {
912
923
  return -1;
@@ -914,13 +925,13 @@ case 5:
914
925
 
915
926
  return p;
916
927
  }
917
-
928
+
918
929
  RubyInteger createInteger(int p, int new_p) {
919
930
  Ruby runtime = getRuntime();
920
931
  ByteList num = absSubSequence(p, new_p);
921
932
  return bytesToInum(runtime, num);
922
933
  }
923
-
934
+
924
935
  RubyInteger bytesToInum(Ruby runtime, ByteList num) {
925
936
  return runtime.is1_9() ?
926
937
  ConvertBytes.byteListToInum19(runtime, num, 10, true) :
@@ -928,7 +939,7 @@ case 5:
928
939
  }
929
940
 
930
941
 
931
- // line 932 "Parser.java"
942
+ // line 943 "Parser.java"
932
943
  private static byte[] init__JSON_float_actions_0()
933
944
  {
934
945
  return new byte [] {
@@ -1030,7 +1041,7 @@ static final int JSON_float_error = 0;
1030
1041
  static final int JSON_float_en_main = 1;
1031
1042
 
1032
1043
 
1033
- // line 520 "Parser.rl"
1044
+ // line 531 "Parser.rl"
1034
1045
 
1035
1046
 
1036
1047
  void parseFloat(ParserResult res, int p, int pe) {
@@ -1039,7 +1050,9 @@ static final int JSON_float_en_main = 1;
1039
1050
  res.update(null, p);
1040
1051
  return;
1041
1052
  }
1042
- RubyFloat number = createFloat(p, new_p);
1053
+ IRubyObject number = parser.decimalClass == null ?
1054
+ createFloat(p, new_p) : createCustomDecimal(p, new_p);
1055
+
1043
1056
  res.update(number, new_p + 1);
1044
1057
  return;
1045
1058
  }
@@ -1048,15 +1061,15 @@ static final int JSON_float_en_main = 1;
1048
1061
  int cs = EVIL;
1049
1062
 
1050
1063
 
1051
- // line 1052 "Parser.java"
1064
+ // line 1065 "Parser.java"
1052
1065
  {
1053
1066
  cs = JSON_float_start;
1054
1067
  }
1055
1068
 
1056
- // line 537 "Parser.rl"
1069
+ // line 550 "Parser.rl"
1057
1070
  int memo = p;
1058
1071
 
1059
- // line 1060 "Parser.java"
1072
+ // line 1073 "Parser.java"
1060
1073
  {
1061
1074
  int _klen;
1062
1075
  int _trans = 0;
@@ -1137,13 +1150,13 @@ case 1:
1137
1150
  switch ( _JSON_float_actions[_acts++] )
1138
1151
  {
1139
1152
  case 0:
1140
- // line 511 "Parser.rl"
1153
+ // line 522 "Parser.rl"
1141
1154
  {
1142
1155
  p--;
1143
1156
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
1144
1157
  }
1145
1158
  break;
1146
- // line 1147 "Parser.java"
1159
+ // line 1160 "Parser.java"
1147
1160
  }
1148
1161
  }
1149
1162
  }
@@ -1163,23 +1176,30 @@ case 5:
1163
1176
  break; }
1164
1177
  }
1165
1178
 
1166
- // line 539 "Parser.rl"
1179
+ // line 552 "Parser.rl"
1167
1180
 
1168
1181
  if (cs < JSON_float_first_final) {
1169
1182
  return -1;
1170
1183
  }
1171
-
1184
+
1172
1185
  return p;
1173
1186
  }
1174
-
1187
+
1175
1188
  RubyFloat createFloat(int p, int new_p) {
1176
1189
  Ruby runtime = getRuntime();
1177
1190
  ByteList num = absSubSequence(p, new_p);
1178
1191
  return RubyFloat.newFloat(runtime, dc.parse(num, true, runtime.is1_9()));
1179
1192
  }
1180
1193
 
1194
+ IRubyObject createCustomDecimal(int p, int new_p) {
1195
+ Ruby runtime = getRuntime();
1196
+ ByteList num = absSubSequence(p, new_p);
1197
+ IRubyObject numString = runtime.newString(num.toString());
1198
+ return parser.decimalClass.callMethod(context, "new", numString);
1199
+ }
1200
+
1181
1201
 
1182
- // line 1183 "Parser.java"
1202
+ // line 1203 "Parser.java"
1183
1203
  private static byte[] init__JSON_string_actions_0()
1184
1204
  {
1185
1205
  return new byte [] {
@@ -1281,7 +1301,7 @@ static final int JSON_string_error = 0;
1281
1301
  static final int JSON_string_en_main = 1;
1282
1302
 
1283
1303
 
1284
- // line 584 "Parser.rl"
1304
+ // line 604 "Parser.rl"
1285
1305
 
1286
1306
 
1287
1307
  void parseString(ParserResult res, int p, int pe) {
@@ -1289,15 +1309,15 @@ static final int JSON_string_en_main = 1;
1289
1309
  IRubyObject result = null;
1290
1310
 
1291
1311
 
1292
- // line 1293 "Parser.java"
1312
+ // line 1313 "Parser.java"
1293
1313
  {
1294
1314
  cs = JSON_string_start;
1295
1315
  }
1296
1316
 
1297
- // line 591 "Parser.rl"
1317
+ // line 611 "Parser.rl"
1298
1318
  int memo = p;
1299
1319
 
1300
- // line 1301 "Parser.java"
1320
+ // line 1321 "Parser.java"
1301
1321
  {
1302
1322
  int _klen;
1303
1323
  int _trans = 0;
@@ -1378,7 +1398,7 @@ case 1:
1378
1398
  switch ( _JSON_string_actions[_acts++] )
1379
1399
  {
1380
1400
  case 0:
1381
- // line 559 "Parser.rl"
1401
+ // line 579 "Parser.rl"
1382
1402
  {
1383
1403
  int offset = byteList.begin();
1384
1404
  ByteList decoded = decoder.decode(byteList, memo + 1 - offset,
@@ -1393,13 +1413,13 @@ case 1:
1393
1413
  }
1394
1414
  break;
1395
1415
  case 1:
1396
- // line 572 "Parser.rl"
1416
+ // line 592 "Parser.rl"
1397
1417
  {
1398
1418
  p--;
1399
1419
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
1400
1420
  }
1401
1421
  break;
1402
- // line 1403 "Parser.java"
1422
+ // line 1423 "Parser.java"
1403
1423
  }
1404
1424
  }
1405
1425
  }
@@ -1419,10 +1439,10 @@ case 5:
1419
1439
  break; }
1420
1440
  }
1421
1441
 
1422
- // line 593 "Parser.rl"
1442
+ // line 613 "Parser.rl"
1423
1443
 
1424
1444
  if (parser.createAdditions) {
1425
- RubyHash matchString = parser.matchString;
1445
+ RubyHash matchString = parser.match_string;
1426
1446
  if (matchString != null) {
1427
1447
  final IRubyObject[] memoArray = { result, null };
1428
1448
  try {
@@ -1446,18 +1466,25 @@ case 5:
1446
1466
  }
1447
1467
  }
1448
1468
 
1449
- if (cs >= JSON_string_first_final && result != null) {
1469
+ if (cs >= JSON_string_first_final && result != null) {
1450
1470
  if (result instanceof RubyString) {
1451
- ((RubyString)result).force_encoding(context, info.utf8.get());
1471
+ RubyString string = (RubyString)result;
1472
+ string.force_encoding(context, info.utf8.get());
1473
+ if (parser.freeze) {
1474
+ string.setFrozen(true);
1475
+ string = getRuntime().freezeAndDedupString(string);
1476
+ }
1477
+ res.update(string, p + 1);
1478
+ } else {
1479
+ res.update(result, p + 1);
1452
1480
  }
1453
- res.update(result, p + 1);
1454
1481
  } else {
1455
1482
  res.update(null, p + 1);
1456
1483
  }
1457
1484
  }
1458
1485
 
1459
1486
 
1460
- // line 1461 "Parser.java"
1487
+ // line 1488 "Parser.java"
1461
1488
  private static byte[] init__JSON_array_actions_0()
1462
1489
  {
1463
1490
  return new byte [] {
@@ -1570,7 +1597,7 @@ static final int JSON_array_error = 0;
1570
1597
  static final int JSON_array_en_main = 1;
1571
1598
 
1572
1599
 
1573
- // line 666 "Parser.rl"
1600
+ // line 693 "Parser.rl"
1574
1601
 
1575
1602
 
1576
1603
  void parseArray(ParserResult res, int p, int pe) {
@@ -1590,14 +1617,14 @@ static final int JSON_array_en_main = 1;
1590
1617
  }
1591
1618
 
1592
1619
 
1593
- // line 1594 "Parser.java"
1620
+ // line 1621 "Parser.java"
1594
1621
  {
1595
1622
  cs = JSON_array_start;
1596
1623
  }
1597
1624
 
1598
- // line 685 "Parser.rl"
1625
+ // line 712 "Parser.rl"
1599
1626
 
1600
- // line 1601 "Parser.java"
1627
+ // line 1628 "Parser.java"
1601
1628
  {
1602
1629
  int _klen;
1603
1630
  int _trans = 0;
@@ -1678,7 +1705,7 @@ case 1:
1678
1705
  switch ( _JSON_array_actions[_acts++] )
1679
1706
  {
1680
1707
  case 0:
1681
- // line 635 "Parser.rl"
1708
+ // line 662 "Parser.rl"
1682
1709
  {
1683
1710
  parseValue(res, p, pe);
1684
1711
  if (res.result == null) {
@@ -1695,13 +1722,13 @@ case 1:
1695
1722
  }
1696
1723
  break;
1697
1724
  case 1:
1698
- // line 650 "Parser.rl"
1725
+ // line 677 "Parser.rl"
1699
1726
  {
1700
1727
  p--;
1701
1728
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
1702
1729
  }
1703
1730
  break;
1704
- // line 1705 "Parser.java"
1731
+ // line 1732 "Parser.java"
1705
1732
  }
1706
1733
  }
1707
1734
  }
@@ -1721,7 +1748,7 @@ case 5:
1721
1748
  break; }
1722
1749
  }
1723
1750
 
1724
- // line 686 "Parser.rl"
1751
+ // line 713 "Parser.rl"
1725
1752
 
1726
1753
  if (cs >= JSON_array_first_final) {
1727
1754
  res.update(result, p + 1);
@@ -1731,7 +1758,7 @@ case 5:
1731
1758
  }
1732
1759
 
1733
1760
 
1734
- // line 1735 "Parser.java"
1761
+ // line 1762 "Parser.java"
1735
1762
  private static byte[] init__JSON_object_actions_0()
1736
1763
  {
1737
1764
  return new byte [] {
@@ -1854,7 +1881,7 @@ static final int JSON_object_error = 0;
1854
1881
  static final int JSON_object_en_main = 1;
1855
1882
 
1856
1883
 
1857
- // line 745 "Parser.rl"
1884
+ // line 772 "Parser.rl"
1858
1885
 
1859
1886
 
1860
1887
  void parseObject(ParserResult res, int p, int pe) {
@@ -1879,14 +1906,14 @@ static final int JSON_object_en_main = 1;
1879
1906
  }
1880
1907
 
1881
1908
 
1882
- // line 1883 "Parser.java"
1909
+ // line 1910 "Parser.java"
1883
1910
  {
1884
1911
  cs = JSON_object_start;
1885
1912
  }
1886
1913
 
1887
- // line 769 "Parser.rl"
1914
+ // line 796 "Parser.rl"
1888
1915
 
1889
- // line 1890 "Parser.java"
1916
+ // line 1917 "Parser.java"
1890
1917
  {
1891
1918
  int _klen;
1892
1919
  int _trans = 0;
@@ -1967,7 +1994,7 @@ case 1:
1967
1994
  switch ( _JSON_object_actions[_acts++] )
1968
1995
  {
1969
1996
  case 0:
1970
- // line 700 "Parser.rl"
1997
+ // line 727 "Parser.rl"
1971
1998
  {
1972
1999
  parseValue(res, p, pe);
1973
2000
  if (res.result == null) {
@@ -1984,7 +2011,7 @@ case 1:
1984
2011
  }
1985
2012
  break;
1986
2013
  case 1:
1987
- // line 715 "Parser.rl"
2014
+ // line 742 "Parser.rl"
1988
2015
  {
1989
2016
  parseString(res, p, pe);
1990
2017
  if (res.result == null) {
@@ -2004,13 +2031,13 @@ case 1:
2004
2031
  }
2005
2032
  break;
2006
2033
  case 2:
2007
- // line 733 "Parser.rl"
2034
+ // line 760 "Parser.rl"
2008
2035
  {
2009
2036
  p--;
2010
2037
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
2011
2038
  }
2012
2039
  break;
2013
- // line 2014 "Parser.java"
2040
+ // line 2041 "Parser.java"
2014
2041
  }
2015
2042
  }
2016
2043
  }
@@ -2030,7 +2057,7 @@ case 5:
2030
2057
  break; }
2031
2058
  }
2032
2059
 
2033
- // line 770 "Parser.rl"
2060
+ // line 797 "Parser.rl"
2034
2061
 
2035
2062
  if (cs < JSON_object_first_final) {
2036
2063
  res.update(null, p + 1);
@@ -2063,7 +2090,7 @@ case 5:
2063
2090
  }
2064
2091
 
2065
2092
 
2066
- // line 2067 "Parser.java"
2093
+ // line 2094 "Parser.java"
2067
2094
  private static byte[] init__JSON_actions_0()
2068
2095
  {
2069
2096
  return new byte [] {
@@ -2166,7 +2193,7 @@ static final int JSON_error = 0;
2166
2193
  static final int JSON_en_main = 1;
2167
2194
 
2168
2195
 
2169
- // line 821 "Parser.rl"
2196
+ // line 848 "Parser.rl"
2170
2197
 
2171
2198
 
2172
2199
  public IRubyObject parseImplemetation() {
@@ -2176,16 +2203,16 @@ static final int JSON_en_main = 1;
2176
2203
  ParserResult res = new ParserResult();
2177
2204
 
2178
2205
 
2179
- // line 2180 "Parser.java"
2206
+ // line 2207 "Parser.java"
2180
2207
  {
2181
2208
  cs = JSON_start;
2182
2209
  }
2183
2210
 
2184
- // line 830 "Parser.rl"
2211
+ // line 857 "Parser.rl"
2185
2212
  p = byteList.begin();
2186
2213
  pe = p + byteList.length();
2187
2214
 
2188
- // line 2189 "Parser.java"
2215
+ // line 2216 "Parser.java"
2189
2216
  {
2190
2217
  int _klen;
2191
2218
  int _trans = 0;
@@ -2266,7 +2293,7 @@ case 1:
2266
2293
  switch ( _JSON_actions[_acts++] )
2267
2294
  {
2268
2295
  case 0:
2269
- // line 807 "Parser.rl"
2296
+ // line 834 "Parser.rl"
2270
2297
  {
2271
2298
  parseValue(res, p, pe);
2272
2299
  if (res.result == null) {
@@ -2278,7 +2305,7 @@ case 1:
2278
2305
  }
2279
2306
  }
2280
2307
  break;
2281
- // line 2282 "Parser.java"
2308
+ // line 2309 "Parser.java"
2282
2309
  }
2283
2310
  }
2284
2311
  }
@@ -2298,7 +2325,7 @@ case 5:
2298
2325
  break; }
2299
2326
  }
2300
2327
 
2301
- // line 833 "Parser.rl"
2328
+ // line 860 "Parser.rl"
2302
2329
 
2303
2330
  if (cs >= JSON_first_final && p == pe) {
2304
2331
  return result;