json-maglev- 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. data/CHANGES +200 -0
  2. data/COPYING +58 -0
  3. data/COPYING-json-jruby +57 -0
  4. data/GPL +340 -0
  5. data/Gemfile +7 -0
  6. data/README-json-jruby.markdown +33 -0
  7. data/README.rdoc +358 -0
  8. data/Rakefile +407 -0
  9. data/TODO +1 -0
  10. data/VERSION +1 -0
  11. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log +52 -0
  12. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat +1000 -0
  13. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat +1001 -0
  14. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty-autocorrelation.dat +900 -0
  15. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty.dat +901 -0
  16. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe-autocorrelation.dat +1000 -0
  17. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe.dat +1001 -0
  18. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt.log +261 -0
  19. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast-autocorrelation.dat +1000 -0
  20. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast.dat +1001 -0
  21. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty-autocorrelation.dat +1000 -0
  22. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty.dat +1001 -0
  23. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat +1000 -0
  24. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe.dat +1001 -0
  25. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure.log +262 -0
  26. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator-autocorrelation.dat +1000 -0
  27. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator.dat +1001 -0
  28. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails.log +82 -0
  29. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkComparison.log +34 -0
  30. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser-autocorrelation.dat +900 -0
  31. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser.dat +901 -0
  32. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt.log +81 -0
  33. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser-autocorrelation.dat +1000 -0
  34. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat +1001 -0
  35. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log +82 -0
  36. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat +1000 -0
  37. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser.dat +1001 -0
  38. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails.log +82 -0
  39. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser-autocorrelation.dat +1000 -0
  40. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser.dat +1001 -0
  41. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML.log +82 -0
  42. data/benchmarks/generator2_benchmark.rb +222 -0
  43. data/benchmarks/generator_benchmark.rb +224 -0
  44. data/benchmarks/ohai.json +1216 -0
  45. data/benchmarks/ohai.ruby +1 -0
  46. data/benchmarks/parser2_benchmark.rb +251 -0
  47. data/benchmarks/parser_benchmark.rb +259 -0
  48. data/bin/edit_json.rb +9 -0
  49. data/bin/prettify_json.rb +48 -0
  50. data/data/example.json +1 -0
  51. data/data/index.html +38 -0
  52. data/data/prototype.js +4184 -0
  53. data/ext/json/ext/generator/extconf.rb +20 -0
  54. data/ext/json/ext/generator/generator.c +1414 -0
  55. data/ext/json/ext/generator/generator.h +196 -0
  56. data/ext/json/ext/parser/extconf.rb +16 -0
  57. data/ext/json/ext/parser/parser.c +1996 -0
  58. data/ext/json/ext/parser/parser.h +82 -0
  59. data/ext/json/ext/parser/parser.rl +853 -0
  60. data/install.rb +26 -0
  61. data/java/lib/bytelist-1.0.6.jar +0 -0
  62. data/java/lib/jcodings.jar +0 -0
  63. data/java/src/json/ext/ByteListTranscoder.java +167 -0
  64. data/java/src/json/ext/Generator.java +437 -0
  65. data/java/src/json/ext/GeneratorMethods.java +232 -0
  66. data/java/src/json/ext/GeneratorService.java +43 -0
  67. data/java/src/json/ext/GeneratorState.java +473 -0
  68. data/java/src/json/ext/OptionsReader.java +119 -0
  69. data/java/src/json/ext/Parser.java +2314 -0
  70. data/java/src/json/ext/Parser.rl +844 -0
  71. data/java/src/json/ext/ParserService.java +35 -0
  72. data/java/src/json/ext/RuntimeInfo.java +121 -0
  73. data/java/src/json/ext/StringDecoder.java +166 -0
  74. data/java/src/json/ext/StringEncoder.java +106 -0
  75. data/java/src/json/ext/Utils.java +89 -0
  76. data/json-java.gemspec +22 -0
  77. data/json.gemspec +41 -0
  78. data/json_pure.gemspec +46 -0
  79. data/lib/json.rb +62 -0
  80. data/lib/json/Array.xpm +21 -0
  81. data/lib/json/FalseClass.xpm +21 -0
  82. data/lib/json/Hash.xpm +21 -0
  83. data/lib/json/Key.xpm +73 -0
  84. data/lib/json/NilClass.xpm +21 -0
  85. data/lib/json/Numeric.xpm +28 -0
  86. data/lib/json/String.xpm +96 -0
  87. data/lib/json/TrueClass.xpm +21 -0
  88. data/lib/json/add/core.rb +243 -0
  89. data/lib/json/add/rails.rb +8 -0
  90. data/lib/json/common.rb +423 -0
  91. data/lib/json/editor.rb +1369 -0
  92. data/lib/json/ext.rb +28 -0
  93. data/lib/json/json.xpm +1499 -0
  94. data/lib/json/pure.rb +15 -0
  95. data/lib/json/pure/generator.rb +442 -0
  96. data/lib/json/pure/parser.rb +320 -0
  97. data/lib/json/version.rb +8 -0
  98. data/tests/fixtures/fail1.json +1 -0
  99. data/tests/fixtures/fail10.json +1 -0
  100. data/tests/fixtures/fail11.json +1 -0
  101. data/tests/fixtures/fail12.json +1 -0
  102. data/tests/fixtures/fail13.json +1 -0
  103. data/tests/fixtures/fail14.json +1 -0
  104. data/tests/fixtures/fail18.json +1 -0
  105. data/tests/fixtures/fail19.json +1 -0
  106. data/tests/fixtures/fail2.json +1 -0
  107. data/tests/fixtures/fail20.json +1 -0
  108. data/tests/fixtures/fail21.json +1 -0
  109. data/tests/fixtures/fail22.json +1 -0
  110. data/tests/fixtures/fail23.json +1 -0
  111. data/tests/fixtures/fail24.json +1 -0
  112. data/tests/fixtures/fail25.json +1 -0
  113. data/tests/fixtures/fail27.json +2 -0
  114. data/tests/fixtures/fail28.json +2 -0
  115. data/tests/fixtures/fail3.json +1 -0
  116. data/tests/fixtures/fail4.json +1 -0
  117. data/tests/fixtures/fail5.json +1 -0
  118. data/tests/fixtures/fail6.json +1 -0
  119. data/tests/fixtures/fail7.json +1 -0
  120. data/tests/fixtures/fail8.json +1 -0
  121. data/tests/fixtures/fail9.json +1 -0
  122. data/tests/fixtures/pass1.json +56 -0
  123. data/tests/fixtures/pass15.json +1 -0
  124. data/tests/fixtures/pass16.json +1 -0
  125. data/tests/fixtures/pass17.json +1 -0
  126. data/tests/fixtures/pass2.json +1 -0
  127. data/tests/fixtures/pass26.json +1 -0
  128. data/tests/fixtures/pass3.json +6 -0
  129. data/tests/setup_variant.rb +11 -0
  130. data/tests/test_json.rb +424 -0
  131. data/tests/test_json_addition.rb +167 -0
  132. data/tests/test_json_encoding.rb +65 -0
  133. data/tests/test_json_fixtures.rb +35 -0
  134. data/tests/test_json_generate.rb +180 -0
  135. data/tests/test_json_string_matching.rb +40 -0
  136. data/tests/test_json_unicode.rb +72 -0
  137. data/tools/fuzz.rb +139 -0
  138. data/tools/server.rb +61 -0
  139. metadata +265 -0
