parsanol 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of parsanol might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/HISTORY.txt +25 -0
- data/LICENSE +23 -0
- data/README.adoc +643 -0
- data/Rakefile +189 -0
- data/example/balanced-parens/basic.rb +42 -0
- data/example/balanced-parens/basic.rb.md +86 -0
- data/example/balanced-parens/parens.rb +42 -0
- data/example/balanced-parens/ruby_transform.rb +162 -0
- data/example/big.erb +73 -0
- data/example/boolean-algebra/basic.rb +70 -0
- data/example/boolean-algebra/basic.rb.md +108 -0
- data/example/boolean-algebra/ruby_transform.rb +263 -0
- data/example/calculator/basic.rb +153 -0
- data/example/calculator/basic.rb.md +120 -0
- data/example/calculator/pattern.rb +153 -0
- data/example/calculator/ruby_transform.rb +156 -0
- data/example/calculator/ruby_transform.rb.md +32 -0
- data/example/calculator/serialized.rb +257 -0
- data/example/calculator/serialized.rb.md +32 -0
- data/example/calculator/transform.rb +153 -0
- data/example/calculator/zero_copy.rb +269 -0
- data/example/calculator/zero_copy.rb.md +36 -0
- data/example/capture/basic.rb +49 -0
- data/example/capture/basic.rb.md +106 -0
- data/example/capture/example.json +39 -0
- data/example/comments/basic.rb +35 -0
- data/example/comments/basic.rb.md +110 -0
- data/example/csv/ruby_transform.rb +148 -0
- data/example/csv/ruby_transform.rb.md +131 -0
- data/example/csv/serialized.rb +201 -0
- data/example/csv/serialized.rb.md +31 -0
- data/example/csv/zero_copy.rb +276 -0
- data/example/csv/zero_copy.rb.md +36 -0
- data/example/custom_atoms/indent_atom.rb +79 -0
- data/example/deepest-errors/basic.rb +131 -0
- data/example/deepest-errors/basic.rb.md +152 -0
- data/example/documentation/basic.rb +18 -0
- data/example/documentation/basic.rb.md +97 -0
- data/example/email/basic.rb +55 -0
- data/example/email/basic.rb.md +102 -0
- data/example/email/ruby_transform.rb +106 -0
- data/example/empty/basic.rb +13 -0
- data/example/empty/basic.rb.md +73 -0
- data/example/empty/example.json +38 -0
- data/example/erb/basic.rb +47 -0
- data/example/erb/basic.rb.md +103 -0
- data/example/erb/optimized.rb +42 -0
- data/example/error-reporting/basic.rb +132 -0
- data/example/error-reporting/basic.rb.md +122 -0
- data/example/expression-evaluator/basic.rb +284 -0
- data/example/expression-evaluator/basic.rb.md +138 -0
- data/example/ini/basic.rb +154 -0
- data/example/ini/basic.rb.md +129 -0
- data/example/ini/ruby_transform.rb +154 -0
- data/example/ip-address/basic.rb +125 -0
- data/example/ip-address/basic.rb.md +139 -0
- data/example/iso-6709/basic.rb +231 -0
- data/example/iso-6709/basic.rb.md +143 -0
- data/example/iso-8601/basic.rb +275 -0
- data/example/iso-8601/basic.rb.md +149 -0
- data/example/json/basic.rb +128 -0
- data/example/json/basic.rb.md +121 -0
- data/example/json/pattern.rb +128 -0
- data/example/json/ruby_transform.rb +200 -0
- data/example/json/ruby_transform.rb.md +32 -0
- data/example/json/serialized.rb +233 -0
- data/example/json/serialized.rb.md +31 -0
- data/example/json/transform.rb +128 -0
- data/example/json/zero_copy.rb +316 -0
- data/example/json/zero_copy.rb.md +36 -0
- data/example/local/basic.rb +34 -0
- data/example/local/basic.rb.md +91 -0
- data/example/local/example.json +38 -0
- data/example/markdown/basic.rb +287 -0
- data/example/markdown/basic.rb.md +160 -0
- data/example/markup/basic.rb +173 -0
- data/example/markup/basic.rb.md +118 -0
- data/example/mathn/basic.rb +47 -0
- data/example/mathn/basic.rb.md +96 -0
- data/example/mathn/example.json +39 -0
- data/example/minilisp/basic.rb +94 -0
- data/example/minilisp/basic.rb.md +133 -0
- data/example/modularity/basic.rb +47 -0
- data/example/modularity/basic.rb.md +152 -0
- data/example/nested-errors/basic.rb +132 -0
- data/example/nested-errors/basic.rb.md +157 -0
- data/example/output/boolean_algebra.out +4 -0
- data/example/output/calc.out +1 -0
- data/example/output/capture.out +3 -0
- data/example/output/comments.out +8 -0
- data/example/output/deepest_errors.out +54 -0
- data/example/output/documentation.err +4 -0
- data/example/output/documentation.out +1 -0
- data/example/output/email_parser.out +2 -0
- data/example/output/empty.err +1 -0
- data/example/output/erb.out +7 -0
- data/example/output/ignore.out +1 -0
- data/example/output/ignore_whitespace.out +1 -0
- data/example/output/ip_address.out +9 -0
- data/example/output/json.out +5 -0
- data/example/output/local.out +3 -0
- data/example/output/mathn.out +4 -0
- data/example/output/minilisp.out +5 -0
- data/example/output/modularity.out +0 -0
- data/example/output/nested_errors.out +54 -0
- data/example/output/optimized_erb.out +1 -0
- data/example/output/parens.out +8 -0
- data/example/output/prec_calc.out +5 -0
- data/example/output/readme.out +1 -0
- data/example/output/scopes.out +1 -0
- data/example/output/seasons.out +28 -0
- data/example/output/sentence.out +1 -0
- data/example/output/simple_xml.out +2 -0
- data/example/output/string_parser.out +3 -0
- data/example/prec-calc/basic.rb +71 -0
- data/example/prec-calc/basic.rb.md +114 -0
- data/example/readme/basic.rb +30 -0
- data/example/readme/basic.rb.md +80 -0
- data/example/scopes/basic.rb +15 -0
- data/example/scopes/basic.rb.md +73 -0
- data/example/scopes/example.json +38 -0
- data/example/seasons/basic.rb +46 -0
- data/example/seasons/basic.rb.md +117 -0
- data/example/seasons/example.json +40 -0
- data/example/sentence/basic.rb +36 -0
- data/example/sentence/basic.rb.md +81 -0
- data/example/sexp/ruby_transform.rb +180 -0
- data/example/sexp/ruby_transform.rb.md +143 -0
- data/example/simple-xml/basic.rb +54 -0
- data/example/simple-xml/basic.rb.md +125 -0
- data/example/simple.lit +3 -0
- data/example/string-literal/basic.rb +77 -0
- data/example/string-literal/basic.rb.md +128 -0
- data/example/test.lit +4 -0
- data/example/toml/basic.rb +226 -0
- data/example/toml/basic.rb.md +173 -0
- data/example/url/basic.rb +219 -0
- data/example/url/basic.rb.md +142 -0
- data/example/url/ruby_transform.rb +219 -0
- data/example/yaml/basic.rb +216 -0
- data/example/yaml/basic.rb.md +148 -0
- data/ext/parsanol_native/extconf.rb +4 -0
- data/lib/parsanol/accelerator/application.rb +62 -0
- data/lib/parsanol/accelerator/engine.rb +112 -0
- data/lib/parsanol/accelerator.rb +162 -0
- data/lib/parsanol/ast_visitor.rb +122 -0
- data/lib/parsanol/atoms/alternative.rb +97 -0
- data/lib/parsanol/atoms/base.rb +214 -0
- data/lib/parsanol/atoms/can_flatten.rb +192 -0
- data/lib/parsanol/atoms/capture.rb +41 -0
- data/lib/parsanol/atoms/context.rb +351 -0
- data/lib/parsanol/atoms/context_optimized.rb +42 -0
- data/lib/parsanol/atoms/custom.rb +110 -0
- data/lib/parsanol/atoms/cut.rb +62 -0
- data/lib/parsanol/atoms/dsl.rb +130 -0
- data/lib/parsanol/atoms/dynamic.rb +33 -0
- data/lib/parsanol/atoms/entity.rb +55 -0
- data/lib/parsanol/atoms/ignored.rb +28 -0
- data/lib/parsanol/atoms/infix.rb +121 -0
- data/lib/parsanol/atoms/lookahead.rb +64 -0
- data/lib/parsanol/atoms/named.rb +50 -0
- data/lib/parsanol/atoms/re.rb +61 -0
- data/lib/parsanol/atoms/repetition.rb +241 -0
- data/lib/parsanol/atoms/scope.rb +28 -0
- data/lib/parsanol/atoms/sequence.rb +157 -0
- data/lib/parsanol/atoms/str.rb +90 -0
- data/lib/parsanol/atoms/visitor.rb +91 -0
- data/lib/parsanol/atoms.rb +36 -0
- data/lib/parsanol/buffer.rb +130 -0
- data/lib/parsanol/builder_callbacks.rb +353 -0
- data/lib/parsanol/cause.rb +101 -0
- data/lib/parsanol/context.rb +23 -0
- data/lib/parsanol/convenience.rb +35 -0
- data/lib/parsanol/edit_tracker.rb +107 -0
- data/lib/parsanol/error_reporter/contextual.rb +122 -0
- data/lib/parsanol/error_reporter/deepest.rb +106 -0
- data/lib/parsanol/error_reporter/tree.rb +68 -0
- data/lib/parsanol/error_reporter.rb +98 -0
- data/lib/parsanol/export.rb +163 -0
- data/lib/parsanol/expression/treetop.rb +94 -0
- data/lib/parsanol/expression.rb +51 -0
- data/lib/parsanol/fast_mode.rb +145 -0
- data/lib/parsanol/first_set.rb +75 -0
- data/lib/parsanol/grammar_builder.rb +177 -0
- data/lib/parsanol/graphviz.rb +97 -0
- data/lib/parsanol/incremental_parser.rb +179 -0
- data/lib/parsanol/interval_tree.rb +215 -0
- data/lib/parsanol/lazy_result.rb +178 -0
- data/lib/parsanol/lexer.rb +146 -0
- data/lib/parsanol/native/parser.rb +630 -0
- data/lib/parsanol/native/serializer.rb +245 -0
- data/lib/parsanol/native/transformer.rb +438 -0
- data/lib/parsanol/native/types.rb +41 -0
- data/lib/parsanol/native.rb +217 -0
- data/lib/parsanol/optimizer.rb +86 -0
- data/lib/parsanol/optimizers/choice_optimizer.rb +78 -0
- data/lib/parsanol/optimizers/cut_inserter.rb +175 -0
- data/lib/parsanol/optimizers/lookahead_optimizer.rb +58 -0
- data/lib/parsanol/optimizers/quantifier_optimizer.rb +62 -0
- data/lib/parsanol/optimizers/sequence_optimizer.rb +97 -0
- data/lib/parsanol/options/ruby_transform.rb +109 -0
- data/lib/parsanol/options/serialized.rb +94 -0
- data/lib/parsanol/options/zero_copy.rb +130 -0
- data/lib/parsanol/options.rb +20 -0
- data/lib/parsanol/parallel.rb +133 -0
- data/lib/parsanol/parsanol_native.bundle +0 -0
- data/lib/parsanol/parser.rb +151 -0
- data/lib/parsanol/parslet.rb +148 -0
- data/lib/parsanol/parslet_native.bundle +0 -0
- data/lib/parsanol/pattern/binding.rb +49 -0
- data/lib/parsanol/pattern.rb +115 -0
- data/lib/parsanol/pool.rb +220 -0
- data/lib/parsanol/pools/array_pool.rb +75 -0
- data/lib/parsanol/pools/buffer_pool.rb +173 -0
- data/lib/parsanol/pools/position_pool.rb +92 -0
- data/lib/parsanol/pools/slice_pool.rb +64 -0
- data/lib/parsanol/position.rb +89 -0
- data/lib/parsanol/result.rb +44 -0
- data/lib/parsanol/result_builder.rb +208 -0
- data/lib/parsanol/result_stream.rb +262 -0
- data/lib/parsanol/rig/rspec.rb +52 -0
- data/lib/parsanol/rope.rb +78 -0
- data/lib/parsanol/scope.rb +42 -0
- data/lib/parsanol/slice.rb +172 -0
- data/lib/parsanol/source/line_cache.rb +99 -0
- data/lib/parsanol/source.rb +171 -0
- data/lib/parsanol/source_location.rb +164 -0
- data/lib/parsanol/streaming_parser.rb +124 -0
- data/lib/parsanol/string_view.rb +192 -0
- data/lib/parsanol/transform.rb +267 -0
- data/lib/parsanol/version.rb +5 -0
- data/lib/parsanol/wasm/README.md +80 -0
- data/lib/parsanol/wasm/package.json +51 -0
- data/lib/parsanol/wasm/parsanol.js +252 -0
- data/lib/parsanol/wasm/parslet.d.ts +129 -0
- data/lib/parsanol/wasm_parser.rb +239 -0
- data/lib/parsanol.rb +408 -0
- data/parsanol-ruby.gemspec +56 -0
- data/spec/acceptance/examples_spec.rb +96 -0
- data/spec/acceptance/infix_parser_spec.rb +145 -0
- data/spec/acceptance/mixing_parsers_spec.rb +74 -0
- data/spec/acceptance/regression_spec.rb +329 -0
- data/spec/acceptance/repetition_and_maybe_spec.rb +44 -0
- data/spec/acceptance/unconsumed_input_spec.rb +21 -0
- data/spec/benchmark/comparative/runner_spec.rb +105 -0
- data/spec/integration/array_pooling_spec.rb +193 -0
- data/spec/integration/buffer_allocation_spec.rb +324 -0
- data/spec/integration/position_pooling_spec.rb +184 -0
- data/spec/integration/result_builder_spec.rb +282 -0
- data/spec/integration/rope_stringview_integration_spec.rb +188 -0
- data/spec/integration/slice_pooling_spec.rb +63 -0
- data/spec/integration/string_view_integration_spec.rb +125 -0
- data/spec/lexer_spec.rb +231 -0
- data/spec/parsanol/atom_results_spec.rb +39 -0
- data/spec/parsanol/atoms/alternative_spec.rb +26 -0
- data/spec/parsanol/atoms/base_spec.rb +127 -0
- data/spec/parsanol/atoms/capture_spec.rb +21 -0
- data/spec/parsanol/atoms/combinations_spec.rb +5 -0
- data/spec/parsanol/atoms/custom_spec.rb +79 -0
- data/spec/parsanol/atoms/dsl_spec.rb +7 -0
- data/spec/parsanol/atoms/entity_spec.rb +77 -0
- data/spec/parsanol/atoms/ignored_spec.rb +15 -0
- data/spec/parsanol/atoms/infix_spec.rb +5 -0
- data/spec/parsanol/atoms/lookahead_spec.rb +22 -0
- data/spec/parsanol/atoms/named_spec.rb +4 -0
- data/spec/parsanol/atoms/re_spec.rb +14 -0
- data/spec/parsanol/atoms/repetition_spec.rb +24 -0
- data/spec/parsanol/atoms/scope_spec.rb +26 -0
- data/spec/parsanol/atoms/sequence_spec.rb +28 -0
- data/spec/parsanol/atoms/str_spec.rb +15 -0
- data/spec/parsanol/atoms/visitor_spec.rb +101 -0
- data/spec/parsanol/atoms_spec.rb +488 -0
- data/spec/parsanol/auto_optimize_spec.rb +334 -0
- data/spec/parsanol/buffer_spec.rb +219 -0
- data/spec/parsanol/builder_callbacks_spec.rb +377 -0
- data/spec/parsanol/choice_optimizer_spec.rb +231 -0
- data/spec/parsanol/convenience_spec.rb +54 -0
- data/spec/parsanol/cut_inserter_spec.rb +248 -0
- data/spec/parsanol/cut_spec.rb +66 -0
- data/spec/parsanol/edit_tracker_spec.rb +218 -0
- data/spec/parsanol/error_reporter/contextual_spec.rb +122 -0
- data/spec/parsanol/error_reporter/deepest_spec.rb +82 -0
- data/spec/parsanol/error_reporter/tree_spec.rb +7 -0
- data/spec/parsanol/export_spec.rb +67 -0
- data/spec/parsanol/expression/treetop_spec.rb +75 -0
- data/spec/parsanol/first_set_spec.rb +298 -0
- data/spec/parsanol/interval_tree_spec.rb +205 -0
- data/spec/parsanol/lazy_result_spec.rb +288 -0
- data/spec/parsanol/lookahead_optimizer_spec.rb +252 -0
- data/spec/parsanol/minilisp.citrus +29 -0
- data/spec/parsanol/minilisp.tt +29 -0
- data/spec/parsanol/optimizer_spec.rb +459 -0
- data/spec/parsanol/options/parslet_compat_spec.rb +166 -0
- data/spec/parsanol/options/ruby_transform_spec.rb +70 -0
- data/spec/parsanol/options/serialized_spec.rb +69 -0
- data/spec/parsanol/options/zero_copy_spec.rb +230 -0
- data/spec/parsanol/parser_spec.rb +36 -0
- data/spec/parsanol/parslet_spec.rb +38 -0
- data/spec/parsanol/pattern_spec.rb +272 -0
- data/spec/parsanol/pool_spec.rb +392 -0
- data/spec/parsanol/pools/array_pool_spec.rb +356 -0
- data/spec/parsanol/pools/buffer_pool_spec.rb +365 -0
- data/spec/parsanol/pools/position_pool_spec.rb +118 -0
- data/spec/parsanol/pools/slice_pool_spec.rb +262 -0
- data/spec/parsanol/position_spec.rb +14 -0
- data/spec/parsanol/result_builder_spec.rb +391 -0
- data/spec/parsanol/rig/rspec_spec.rb +54 -0
- data/spec/parsanol/rope_spec.rb +207 -0
- data/spec/parsanol/scope_spec.rb +45 -0
- data/spec/parsanol/slice_spec.rb +249 -0
- data/spec/parsanol/source/line_cache_spec.rb +74 -0
- data/spec/parsanol/source_spec.rb +207 -0
- data/spec/parsanol/string_view_spec.rb +345 -0
- data/spec/parsanol/transform/context_spec.rb +56 -0
- data/spec/parsanol/transform_spec.rb +183 -0
- data/spec/parsanol/tree_memoization_spec.rb +149 -0
- data/spec/parslet_compatibility/expressir_edge_cases_spec.rb +153 -0
- data/spec/parslet_compatibility/minimal_reproduction.rb +199 -0
- data/spec/parslet_compatibility_spec.rb +399 -0
- data/spec/parslet_imported/atom_spec.rb +93 -0
- data/spec/parslet_imported/combinator_spec.rb +161 -0
- data/spec/parslet_imported/spec_helper.rb +73 -0
- data/spec/performance/batch_parsing_benchmark.rb +129 -0
- data/spec/performance/complete_optimization_summary.rb +143 -0
- data/spec/performance/grammar_caching_analysis.rb +121 -0
- data/spec/performance/grammar_caching_benchmark.rb +80 -0
- data/spec/performance/native_benchmark_spec.rb +230 -0
- data/spec/performance/phase5_benchmark.rb +144 -0
- data/spec/performance/profiling_benchmark.rb +131 -0
- data/spec/performance/ruby_improvements_benchmark.rb +171 -0
- data/spec/performance_spec.rb +374 -0
- data/spec/spec_helper.rb +79 -0
- data/spec/support/opal.rb +8 -0
- data/spec/support/opal.rb.erb +14 -0
- metadata +485 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@parsanol/wasm",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "High-performance PEG parser using WebAssembly for Opal and JavaScript",
|
|
5
|
+
"main": "parsanol.js",
|
|
6
|
+
"module": "parsanol.js",
|
|
7
|
+
"types": "parsanol.d.ts",
|
|
8
|
+
"browser": "parsanol.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"parsanol.js",
|
|
11
|
+
"parsanol.d.ts",
|
|
12
|
+
"parsanol_native.js",
|
|
13
|
+
"parsanol_native_bg.js",
|
|
14
|
+
"parsanol_native_bg.wasm",
|
|
15
|
+
"parsanol_native.d.ts"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "cd ../../ext/parsanol_native && wasm-pack build --features wasm --target web --out-dir ../../lib/parsanol/wasm",
|
|
19
|
+
"build:node": "cd ../../ext/parsanol_native && wasm-pack build --features wasm --target nodejs --out-dir ../../lib/parsanol/wasm-node",
|
|
20
|
+
"test": "node test/parsanol.test.js",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"parser",
|
|
25
|
+
"peg",
|
|
26
|
+
"parsing-expression-grammar",
|
|
27
|
+
"wasm",
|
|
28
|
+
"webassembly",
|
|
29
|
+
"opal",
|
|
30
|
+
"ruby",
|
|
31
|
+
"performance"
|
|
32
|
+
],
|
|
33
|
+
"author": "Ribose Inc. <open.source@ribose.com>",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/parsanol/parsanol-rs.git",
|
|
38
|
+
"directory": "lib/parsanol/wasm"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/parsanol/parsanol-ruby/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/parsanol/parsanol-ruby#readme",
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=14.0.0"
|
|
46
|
+
},
|
|
47
|
+
"sideEffects": [
|
|
48
|
+
"parsanol_native.js",
|
|
49
|
+
"parsanol_native_bg.js"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsanol WASM Parser
|
|
3
|
+
*
|
|
4
|
+
* High-performance parser using WebAssembly for use in browsers and Node.js.
|
|
5
|
+
* Compatible with Opal (Ruby in JavaScript) for parsing in the browser.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // Browser/ESM
|
|
9
|
+
* import { ParsanolParser } from '@parsanol/wasm';
|
|
10
|
+
* const parser = new ParsanolParser(grammarJson);
|
|
11
|
+
* const result = parser.parse('input text');
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Node.js
|
|
15
|
+
* const { ParsanolParser } = require('@parsanol/wasm');
|
|
16
|
+
* const parser = new ParsanolParser(grammarJson);
|
|
17
|
+
* const result = parser.parse('input text');
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Opal
|
|
21
|
+
* %x{
|
|
22
|
+
* var parser = new ParsanolNative.WasmParser(#{grammar_json});
|
|
23
|
+
* var result = parser.parse(#{input});
|
|
24
|
+
* return result;
|
|
25
|
+
* }
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import init, { WasmParser } from './parsanol_native.js';
|
|
29
|
+
|
|
30
|
+
let initialized = false;
|
|
31
|
+
let initPromise = null;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Initialize the WASM module
|
|
35
|
+
* Must be called before creating parsers (automatically called on first use)
|
|
36
|
+
*
|
|
37
|
+
* @returns {Promise<void>}
|
|
38
|
+
*/
|
|
39
|
+
export async function initParsanol() {
|
|
40
|
+
if (initialized) return;
|
|
41
|
+
if (initPromise) return initPromise;
|
|
42
|
+
|
|
43
|
+
initPromise = init().then(() => {
|
|
44
|
+
initialized = true;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return initPromise;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check if the WASM module is initialized
|
|
52
|
+
*
|
|
53
|
+
* @returns {boolean}
|
|
54
|
+
*/
|
|
55
|
+
export function isInitialized() {
|
|
56
|
+
return initialized;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* High-performance parser using WebAssembly
|
|
61
|
+
*
|
|
62
|
+
* Usage:
|
|
63
|
+
* const parser = new ParsanolParser(grammarJson);
|
|
64
|
+
* const result = parser.parse('input');
|
|
65
|
+
* console.log(result);
|
|
66
|
+
*/
|
|
67
|
+
export class ParsanolParser {
|
|
68
|
+
#parser = null;
|
|
69
|
+
#grammarJson = null;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create a new parser instance
|
|
73
|
+
*
|
|
74
|
+
* @param {string|object} grammar - Grammar JSON string or object
|
|
75
|
+
* @throws {Error} If WASM not initialized or grammar is invalid
|
|
76
|
+
*/
|
|
77
|
+
constructor(grammar) {
|
|
78
|
+
if (!initialized) {
|
|
79
|
+
throw new Error('Parsanol WASM not initialized. Call initParsanol() first.');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.#grammarJson = typeof grammar === 'string' ? grammar : JSON.stringify(grammar);
|
|
83
|
+
this.#parser = new WasmParser(this.#grammarJson);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Parse input string and return AST
|
|
88
|
+
*
|
|
89
|
+
* @param {string} input - Input string to parse
|
|
90
|
+
* @returns {object} Parsed AST as JavaScript object
|
|
91
|
+
* @throws {Error} If parsing fails
|
|
92
|
+
*/
|
|
93
|
+
parse(input) {
|
|
94
|
+
return this.#parser.parse(input);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Parse input and return flat array format
|
|
99
|
+
* More efficient for large results (avoids object creation)
|
|
100
|
+
*
|
|
101
|
+
* @param {string} input - Input string to parse
|
|
102
|
+
* @returns {BigUint64Array} Flat array with tagged values
|
|
103
|
+
* @throws {Error} If parsing fails
|
|
104
|
+
*/
|
|
105
|
+
parseFlat(input) {
|
|
106
|
+
return this.#parser.parse_flat(input);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Parse input and return JSON string
|
|
111
|
+
* Useful for transferring to other contexts
|
|
112
|
+
*
|
|
113
|
+
* @param {string} input - Input string to parse
|
|
114
|
+
* @returns {string} JSON string of parsed AST
|
|
115
|
+
* @throws {Error} If parsing fails
|
|
116
|
+
*/
|
|
117
|
+
parseJson(input) {
|
|
118
|
+
return this.#parser.parse_json(input);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Reset parser state for reuse
|
|
123
|
+
*/
|
|
124
|
+
reset() {
|
|
125
|
+
// Parser state is reset automatically on each parse
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Decode flat array format to JavaScript object
|
|
131
|
+
*
|
|
132
|
+
* Tag format:
|
|
133
|
+
* - 0x00: nil
|
|
134
|
+
* - 0x01: bool (followed by 0 or 1)
|
|
135
|
+
* - 0x02: int (followed by value)
|
|
136
|
+
* - 0x03: float (followed by bits)
|
|
137
|
+
* - 0x04: string (followed by offset, length)
|
|
138
|
+
* - 0x05: array start
|
|
139
|
+
* - 0x06: array end
|
|
140
|
+
* - 0x07: hash start
|
|
141
|
+
* - 0x08: hash end
|
|
142
|
+
* - 0x09: hash key
|
|
143
|
+
*
|
|
144
|
+
* @param {BigUint64Array} flat - Flat array from parseFlat()
|
|
145
|
+
* @param {string} input - Original input string for string references
|
|
146
|
+
* @returns {any} Decoded JavaScript value
|
|
147
|
+
*/
|
|
148
|
+
export function decodeFlatArray(flat, input) {
|
|
149
|
+
const TAG_NIL = 0x00n;
|
|
150
|
+
const TAG_BOOL = 0x01n;
|
|
151
|
+
const TAG_INT = 0x02n;
|
|
152
|
+
const TAG_FLOAT = 0x03n;
|
|
153
|
+
const TAG_STRING = 0x04n;
|
|
154
|
+
const TAG_ARRAY_START = 0x05n;
|
|
155
|
+
const TAG_ARRAY_END = 0x06n;
|
|
156
|
+
const TAG_HASH_START = 0x07n;
|
|
157
|
+
const TAG_HASH_END = 0x08n;
|
|
158
|
+
const TAG_HASH_KEY = 0x09n;
|
|
159
|
+
|
|
160
|
+
let pos = 0;
|
|
161
|
+
|
|
162
|
+
function decode() {
|
|
163
|
+
const tag = flat[pos++];
|
|
164
|
+
|
|
165
|
+
switch (tag) {
|
|
166
|
+
case TAG_NIL:
|
|
167
|
+
return null;
|
|
168
|
+
|
|
169
|
+
case TAG_BOOL:
|
|
170
|
+
return flat[pos++] !== 0n;
|
|
171
|
+
|
|
172
|
+
case TAG_INT:
|
|
173
|
+
return Number(flat[pos++]);
|
|
174
|
+
|
|
175
|
+
case TAG_FLOAT: {
|
|
176
|
+
const bits = flat[pos++];
|
|
177
|
+
return new Float64Array(new BigUint64Array([bits]).buffer)[0];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
case TAG_STRING: {
|
|
181
|
+
const offset = Number(flat[pos++]);
|
|
182
|
+
const length = Number(flat[pos++]);
|
|
183
|
+
return input.substring(offset, offset + length);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
case TAG_ARRAY_START: {
|
|
187
|
+
const arr = [];
|
|
188
|
+
while (flat[pos] !== TAG_ARRAY_END) {
|
|
189
|
+
arr.push(decode());
|
|
190
|
+
}
|
|
191
|
+
pos++; // Skip ARRAY_END
|
|
192
|
+
return arr;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
case TAG_HASH_START: {
|
|
196
|
+
const obj = {};
|
|
197
|
+
while (flat[pos] !== TAG_HASH_END) {
|
|
198
|
+
// Skip TAG_HASH_KEY
|
|
199
|
+
pos++;
|
|
200
|
+
|
|
201
|
+
// Read key
|
|
202
|
+
const keyLen = Number(flat[pos++]);
|
|
203
|
+
// Skip placeholder
|
|
204
|
+
pos++;
|
|
205
|
+
|
|
206
|
+
// Read key bytes
|
|
207
|
+
let key = '';
|
|
208
|
+
const numChunks = Math.ceil(keyLen / 8);
|
|
209
|
+
for (let i = 0; i < numChunks; i++) {
|
|
210
|
+
const chunk = flat[pos++];
|
|
211
|
+
for (let j = 0; j < 8 && key.length < keyLen; j++) {
|
|
212
|
+
const byte = Number((chunk >> BigInt(j * 8)) & 0xffn);
|
|
213
|
+
key += String.fromCharCode(byte);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Read value
|
|
218
|
+
const value = decode();
|
|
219
|
+
obj[key] = value;
|
|
220
|
+
}
|
|
221
|
+
pos++; // Skip HASH_END
|
|
222
|
+
return obj;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
default:
|
|
226
|
+
throw new Error(`Unknown tag: ${tag}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return decode();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Create a parser with automatic initialization
|
|
235
|
+
* Convenience function that handles async initialization
|
|
236
|
+
*
|
|
237
|
+
* @param {string|object} grammar - Grammar JSON string or object
|
|
238
|
+
* @returns {Promise<ParsanolParser>} Initialized parser
|
|
239
|
+
*/
|
|
240
|
+
export async function createParser(grammar) {
|
|
241
|
+
await initParsanol();
|
|
242
|
+
return new ParsanolParser(grammar);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Default export
|
|
246
|
+
export default {
|
|
247
|
+
initParsanol,
|
|
248
|
+
isInitialized,
|
|
249
|
+
ParsanolParser,
|
|
250
|
+
decodeFlatArray,
|
|
251
|
+
createParser
|
|
252
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for Parslet WASM Parser
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Initialize the WASM module
|
|
9
|
+
* Must be called before creating parsers
|
|
10
|
+
*/
|
|
11
|
+
export function initParslet(): Promise<void>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if the WASM module is initialized
|
|
15
|
+
*/
|
|
16
|
+
export function isInitialized(): boolean;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Grammar specification for the parser
|
|
20
|
+
*
|
|
21
|
+
* The grammar is a JSON object with atoms and a root index.
|
|
22
|
+
*/
|
|
23
|
+
export interface Grammar {
|
|
24
|
+
/** Array of atom definitions */
|
|
25
|
+
atoms: Atom[];
|
|
26
|
+
/** Index of the root atom */
|
|
27
|
+
root: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Atom types in the grammar
|
|
32
|
+
*/
|
|
33
|
+
export type Atom =
|
|
34
|
+
| { Str: { pattern: string } }
|
|
35
|
+
| { Re: { pattern: string } }
|
|
36
|
+
| { Sequence: { atoms: number[] } }
|
|
37
|
+
| { Alternative: { atoms: number[] } }
|
|
38
|
+
| { Repetition: { atom: number; min: number; max: number | null } }
|
|
39
|
+
| { Named: { name: string; atom: number } }
|
|
40
|
+
| { Entity: { atom: number } }
|
|
41
|
+
| { Lookahead: { atom: number; positive: boolean } }
|
|
42
|
+
| 'Cut';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse result - can be various types
|
|
46
|
+
*/
|
|
47
|
+
export type ParseResult =
|
|
48
|
+
| null
|
|
49
|
+
| boolean
|
|
50
|
+
| number
|
|
51
|
+
| string
|
|
52
|
+
| ParseResult[]
|
|
53
|
+
| { [key: string]: ParseResult };
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* High-performance parser using WebAssembly
|
|
57
|
+
*/
|
|
58
|
+
export class ParsletParser {
|
|
59
|
+
/**
|
|
60
|
+
* Create a new parser instance
|
|
61
|
+
*
|
|
62
|
+
* @param grammar - Grammar JSON string or object
|
|
63
|
+
* @throws {Error} If WASM not initialized or grammar is invalid
|
|
64
|
+
*/
|
|
65
|
+
constructor(grammar: string | Grammar);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Parse input string and return AST
|
|
69
|
+
*
|
|
70
|
+
* @param input - Input string to parse
|
|
71
|
+
* @returns Parsed AST as JavaScript object
|
|
72
|
+
* @throws {Error} If parsing fails
|
|
73
|
+
*/
|
|
74
|
+
parse(input: string): ParseResult;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Parse input and return flat array format
|
|
78
|
+
*
|
|
79
|
+
* @param input - Input string to parse
|
|
80
|
+
* @returns Flat array with tagged values
|
|
81
|
+
* @throws {Error} If parsing fails
|
|
82
|
+
*/
|
|
83
|
+
parseFlat(input: string): BigUint64Array;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Parse input and return JSON string
|
|
87
|
+
*
|
|
88
|
+
* @param input - Input string to parse
|
|
89
|
+
* @returns JSON string of parsed AST
|
|
90
|
+
* @throws {Error} If parsing fails
|
|
91
|
+
*/
|
|
92
|
+
parseJson(input: string): string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Decode flat array format to JavaScript object
|
|
97
|
+
*
|
|
98
|
+
* @param flat - Flat array from parseFlat()
|
|
99
|
+
* @param input - Original input string for string references
|
|
100
|
+
* @returns Decoded JavaScript value
|
|
101
|
+
*/
|
|
102
|
+
export function decodeFlatArray(flat: BigUint64Array, input: string): ParseResult;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Create a parser with automatic initialization
|
|
106
|
+
*
|
|
107
|
+
* @param grammar - Grammar JSON string or object
|
|
108
|
+
* @returns Promise resolving to initialized parser
|
|
109
|
+
*/
|
|
110
|
+
export function createParser(grammar: string | Grammar): Promise<ParsletParser>;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Low-level WASM parser (from wasm-bindgen)
|
|
114
|
+
*
|
|
115
|
+
* @internal
|
|
116
|
+
*/
|
|
117
|
+
export class WasmParser {
|
|
118
|
+
constructor(grammarJson: string);
|
|
119
|
+
parse(input: string): any;
|
|
120
|
+
parse_flat(input: string): BigUint64Array;
|
|
121
|
+
parse_json(input: string): string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* WASM module initialization function
|
|
126
|
+
*
|
|
127
|
+
* @internal
|
|
128
|
+
*/
|
|
129
|
+
export default function init(): Promise<void>;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Parsanol
|
|
4
|
+
# WASM-based parser for Opal environments
|
|
5
|
+
#
|
|
6
|
+
# This class provides a bridge between Opal (Ruby compiled to JavaScript)
|
|
7
|
+
# and the WASM parser. It uses the Parslet WASM module.
|
|
8
|
+
#
|
|
9
|
+
# @example In Opal environment
|
|
10
|
+
# # First, ensure WASM is loaded (in your HTML/JS)
|
|
11
|
+
# # <script src="parslet_wasm.js"></script>
|
|
12
|
+
# # <script>
|
|
13
|
+
# # ParsletWasm.init().then(() => console.log('ready'));
|
|
14
|
+
# # </script>
|
|
15
|
+
#
|
|
16
|
+
# # Then in Ruby/Opal:
|
|
17
|
+
# grammar_json = parser.to_json
|
|
18
|
+
# wasm_parser = Parsanol::WasmParser.new(grammar_json)
|
|
19
|
+
# result = wasm_parser.parse(input)
|
|
20
|
+
#
|
|
21
|
+
class WasmParser
|
|
22
|
+
# Tags for flat array format
|
|
23
|
+
TAG_NIL = 0x00
|
|
24
|
+
TAG_BOOL = 0x01
|
|
25
|
+
TAG_INT = 0x02
|
|
26
|
+
TAG_FLOAT = 0x03
|
|
27
|
+
TAG_STRING = 0x04
|
|
28
|
+
TAG_ARRAY_START = 0x05
|
|
29
|
+
TAG_ARRAY_END = 0x06
|
|
30
|
+
TAG_HASH_START = 0x07
|
|
31
|
+
TAG_HASH_END = 0x08
|
|
32
|
+
TAG_HASH_KEY = 0x09
|
|
33
|
+
|
|
34
|
+
# @return [String] The grammar JSON
|
|
35
|
+
attr_reader :grammar_json
|
|
36
|
+
|
|
37
|
+
# Create a new WASM parser
|
|
38
|
+
#
|
|
39
|
+
# @param grammar_json [String, Hash] Grammar JSON string or hash
|
|
40
|
+
# @raise [RuntimeError] If WASM is not initialized
|
|
41
|
+
#
|
|
42
|
+
def initialize(grammar_json)
|
|
43
|
+
@grammar_json = grammar_json.is_a?(Hash) ? grammar_json.to_json : grammar_json
|
|
44
|
+
@parser = nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Parse input string and return AST
|
|
48
|
+
#
|
|
49
|
+
# @param input [String] Input string to parse
|
|
50
|
+
# @return [Hash, Array, String, nil] Parsed AST
|
|
51
|
+
# @raise [RuntimeError] If parsing fails
|
|
52
|
+
#
|
|
53
|
+
def parse(input)
|
|
54
|
+
ensure_initialized
|
|
55
|
+
result = `#{@parser}.parse(#{input})`
|
|
56
|
+
convert_js_to_ruby(result)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Parse input and return flat array (more efficient for large results)
|
|
60
|
+
#
|
|
61
|
+
# @param input [String] Input string to parse
|
|
62
|
+
# @return [Array] Flat array with tagged values
|
|
63
|
+
# @raise [RuntimeError] If parsing fails
|
|
64
|
+
#
|
|
65
|
+
def parse_flat(input)
|
|
66
|
+
ensure_initialized
|
|
67
|
+
flat = `#{@parser}.parseFlat(#{input})`
|
|
68
|
+
decode_flat(flat, input)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Parse input and return JSON string
|
|
72
|
+
#
|
|
73
|
+
# @param input [String] Input string to parse
|
|
74
|
+
# @return [String] JSON string of parsed AST
|
|
75
|
+
# @raise [RuntimeError] If parsing fails
|
|
76
|
+
#
|
|
77
|
+
def parse_json(input)
|
|
78
|
+
ensure_initialized
|
|
79
|
+
`#{@parser}.parseJson(#{input})`
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Check if WASM is available and initialized
|
|
83
|
+
#
|
|
84
|
+
# @return [Boolean]
|
|
85
|
+
#
|
|
86
|
+
def self.available?
|
|
87
|
+
%x{
|
|
88
|
+
if (typeof ParsletWasm === 'undefined') {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
return ParsletWasm.isInitialized ? ParsletWasm.isInitialized() : false;
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Initialize WASM module (async)
|
|
96
|
+
#
|
|
97
|
+
# @return [Promise] Promise that resolves when WASM is ready
|
|
98
|
+
#
|
|
99
|
+
def self.init
|
|
100
|
+
%x{
|
|
101
|
+
if (typeof ParsletWasm !== 'undefined' && ParsletWasm.initParslet) {
|
|
102
|
+
return ParsletWasm.initParslet();
|
|
103
|
+
}
|
|
104
|
+
return Promise.reject(new Error('ParsletWasm not loaded'));
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
def ensure_initialized
|
|
111
|
+
return if @parser
|
|
112
|
+
|
|
113
|
+
%x{
|
|
114
|
+
if (typeof ParsletWasm === 'undefined') {
|
|
115
|
+
throw new Error('ParsletWasm not loaded. Include parslet.js and parsanol_native_bg.wasm');
|
|
116
|
+
}
|
|
117
|
+
if (!ParsletWasm.isInitialized || !ParsletWasm.isInitialized()) {
|
|
118
|
+
throw new Error('WASM not initialized. Call Parsanol::WasmParser.init first');
|
|
119
|
+
}
|
|
120
|
+
#{@parser} = new ParsletWasm.ParsletParser(#{@grammar_json});
|
|
121
|
+
}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Convert JavaScript result to Ruby
|
|
125
|
+
def convert_js_to_ruby(js_obj)
|
|
126
|
+
%x{
|
|
127
|
+
if (js_obj === null || js_obj === undefined) {
|
|
128
|
+
return nil;
|
|
129
|
+
}
|
|
130
|
+
if (typeof js_obj === 'boolean') {
|
|
131
|
+
return js_obj;
|
|
132
|
+
}
|
|
133
|
+
if (typeof js_obj === 'number') {
|
|
134
|
+
return js_obj;
|
|
135
|
+
}
|
|
136
|
+
if (typeof js_obj === 'string') {
|
|
137
|
+
return js_obj;
|
|
138
|
+
}
|
|
139
|
+
if (Array.isArray(js_obj)) {
|
|
140
|
+
return js_obj.map(function(item) {
|
|
141
|
+
return #{convert_js_to_ruby(`item`)};
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (typeof js_obj === 'object') {
|
|
145
|
+
var hash = {};
|
|
146
|
+
Object.keys(js_obj).forEach(function(key) {
|
|
147
|
+
hash[key] = #{convert_js_to_ruby(`js_obj[key]`)};
|
|
148
|
+
});
|
|
149
|
+
return hash;
|
|
150
|
+
}
|
|
151
|
+
return nil;
|
|
152
|
+
}
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Decode flat array format to Ruby objects
|
|
156
|
+
def decode_flat(flat, input)
|
|
157
|
+
stack = []
|
|
158
|
+
i = 0
|
|
159
|
+
length = `#{flat}.length`
|
|
160
|
+
|
|
161
|
+
while i < length
|
|
162
|
+
tag = `#{flat}[#{i}]`
|
|
163
|
+
|
|
164
|
+
case tag
|
|
165
|
+
when TAG_NIL
|
|
166
|
+
stack << nil
|
|
167
|
+
i += 1
|
|
168
|
+
when TAG_BOOL
|
|
169
|
+
stack << (`#{flat}[#{i + 1}]` != 0)
|
|
170
|
+
i += 2
|
|
171
|
+
when TAG_INT
|
|
172
|
+
stack << `#{flat}[#{i + 1}]`
|
|
173
|
+
i += 2
|
|
174
|
+
when TAG_FLOAT
|
|
175
|
+
bits = `#{flat}[#{i + 1}]`
|
|
176
|
+
float = `new Float64Array(new BigUint64Array([#{bits}]).buffer)[0]`
|
|
177
|
+
stack << float
|
|
178
|
+
i += 2
|
|
179
|
+
when TAG_STRING
|
|
180
|
+
offset = `#{flat}[#{i + 1}]`
|
|
181
|
+
len = `#{flat}[#{i + 2}]`
|
|
182
|
+
stack << input.byteslice(offset, len)
|
|
183
|
+
i += 3
|
|
184
|
+
when TAG_ARRAY_START
|
|
185
|
+
stack << :array_marker
|
|
186
|
+
i += 1
|
|
187
|
+
when TAG_ARRAY_END
|
|
188
|
+
items = []
|
|
189
|
+
items.unshift(stack.pop) while stack.last != :array_marker
|
|
190
|
+
stack.pop # Remove marker
|
|
191
|
+
stack << items
|
|
192
|
+
i += 1
|
|
193
|
+
when TAG_HASH_START
|
|
194
|
+
stack << :hash_marker
|
|
195
|
+
i += 1
|
|
196
|
+
when TAG_HASH_END
|
|
197
|
+
pairs = []
|
|
198
|
+
while stack.last != :hash_marker
|
|
199
|
+
value = stack.pop
|
|
200
|
+
key = stack.pop
|
|
201
|
+
pairs.unshift([key, value])
|
|
202
|
+
end
|
|
203
|
+
stack.pop # Remove marker
|
|
204
|
+
stack << pairs.to_h
|
|
205
|
+
i += 1
|
|
206
|
+
when TAG_HASH_KEY
|
|
207
|
+
len = `#{flat}[#{i + 1}]`
|
|
208
|
+
i += 3 # Skip tag, len, and placeholder
|
|
209
|
+
# Read key bytes
|
|
210
|
+
key_bytes = []
|
|
211
|
+
chunks = (len + 7) / 8
|
|
212
|
+
chunks.times do |j|
|
|
213
|
+
chunk = `#{flat}[#{i + j}]`
|
|
214
|
+
8.times do |k|
|
|
215
|
+
break if key_bytes.length >= len
|
|
216
|
+
key_bytes << ((chunk >> (k * 8)) & 0xff)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
i += chunks
|
|
220
|
+
key = key_bytes.pack('C*').force_encoding('UTF-8')
|
|
221
|
+
stack << key
|
|
222
|
+
else
|
|
223
|
+
raise "Unknown tag: #{tag} at index #{i}"
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
stack.first
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Factory method to create appropriate parser
|
|
232
|
+
#
|
|
233
|
+
# @param grammar_json [String, Hash] Grammar JSON
|
|
234
|
+
# @return [WasmParser, Object] Appropriate parser for current environment
|
|
235
|
+
#
|
|
236
|
+
def self.create_wasm_parser(grammar_json)
|
|
237
|
+
WasmParser.new(grammar_json)
|
|
238
|
+
end
|
|
239
|
+
end
|