@@ -0,0 +1,844 @@
1
+ /*
2
+ * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
+ *
4
+ * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
5
+ * for details.
6
+ */
7
+ package json.ext;
8
+
9
+ import org.jruby.Ruby;
10
+ import org.jruby.RubyArray;
11
+ import org.jruby.RubyClass;
12
+ import org.jruby.RubyEncoding;
13
+ import org.jruby.RubyFloat;
14
+ import org.jruby.RubyHash;
15
+ import org.jruby.RubyInteger;
16
+ import org.jruby.RubyModule;
17
+ import org.jruby.RubyNumeric;
18
+ import org.jruby.RubyObject;
19
+ import org.jruby.RubyString;
20
+ import org.jruby.anno.JRubyMethod;
21
+ import org.jruby.exceptions.JumpException;
22
+ import org.jruby.exceptions.RaiseException;
23
+ import org.jruby.runtime.Block;
24
+ import org.jruby.runtime.ObjectAllocator;
25
+ import org.jruby.runtime.ThreadContext;
26
+ import org.jruby.runtime.Visibility;
27
+ import org.jruby.runtime.builtin.IRubyObject;
28
+ import org.jruby.util.ByteList;
29
+
30
+ /**
31
+ * The <code>JSON::Ext::Parser</code> class.
32
+ *
33
+ * <p>This is the JSON parser implemented as a Java class. To use it as the
34
+ * standard parser, set
35
+ * <pre>JSON.parser = JSON::Ext::Parser</pre>
36
+ * This is performed for you when you <code>include "json/ext"</code>.
37
+ *
38
+ * <p>This class does not perform the actual parsing, just acts as an interface
39
+ * to Ruby code. When the {@link #parse()} method is invoked, a
40
+ * Parser.ParserSession object is instantiated, which handles the process.
41
+ *
42
+ * @author mernen
43
+ */
44
+ public class Parser extends RubyObject {
45
+ private final RuntimeInfo info;
46
+ private RubyString vSource;
47
+ private RubyString createId;
48
+ private boolean createAdditions;
49
+ private int maxNesting;
50
+ private boolean allowNaN;
51
+ private boolean symbolizeNames;
52
+ private RubyClass objectClass;
53
+ private RubyClass arrayClass;
54
+ private RubyHash match_string;
55
+
56
+ private static final int DEFAULT_MAX_NESTING = 19;
57
+
58
+ private static final String JSON_MINUS_INFINITY = "-Infinity";
59
+ // constant names in the JSON module containing those values
60
+ private static final String CONST_NAN = "NaN";
61
+ private static final String CONST_INFINITY = "Infinity";
62
+ private static final String CONST_MINUS_INFINITY = "MinusInfinity";
63
+
64
+ static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
65
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
66
+ return new Parser(runtime, klazz);
67
+ }
68
+ };
69
+
70
+ /**
71
+ * Multiple-value return for internal parser methods.
72
+ *
73
+ * <p>All the <code>parse<var>Stuff</var></code> methods return instances of
74
+ * <code>ParserResult</code> when successful, or <code>null</code> when
75
+ * there's a problem with the input data.
76
+ */
77
+ static final class ParserResult {
78
+ /**
79
+ * The result of the successful parsing. Should never be
80
+ * <code>null</code>.
81
+ */
82
+ final IRubyObject result;
83
+ /**
84
+ * The point where the parser returned.
85
+ */
86
+ final int p;
87
+
88
+ ParserResult(IRubyObject result, int p) {
89
+ this.result = result;
90
+ this.p = p;
91
+ }
92
+ }
93
+
94
+ public Parser(Ruby runtime, RubyClass metaClass) {
95
+ super(runtime, metaClass);
96
+ info = RuntimeInfo.forRuntime(runtime);
97
+ }
98
+
99
+ /**
100
+ * <code>Parser.new(source, opts = {})</code>
101
+ *
102
+ * <p>Creates a new <code>JSON::Ext::Parser</code> instance for the string
103
+ * <code>source</code>.
104
+ * It will be configured by the <code>opts</code> Hash.
105
+ * <code>opts</code> can have the following keys:
106
+ *
107
+ * <dl>
108
+ * <dt><code>:max_nesting</code>
109
+ * <dd>The maximum depth of nesting allowed in the parsed data
110
+ * structures. Disable depth checking with <code>:max_nesting => false|nil|0</code>,
111
+ * it defaults to 19.
112
+ *
113
+ * <dt><code>:allow_nan</code>
114
+ * <dd>If set to <code>true</code>, allow <code>NaN</code>,
115
+ * <code>Infinity</code> and <code>-Infinity</code> in defiance of RFC 4627
116
+ * to be parsed by the Parser. This option defaults to <code>false</code>.
117
+ *
118
+ * <dt><code>:symbolize_names</code>
119
+ * <dd>If set to <code>true</code>, returns symbols for the names (keys) in
120
+ * a JSON object. Otherwise strings are returned, which is also the default.
121
+ *
122
+ * <dt><code>:create_additions</code>
123
+ * <dd>If set to <code>false</code>, the Parser doesn't create additions
124
+ * even if a matchin class and <code>create_id</code> was found. This option
125
+ * defaults to <code>true</code>.
126
+ *
127
+ * <dt><code>:object_class</code>
128
+ * <dd>Defaults to Hash.
129
+ *
130
+ * <dt><code>:array_class</code>
131
+ * <dd>Defaults to Array.
132
+ * </dl>
133
+ */
134
+ @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
135
+ public static IRubyObject newInstance(IRubyObject clazz, IRubyObject[] args, Block block) {
136
+ Parser parser = (Parser)((RubyClass)clazz).allocate();
137
+
138
+ parser.callInit(args, block);
139
+
140
+ return parser;
141
+ }
142
+
143
+ @JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE)
144
+ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
145
+ Ruby runtime = context.getRuntime();
146
+ if (this.vSource != null) {
147
+ throw runtime.newTypeError("already initialized instance");
148
+ }
149
+ RubyString source = convertEncoding(context, args[0].convertToString());
150
+
151
+ OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null);
152
+ this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
153
+ this.allowNaN = opts.getBool("allow_nan", false);
154
+ this.symbolizeNames = opts.getBool("symbolize_names", false);
155
+ this.createId = opts.getString("create_id", getCreateId(context));
156
+ this.createAdditions = opts.getBool("create_additions", true);
157
+ this.objectClass = opts.getClass("object_class", runtime.getHash());
158
+ this.arrayClass = opts.getClass("array_class", runtime.getArray());
159
+ this.match_string = opts.getHash("match_string");
160
+
161
+ this.vSource = source;
162
+ return this;
163
+ }
164
+
165
+ /**
166
+ * Checks the given string's encoding. If a non-UTF-8 encoding is detected,
167
+ * a converted copy is returned.
168
+ * Returns the source string if no conversion is needed.
169
+ */
170
+ private RubyString convertEncoding(ThreadContext context, RubyString source) {
171
+ ByteList bl = source.getByteList();
172
+ int len = bl.length();
173
+ if (len < 2) {
174
+ throw Utils.newException(context, Utils.M_PARSER_ERROR,
175
+ "A JSON text must at least contain two octets!");
176
+ }
177
+
178
+ if (info.encodingsSupported()) {
179
+ RubyEncoding encoding = (RubyEncoding)source.encoding(context);
180
+ if (encoding != info.ascii8bit.get()) {
181
+ return (RubyString)source.encode(context, info.utf8.get());
182
+ }
183
+
184
+ String sniffedEncoding = sniffByteList(bl);
185
+ if (sniffedEncoding == null) return source; // assume UTF-8
186
+ return reinterpretEncoding(context, source, sniffedEncoding);
187
+ }
188
+
189
+ String sniffedEncoding = sniffByteList(bl);
190
+ if (sniffedEncoding == null) return source; // assume UTF-8
191
+ Ruby runtime = context.getRuntime();
192
+ return (RubyString)info.jsonModule.get().
193
+ callMethod(context, "iconv",
194
+ new IRubyObject[] {
195
+ runtime.newString("utf-8"),
196
+ runtime.newString(sniffedEncoding),
197
+ source});
198
+ }
199
+
200
+ /**
201
+ * Checks the first four bytes of the given ByteList to infer its encoding,
202
+ * using the principle demonstrated on section 3 of RFC 4627 (JSON).
203
+ */
204
+ private static String sniffByteList(ByteList bl) {
205
+ if (bl.length() < 4) return null;
206
+ if (bl.get(0) == 0 && bl.get(2) == 0) {
207
+ return bl.get(1) == 0 ? "utf-32be" : "utf-16be";
208
+ }
209
+ if (bl.get(1) == 0 && bl.get(3) == 0) {
210
+ return bl.get(2) == 0 ? "utf-32le" : "utf-16le";
211
+ }
212
+ return null;
213
+ }
214
+
215
+ /**
216
+ * Assumes the given (binary) RubyString to be in the given encoding, then
217
+ * converts it to UTF-8.
218
+ */
219
+ private RubyString reinterpretEncoding(ThreadContext context,
220
+ RubyString str, String sniffedEncoding) {
221
+ RubyEncoding actualEncoding = info.getEncoding(context, sniffedEncoding);
222
+ RubyEncoding targetEncoding = info.utf8.get();
223
+ RubyString dup = (RubyString)str.dup();
224
+ dup.force_encoding(context, actualEncoding);
225
+ return (RubyString)dup.encode_bang(context, targetEncoding);
226
+ }
227
+
228
+ /**
229
+ * <code>Parser#parse()</code>
230
+ *
231
+ * <p>Parses the current JSON text <code>source</code> and returns the
232
+ * complete data structure as a result.
233
+ */
234
+ @JRubyMethod
235
+ public IRubyObject parse(ThreadContext context) {
236
+ return new ParserSession(this, context).parse();
237
+ }
238
+
239
+ /**
240
+ * <code>Parser#source()</code>
241
+ *
242
+ * <p>Returns a copy of the current <code>source</code> string, that was
243
+ * used to construct this Parser.
244
+ */
245
+ @JRubyMethod(name = "source")
246
+ public IRubyObject source_get() {
247
+ return checkAndGetSource().dup();
248
+ }
249
+
250
+ public RubyString checkAndGetSource() {
251
+ if (vSource != null) {
252
+ return vSource;
253
+ } else {
254
+ throw getRuntime().newTypeError("uninitialized instance");
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Queries <code>JSON.create_id</code>. Returns <code>null</code> if it is
260
+ * set to <code>nil</code> or <code>false</code>, and a String if not.
261
+ */
262
+ private RubyString getCreateId(ThreadContext context) {
263
+ IRubyObject v = info.jsonModule.get().callMethod(context, "create_id");
264
+ return v.isTrue() ? v.convertToString() : null;
265
+ }
266
+
267
+ /**
268
+ * A string parsing session.
269
+ *
270
+ * <p>Once a ParserSession is instantiated, the source string should not
271
+ * change until the parsing is complete. The ParserSession object assumes
272
+ * the source {@link RubyString} is still associated to its original
273
+ * {@link ByteList}, which in turn must still be bound to the same
274
+ * <code>byte[]</code> value (and on the same offset).
275
+ */
276
+ // Ragel uses lots of fall-through
277
+ @SuppressWarnings("fallthrough")
278
+ private static class ParserSession {
279
+ private final Parser parser;
280
+ private final ThreadContext context;
281
+ private final ByteList byteList;
282
+ private final byte[] data;
283
+ private final StringDecoder decoder;
284
+ private int currentNesting = 0;
285
+
286
+ // initialization value for all state variables.
287
+ // no idea about the origins of this value, ask Flori ;)
288
+ private static final int EVIL = 0x666;
289
+
290
+ private ParserSession(Parser parser, ThreadContext context) {
291
+ this.parser = parser;
292
+ this.context = context;
293
+ this.byteList = parser.checkAndGetSource().getByteList();
294
+ this.data = byteList.unsafeBytes();
295
+ this.decoder = new StringDecoder(context);
296
+ }
297
+
298
+ private RaiseException unexpectedToken(int absStart, int absEnd) {
299
+ RubyString msg = getRuntime().newString("unexpected token at '")
300
+ .cat(data, absStart, absEnd - absStart)
301
+ .cat((byte)'\'');
302
+ return newException(Utils.M_PARSER_ERROR, msg);
303
+ }
304
+
305
+ private Ruby getRuntime() {
306
+ return context.getRuntime();
307
+ }
308
+
309
+ %%{
310
+ machine JSON_common;
311
+
312
+ cr = '\n';
313
+ cr_neg = [^\n];
314
+ ws = [ \t\r\n];
315
+ c_comment = '/*' ( any* - (any* '*/' any* ) ) '*/';
316
+ cpp_comment = '//' cr_neg* cr;
317
+ comment = c_comment | cpp_comment;
318
+ ignore = ws | comment;
319
+ name_separator = ':';
320
+ value_separator = ',';
321
+ Vnull = 'null';
322
+ Vfalse = 'false';
323
+ Vtrue = 'true';
324
+ VNaN = 'NaN';
325
+ VInfinity = 'Infinity';
326
+ VMinusInfinity = '-Infinity';
327
+ begin_value = [nft"\-[{NI] | digit;
328
+ begin_object = '{';
329
+ end_object = '}';
330
+ begin_array = '[';
331
+ end_array = ']';
332
+ begin_string = '"';
333
+ begin_name = begin_string;
334
+ begin_number = digit | '-';
335
+ }%%
336
+
337
+ %%{
338
+ machine JSON_value;
339
+ include JSON_common;
340
+
341
+ write data;
342
+
343
+ action parse_null {
344
+ result = getRuntime().getNil();
345
+ }
346
+ action parse_false {
347
+ result = getRuntime().getFalse();
348
+ }
349
+ action parse_true {
350
+ result = getRuntime().getTrue();
351
+ }
352
+ action parse_nan {
353
+ if (parser.allowNaN) {
354
+ result = getConstant(CONST_NAN);
355
+ } else {
356
+ throw unexpectedToken(p - 2, pe);
357
+ }
358
+ }
359
+ action parse_infinity {
360
+ if (parser.allowNaN) {
361
+ result = getConstant(CONST_INFINITY);
362
+ } else {
363
+ throw unexpectedToken(p - 7, pe);
364
+ }
365
+ }
366
+ action parse_number {
367
+ if (pe > fpc + 9 &&
368
+ absSubSequence(fpc, fpc + 9).toString().equals(JSON_MINUS_INFINITY)) {
369
+
370
+ if (parser.allowNaN) {
371
+ result = getConstant(CONST_MINUS_INFINITY);
372
+ fexec p + 10;
373
+ fhold;
374
+ fbreak;
375
+ } else {
376
+ throw unexpectedToken(p, pe);
377
+ }
378
+ }
379
+ ParserResult res = parseFloat(fpc, pe);
380
+ if (res != null) {
381
+ result = res.result;
382
+ fexec res.p;
383
+ }
384
+ res = parseInteger(fpc, pe);
385
+ if (res != null) {
386
+ result = res.result;
387
+ fexec res.p;
388
+ }
389
+ fhold;
390
+ fbreak;
391
+ }
392
+ action parse_string {
393
+ ParserResult res = parseString(fpc, pe);
394
+ if (res == null) {
395
+ fhold;
396
+ fbreak;
397
+ } else {
398
+ result = res.result;
399
+ fexec res.p;
400
+ }
401
+ }
402
+ action parse_array {
403
+ currentNesting++;
404
+ ParserResult res = parseArray(fpc, pe);
405
+ currentNesting--;
406
+ if (res == null) {
407
+ fhold;
408
+ fbreak;
409
+ } else {
410
+ result = res.result;
411
+ fexec res.p;
412
+ }
413
+ }
414
+ action parse_object {
415
+ currentNesting++;
416
+ ParserResult res = parseObject(fpc, pe);
417
+ currentNesting--;
418
+ if (res == null) {
419
+ fhold;
420
+ fbreak;
421
+ } else {
422
+ result = res.result;
423
+ fexec res.p;
424
+ }
425
+ }
426
+ action exit {
427
+ fhold;
428
+ fbreak;
429
+ }
430
+
431
+ main := ( Vnull @parse_null |
432
+ Vfalse @parse_false |
433
+ Vtrue @parse_true |
434
+ VNaN @parse_nan |
435
+ VInfinity @parse_infinity |
436
+ begin_number >parse_number |
437
+ begin_string >parse_string |
438
+ begin_array >parse_array |
439
+ begin_object >parse_object
440
+ ) %*exit;
441
+ }%%
442
+
443
+ ParserResult parseValue(int p, int pe) {
444
+ int cs = EVIL;
445
+ IRubyObject result = null;
446
+
447
+ %% write init;
448
+ %% write exec;
449
+
450
+ if (cs >= JSON_value_first_final && result != null) {
451
+ return new ParserResult(result, p);
452
+ } else {
453
+ return null;
454
+ }
455
+ }
456
+
457
+ %%{
458
+ machine JSON_integer;
459
+
460
+ write data;
461
+
462
+ action exit {
463
+ fhold;
464
+ fbreak;
465
+ }
466
+
467
+ main := '-'? ( '0' | [1-9][0-9]* ) ( ^[0-9] @exit );
468
+ }%%
469
+
470
+ ParserResult parseInteger(int p, int pe) {
471
+ int cs = EVIL;
472
+
473
+ %% write init;
474
+ int memo = p;
475
+ %% write exec;
476
+
477
+ if (cs < JSON_integer_first_final) {
478
+ return null;
479
+ }
480
+
481
+ ByteList num = absSubSequence(memo, p);
482
+ // note: this is actually a shared string, but since it is temporary and
483
+ // read-only, it doesn't really matter
484
+ RubyString expr = RubyString.newStringLight(getRuntime(), num);
485
+ RubyInteger number = RubyNumeric.str2inum(getRuntime(), expr, 10, true);
486
+ return new ParserResult(number, p + 1);
487
+ }
488
+
489
+ %%{
490
+ machine JSON_float;
491
+ include JSON_common;
492
+
493
+ write data;
494
+
495
+ action exit {
496
+ fhold;
497
+ fbreak;
498
+ }
499
+
500
+ main := '-'?
501
+ ( ( ( '0' | [1-9][0-9]* ) '.' [0-9]+ ( [Ee] [+\-]?[0-9]+ )? )
502
+ | ( ( '0' | [1-9][0-9]* ) ( [Ee] [+\-]? [0-9]+ ) ) )
503
+ ( ^[0-9Ee.\-] @exit );
504
+ }%%
505
+
506
+ ParserResult parseFloat(int p, int pe) {
507
+ int cs = EVIL;
508
+
509
+ %% write init;
510
+ int memo = p;
511
+ %% write exec;
512
+
513
+ if (cs < JSON_float_first_final) {
514
+ return null;
515
+ }
516
+
517
+ ByteList num = absSubSequence(memo, p);
518
+ // note: this is actually a shared string, but since it is temporary and
519
+ // read-only, it doesn't really matter
520
+ RubyString expr = RubyString.newStringLight(getRuntime(), num);
521
+ RubyFloat number = RubyNumeric.str2fnum(getRuntime(), expr, true);
522
+ return new ParserResult(number, p + 1);
523
+ }
524
+
525
+ %%{
526
+ machine JSON_string;
527
+ include JSON_common;
528
+
529
+ write data;
530
+
531
+ action parse_string {
532
+ int offset = byteList.begin();
533
+ ByteList decoded = decoder.decode(byteList, memo + 1 - offset,
534
+ p - offset);
535
+ result = getRuntime().newString(decoded);
536
+ if (result == null) {
537
+ fhold;
538
+ fbreak;
539
+ } else {
540
+ fexec p + 1;
541
+ }
542
+ }
543
+
544
+ action exit {
545
+ fhold;
546
+ fbreak;
547
+ }
548
+
549
+ main := '"'
550
+ ( ( ^(["\\]|0..0x1f)
551
+ | '\\'["\\/bfnrt]
552
+ | '\\u'[0-9a-fA-F]{4}
553
+ | '\\'^(["\\/bfnrtu]|0..0x1f)
554
+ )* %parse_string
555
+ ) '"' @exit;
556
+ }%%
557
+
558
+ ParserResult parseString(int p, int pe) {
559
+ int cs = EVIL;
560
+ IRubyObject result = null;
561
+
562
+ %% write init;
563
+ int memo = p;
564
+ %% write exec;
565
+
566
+ if (parser.createAdditions) {
567
+ RubyHash match_string = parser.match_string;
568
+ if (match_string != null) {
569
+ final IRubyObject[] memoArray = { result, null };
570
+ try {
571
+ match_string.visitAll(new RubyHash.Visitor() {
572
+ @Override
573
+ public void visit(IRubyObject pattern, IRubyObject klass) {
574
+ if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) {
575
+ memoArray[1] = klass;
576
+ throw JumpException.SPECIAL_JUMP;
577
+ }
578
+ }
579
+ });
580
+ } catch (JumpException e) { }
581
+ if (memoArray[1] != null) {
582
+ RubyClass klass = (RubyClass) memoArray[1];
583
+ if (klass.respondsTo("json_creatable?") &&
584
+ klass.callMethod(context, "json_creatable?").isTrue()) {
585
+ result = klass.callMethod(context, "json_create", result);
586
+ }
587
+ }
588
+ }
589
+ }
590
+
591
+ if (cs >= JSON_string_first_final && result != null) {
592
+ return new ParserResult(result, p + 1);
593
+ } else {
594
+ return null;
595
+ }
596
+ }
597
+
598
+ %%{
599
+ machine JSON_array;
600
+ include JSON_common;
601
+
602
+ write data;
603
+
604
+ action parse_value {
605
+ ParserResult res = parseValue(fpc, pe);
606
+ if (res == null) {
607
+ fhold;
608
+ fbreak;
609
+ } else {
610
+ if (!parser.arrayClass.getName().equals("Array")) {
611
+ result.callMethod(context, "<<", res.result);
612
+ } else {
613
+ result.append(res.result);
614
+ }
615
+ fexec res.p;
616
+ }
617
+ }
618
+
619
+ action exit {
620
+ fhold;
621
+ fbreak;
622
+ }
623
+
624
+ next_element = value_separator ignore* begin_value >parse_value;
625
+
626
+ main := begin_array
627
+ ignore*
628
+ ( ( begin_value >parse_value
629
+ ignore* )
630
+ ( ignore*
631
+ next_element
632
+ ignore* )* )?
633
+ ignore*
634
+ end_array @exit;
635
+ }%%
636
+
637
+ ParserResult parseArray(int p, int pe) {
638
+ int cs = EVIL;
639
+
640
+ if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) {
641
+ throw newException(Utils.M_NESTING_ERROR,
642
+ "nesting of " + currentNesting + " is too deep");
643
+ }
644
+
645
+ // this is guaranteed to be a RubyArray due to the earlier
646
+ // allocator test at OptionsReader#getClass
647
+ RubyArray result =
648
+ (RubyArray)parser.arrayClass.newInstance(context,
649
+ IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
650
+
651
+ %% write init;
652
+ %% write exec;
653
+
654
+ if (cs >= JSON_array_first_final) {
655
+ return new ParserResult(result, p + 1);
656
+ } else {
657
+ throw unexpectedToken(p, pe);
658
+ }
659
+ }
660
+
661
+ %%{
662
+ machine JSON_object;
663
+ include JSON_common;
664
+
665
+ write data;
666
+
667
+ action parse_value {
668
+ ParserResult res = parseValue(fpc, pe);
669
+ if (res == null) {
670
+ fhold;
671
+ fbreak;
672
+ } else {
673
+ if (!parser.objectClass.getName().equals("Hash")) {
674
+ result.callMethod(context, "[]=", new IRubyObject[] { lastName, res.result });
675
+ } else {
676
+ result.op_aset(context, lastName, res.result);
677
+ }
678
+ fexec res.p;
679
+ }
680
+ }
681
+
682
+ action parse_name {
683
+ ParserResult res = parseString(fpc, pe);
684
+ if (res == null) {
685
+ fhold;
686
+ fbreak;
687
+ } else {
688
+ RubyString name = (RubyString)res.result;
689
+ if (parser.symbolizeNames) {
690
+ lastName = context.getRuntime().is1_9()
691
+ ? name.intern19()
692
+ : name.intern();
693
+ } else {
694
+ lastName = name;
695
+ }
696
+ fexec res.p;
697
+ }
698
+ }
699
+
700
+ action exit {
701
+ fhold;
702
+ fbreak;
703
+ }
704
+
705
+ a_pair = ignore*
706
+ begin_name >parse_name
707
+ ignore* name_separator ignore*
708
+ begin_value >parse_value;
709
+
710
+ main := begin_object
711
+ (a_pair (ignore* value_separator a_pair)*)?
712
+ ignore* end_object @exit;
713
+ }%%
714
+
715
+ ParserResult parseObject(int p, int pe) {
716
+ int cs = EVIL;
717
+ IRubyObject lastName = null;
718
+
719
+ if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) {
720
+ throw newException(Utils.M_NESTING_ERROR,
721
+ "nesting of " + currentNesting + " is too deep");
722
+ }
723
+
724
+ // this is guaranteed to be a RubyHash due to the earlier
725
+ // allocator test at OptionsReader#getClass
726
+ RubyHash result =
727
+ (RubyHash)parser.objectClass.newInstance(context,
728
+ IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
729
+
730
+ %% write init;
731
+ %% write exec;
732
+
733
+ if (cs < JSON_object_first_final) {
734
+ return null;
735
+ }
736
+
737
+ IRubyObject returnedResult = result;
738
+
739
+ // attempt to de-serialize object
740
+ if (parser.createAdditions) {
741
+ IRubyObject vKlassName = result.op_aref(context, parser.createId);
742
+ if (!vKlassName.isNil()) {
743
+ // might throw ArgumentError, we let it propagate
744
+ IRubyObject klass = parser.info.jsonModule.get().
745
+ callMethod(context, "deep_const_get", vKlassName);
746
+ if (klass.respondsTo("json_creatable?") &&
747
+ klass.callMethod(context, "json_creatable?").isTrue()) {
748
+
749
+ returnedResult = klass.callMethod(context, "json_create", result);
750
+ }
751
+ }
752
+ }
753
+ return new ParserResult(returnedResult, p + 1);
754
+ }
755
+
756
+ %%{
757
+ machine JSON;
758
+ include JSON_common;
759
+
760
+ write data;
761
+
762
+ action parse_object {
763
+ currentNesting = 1;
764
+ ParserResult res = parseObject(fpc, pe);
765
+ if (res == null) {
766
+ fhold;
767
+ fbreak;
768
+ } else {
769
+ result = res.result;
770
+ fexec res.p;
771
+ }
772
+ }
773
+
774
+ action parse_array {
775
+ currentNesting = 1;
776
+ ParserResult res = parseArray(fpc, pe);
777
+ if (res == null) {
778
+ fhold;
779
+ fbreak;
780
+ } else {
781
+ result = res.result;
782
+ fexec res.p;
783
+ }
784
+ }
785
+
786
+ main := ignore*
787
+ ( begin_object >parse_object
788
+ | begin_array >parse_array )
789
+ ignore*;
790
+ }%%
791
+
792
+ public IRubyObject parse() {
793
+ int cs = EVIL;
794
+ int p, pe;
795
+ IRubyObject result = null;
796
+
797
+ %% write init;
798
+ p = byteList.begin();
799
+ pe = p + byteList.length();
800
+ %% write exec;
801
+
802
+ if (cs >= JSON_first_final && p == pe) {
803
+ return result;
804
+ } else {
805
+ throw unexpectedToken(p, pe);
806
+ }
807
+ }
808
+
809
+ /**
810
+ * Returns a subsequence of the source ByteList, based on source
811
+ * array byte offsets (i.e., the ByteList's own begin offset is not
812
+ * automatically added).
813
+ * @param start
814
+ * @param end
815
+ */
816
+ private ByteList absSubSequence(int absStart, int absEnd) {
817
+ int offset = byteList.begin();
818
+ return (ByteList)byteList.subSequence(absStart - offset,
819
+ absEnd - offset);
820
+ }
821
+
822
+ /**
823
+ * Retrieves a constant directly descended from the <code>JSON</code> module.
824
+ * @param name The constant name
825
+ */
826
+ private IRubyObject getConstant(String name) {
827
+ return parser.info.jsonModule.get().getConstant(name);
828
+ }
829
+
830
+ private RaiseException newException(String className, String message) {
831
+ return Utils.newException(context, className, message);
832
+ }
833
+
834
+ private RaiseException newException(String className, RubyString message) {
835
+ return Utils.newException(context, className, message);
836
+ }
837
+
838
+ private RaiseException newException(String className,
839
+ String messageBegin, ByteList messageEnd) {
840
+ return newException(className,
841
+ getRuntime().newString(messageBegin).cat(messageEnd));
842
+ }
843
+ }
844
+ }