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.

Files changed (336) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY.txt +25 -0
  3. data/LICENSE +23 -0
  4. data/README.adoc +643 -0
  5. data/Rakefile +189 -0
  6. data/example/balanced-parens/basic.rb +42 -0
  7. data/example/balanced-parens/basic.rb.md +86 -0
  8. data/example/balanced-parens/parens.rb +42 -0
  9. data/example/balanced-parens/ruby_transform.rb +162 -0
  10. data/example/big.erb +73 -0
  11. data/example/boolean-algebra/basic.rb +70 -0
  12. data/example/boolean-algebra/basic.rb.md +108 -0
  13. data/example/boolean-algebra/ruby_transform.rb +263 -0
  14. data/example/calculator/basic.rb +153 -0
  15. data/example/calculator/basic.rb.md +120 -0
  16. data/example/calculator/pattern.rb +153 -0
  17. data/example/calculator/ruby_transform.rb +156 -0
  18. data/example/calculator/ruby_transform.rb.md +32 -0
  19. data/example/calculator/serialized.rb +257 -0
  20. data/example/calculator/serialized.rb.md +32 -0
  21. data/example/calculator/transform.rb +153 -0
  22. data/example/calculator/zero_copy.rb +269 -0
  23. data/example/calculator/zero_copy.rb.md +36 -0
  24. data/example/capture/basic.rb +49 -0
  25. data/example/capture/basic.rb.md +106 -0
  26. data/example/capture/example.json +39 -0
  27. data/example/comments/basic.rb +35 -0
  28. data/example/comments/basic.rb.md +110 -0
  29. data/example/csv/ruby_transform.rb +148 -0
  30. data/example/csv/ruby_transform.rb.md +131 -0
  31. data/example/csv/serialized.rb +201 -0
  32. data/example/csv/serialized.rb.md +31 -0
  33. data/example/csv/zero_copy.rb +276 -0
  34. data/example/csv/zero_copy.rb.md +36 -0
  35. data/example/custom_atoms/indent_atom.rb +79 -0
  36. data/example/deepest-errors/basic.rb +131 -0
  37. data/example/deepest-errors/basic.rb.md +152 -0
  38. data/example/documentation/basic.rb +18 -0
  39. data/example/documentation/basic.rb.md +97 -0
  40. data/example/email/basic.rb +55 -0
  41. data/example/email/basic.rb.md +102 -0
  42. data/example/email/ruby_transform.rb +106 -0
  43. data/example/empty/basic.rb +13 -0
  44. data/example/empty/basic.rb.md +73 -0
  45. data/example/empty/example.json +38 -0
  46. data/example/erb/basic.rb +47 -0
  47. data/example/erb/basic.rb.md +103 -0
  48. data/example/erb/optimized.rb +42 -0
  49. data/example/error-reporting/basic.rb +132 -0
  50. data/example/error-reporting/basic.rb.md +122 -0
  51. data/example/expression-evaluator/basic.rb +284 -0
  52. data/example/expression-evaluator/basic.rb.md +138 -0
  53. data/example/ini/basic.rb +154 -0
  54. data/example/ini/basic.rb.md +129 -0
  55. data/example/ini/ruby_transform.rb +154 -0
  56. data/example/ip-address/basic.rb +125 -0
  57. data/example/ip-address/basic.rb.md +139 -0
  58. data/example/iso-6709/basic.rb +231 -0
  59. data/example/iso-6709/basic.rb.md +143 -0
  60. data/example/iso-8601/basic.rb +275 -0
  61. data/example/iso-8601/basic.rb.md +149 -0
  62. data/example/json/basic.rb +128 -0
  63. data/example/json/basic.rb.md +121 -0
  64. data/example/json/pattern.rb +128 -0
  65. data/example/json/ruby_transform.rb +200 -0
  66. data/example/json/ruby_transform.rb.md +32 -0
  67. data/example/json/serialized.rb +233 -0
  68. data/example/json/serialized.rb.md +31 -0
  69. data/example/json/transform.rb +128 -0
  70. data/example/json/zero_copy.rb +316 -0
  71. data/example/json/zero_copy.rb.md +36 -0
  72. data/example/local/basic.rb +34 -0
  73. data/example/local/basic.rb.md +91 -0
  74. data/example/local/example.json +38 -0
  75. data/example/markdown/basic.rb +287 -0
  76. data/example/markdown/basic.rb.md +160 -0
  77. data/example/markup/basic.rb +173 -0
  78. data/example/markup/basic.rb.md +118 -0
  79. data/example/mathn/basic.rb +47 -0
  80. data/example/mathn/basic.rb.md +96 -0
  81. data/example/mathn/example.json +39 -0
  82. data/example/minilisp/basic.rb +94 -0
  83. data/example/minilisp/basic.rb.md +133 -0
  84. data/example/modularity/basic.rb +47 -0
  85. data/example/modularity/basic.rb.md +152 -0
  86. data/example/nested-errors/basic.rb +132 -0
  87. data/example/nested-errors/basic.rb.md +157 -0
  88. data/example/output/boolean_algebra.out +4 -0
  89. data/example/output/calc.out +1 -0
  90. data/example/output/capture.out +3 -0
  91. data/example/output/comments.out +8 -0
  92. data/example/output/deepest_errors.out +54 -0
  93. data/example/output/documentation.err +4 -0
  94. data/example/output/documentation.out +1 -0
  95. data/example/output/email_parser.out +2 -0
  96. data/example/output/empty.err +1 -0
  97. data/example/output/erb.out +7 -0
  98. data/example/output/ignore.out +1 -0
  99. data/example/output/ignore_whitespace.out +1 -0
  100. data/example/output/ip_address.out +9 -0
  101. data/example/output/json.out +5 -0
  102. data/example/output/local.out +3 -0
  103. data/example/output/mathn.out +4 -0
  104. data/example/output/minilisp.out +5 -0
  105. data/example/output/modularity.out +0 -0
  106. data/example/output/nested_errors.out +54 -0
  107. data/example/output/optimized_erb.out +1 -0
  108. data/example/output/parens.out +8 -0
  109. data/example/output/prec_calc.out +5 -0
  110. data/example/output/readme.out +1 -0
  111. data/example/output/scopes.out +1 -0
  112. data/example/output/seasons.out +28 -0
  113. data/example/output/sentence.out +1 -0
  114. data/example/output/simple_xml.out +2 -0
  115. data/example/output/string_parser.out +3 -0
  116. data/example/prec-calc/basic.rb +71 -0
  117. data/example/prec-calc/basic.rb.md +114 -0
  118. data/example/readme/basic.rb +30 -0
  119. data/example/readme/basic.rb.md +80 -0
  120. data/example/scopes/basic.rb +15 -0
  121. data/example/scopes/basic.rb.md +73 -0
  122. data/example/scopes/example.json +38 -0
  123. data/example/seasons/basic.rb +46 -0
  124. data/example/seasons/basic.rb.md +117 -0
  125. data/example/seasons/example.json +40 -0
  126. data/example/sentence/basic.rb +36 -0
  127. data/example/sentence/basic.rb.md +81 -0
  128. data/example/sexp/ruby_transform.rb +180 -0
  129. data/example/sexp/ruby_transform.rb.md +143 -0
  130. data/example/simple-xml/basic.rb +54 -0
  131. data/example/simple-xml/basic.rb.md +125 -0
  132. data/example/simple.lit +3 -0
  133. data/example/string-literal/basic.rb +77 -0
  134. data/example/string-literal/basic.rb.md +128 -0
  135. data/example/test.lit +4 -0
  136. data/example/toml/basic.rb +226 -0
  137. data/example/toml/basic.rb.md +173 -0
  138. data/example/url/basic.rb +219 -0
  139. data/example/url/basic.rb.md +142 -0
  140. data/example/url/ruby_transform.rb +219 -0
  141. data/example/yaml/basic.rb +216 -0
  142. data/example/yaml/basic.rb.md +148 -0
  143. data/ext/parsanol_native/extconf.rb +4 -0
  144. data/lib/parsanol/accelerator/application.rb +62 -0
  145. data/lib/parsanol/accelerator/engine.rb +112 -0
  146. data/lib/parsanol/accelerator.rb +162 -0
  147. data/lib/parsanol/ast_visitor.rb +122 -0
  148. data/lib/parsanol/atoms/alternative.rb +97 -0
  149. data/lib/parsanol/atoms/base.rb +214 -0
  150. data/lib/parsanol/atoms/can_flatten.rb +192 -0
  151. data/lib/parsanol/atoms/capture.rb +41 -0
  152. data/lib/parsanol/atoms/context.rb +351 -0
  153. data/lib/parsanol/atoms/context_optimized.rb +42 -0
  154. data/lib/parsanol/atoms/custom.rb +110 -0
  155. data/lib/parsanol/atoms/cut.rb +62 -0
  156. data/lib/parsanol/atoms/dsl.rb +130 -0
  157. data/lib/parsanol/atoms/dynamic.rb +33 -0
  158. data/lib/parsanol/atoms/entity.rb +55 -0
  159. data/lib/parsanol/atoms/ignored.rb +28 -0
  160. data/lib/parsanol/atoms/infix.rb +121 -0
  161. data/lib/parsanol/atoms/lookahead.rb +64 -0
  162. data/lib/parsanol/atoms/named.rb +50 -0
  163. data/lib/parsanol/atoms/re.rb +61 -0
  164. data/lib/parsanol/atoms/repetition.rb +241 -0
  165. data/lib/parsanol/atoms/scope.rb +28 -0
  166. data/lib/parsanol/atoms/sequence.rb +157 -0
  167. data/lib/parsanol/atoms/str.rb +90 -0
  168. data/lib/parsanol/atoms/visitor.rb +91 -0
  169. data/lib/parsanol/atoms.rb +36 -0
  170. data/lib/parsanol/buffer.rb +130 -0
  171. data/lib/parsanol/builder_callbacks.rb +353 -0
  172. data/lib/parsanol/cause.rb +101 -0
  173. data/lib/parsanol/context.rb +23 -0
  174. data/lib/parsanol/convenience.rb +35 -0
  175. data/lib/parsanol/edit_tracker.rb +107 -0
  176. data/lib/parsanol/error_reporter/contextual.rb +122 -0
  177. data/lib/parsanol/error_reporter/deepest.rb +106 -0
  178. data/lib/parsanol/error_reporter/tree.rb +68 -0
  179. data/lib/parsanol/error_reporter.rb +98 -0
  180. data/lib/parsanol/export.rb +163 -0
  181. data/lib/parsanol/expression/treetop.rb +94 -0
  182. data/lib/parsanol/expression.rb +51 -0
  183. data/lib/parsanol/fast_mode.rb +145 -0
  184. data/lib/parsanol/first_set.rb +75 -0
  185. data/lib/parsanol/grammar_builder.rb +177 -0
  186. data/lib/parsanol/graphviz.rb +97 -0
  187. data/lib/parsanol/incremental_parser.rb +179 -0
  188. data/lib/parsanol/interval_tree.rb +215 -0
  189. data/lib/parsanol/lazy_result.rb +178 -0
  190. data/lib/parsanol/lexer.rb +146 -0
  191. data/lib/parsanol/native/parser.rb +630 -0
  192. data/lib/parsanol/native/serializer.rb +245 -0
  193. data/lib/parsanol/native/transformer.rb +438 -0
  194. data/lib/parsanol/native/types.rb +41 -0
  195. data/lib/parsanol/native.rb +217 -0
  196. data/lib/parsanol/optimizer.rb +86 -0
  197. data/lib/parsanol/optimizers/choice_optimizer.rb +78 -0
  198. data/lib/parsanol/optimizers/cut_inserter.rb +175 -0
  199. data/lib/parsanol/optimizers/lookahead_optimizer.rb +58 -0
  200. data/lib/parsanol/optimizers/quantifier_optimizer.rb +62 -0
  201. data/lib/parsanol/optimizers/sequence_optimizer.rb +97 -0
  202. data/lib/parsanol/options/ruby_transform.rb +109 -0
  203. data/lib/parsanol/options/serialized.rb +94 -0
  204. data/lib/parsanol/options/zero_copy.rb +130 -0
  205. data/lib/parsanol/options.rb +20 -0
  206. data/lib/parsanol/parallel.rb +133 -0
  207. data/lib/parsanol/parsanol_native.bundle +0 -0
  208. data/lib/parsanol/parser.rb +151 -0
  209. data/lib/parsanol/parslet.rb +148 -0
  210. data/lib/parsanol/parslet_native.bundle +0 -0
  211. data/lib/parsanol/pattern/binding.rb +49 -0
  212. data/lib/parsanol/pattern.rb +115 -0
  213. data/lib/parsanol/pool.rb +220 -0
  214. data/lib/parsanol/pools/array_pool.rb +75 -0
  215. data/lib/parsanol/pools/buffer_pool.rb +173 -0
  216. data/lib/parsanol/pools/position_pool.rb +92 -0
  217. data/lib/parsanol/pools/slice_pool.rb +64 -0
  218. data/lib/parsanol/position.rb +89 -0
  219. data/lib/parsanol/result.rb +44 -0
  220. data/lib/parsanol/result_builder.rb +208 -0
  221. data/lib/parsanol/result_stream.rb +262 -0
  222. data/lib/parsanol/rig/rspec.rb +52 -0
  223. data/lib/parsanol/rope.rb +78 -0
  224. data/lib/parsanol/scope.rb +42 -0
  225. data/lib/parsanol/slice.rb +172 -0
  226. data/lib/parsanol/source/line_cache.rb +99 -0
  227. data/lib/parsanol/source.rb +171 -0
  228. data/lib/parsanol/source_location.rb +164 -0
  229. data/lib/parsanol/streaming_parser.rb +124 -0
  230. data/lib/parsanol/string_view.rb +192 -0
  231. data/lib/parsanol/transform.rb +267 -0
  232. data/lib/parsanol/version.rb +5 -0
  233. data/lib/parsanol/wasm/README.md +80 -0
  234. data/lib/parsanol/wasm/package.json +51 -0
  235. data/lib/parsanol/wasm/parsanol.js +252 -0
  236. data/lib/parsanol/wasm/parslet.d.ts +129 -0
  237. data/lib/parsanol/wasm_parser.rb +239 -0
  238. data/lib/parsanol.rb +408 -0
  239. data/parsanol-ruby.gemspec +56 -0
  240. data/spec/acceptance/examples_spec.rb +96 -0
  241. data/spec/acceptance/infix_parser_spec.rb +145 -0
  242. data/spec/acceptance/mixing_parsers_spec.rb +74 -0
  243. data/spec/acceptance/regression_spec.rb +329 -0
  244. data/spec/acceptance/repetition_and_maybe_spec.rb +44 -0
  245. data/spec/acceptance/unconsumed_input_spec.rb +21 -0
  246. data/spec/benchmark/comparative/runner_spec.rb +105 -0
  247. data/spec/integration/array_pooling_spec.rb +193 -0
  248. data/spec/integration/buffer_allocation_spec.rb +324 -0
  249. data/spec/integration/position_pooling_spec.rb +184 -0
  250. data/spec/integration/result_builder_spec.rb +282 -0
  251. data/spec/integration/rope_stringview_integration_spec.rb +188 -0
  252. data/spec/integration/slice_pooling_spec.rb +63 -0
  253. data/spec/integration/string_view_integration_spec.rb +125 -0
  254. data/spec/lexer_spec.rb +231 -0
  255. data/spec/parsanol/atom_results_spec.rb +39 -0
  256. data/spec/parsanol/atoms/alternative_spec.rb +26 -0
  257. data/spec/parsanol/atoms/base_spec.rb +127 -0
  258. data/spec/parsanol/atoms/capture_spec.rb +21 -0
  259. data/spec/parsanol/atoms/combinations_spec.rb +5 -0
  260. data/spec/parsanol/atoms/custom_spec.rb +79 -0
  261. data/spec/parsanol/atoms/dsl_spec.rb +7 -0
  262. data/spec/parsanol/atoms/entity_spec.rb +77 -0
  263. data/spec/parsanol/atoms/ignored_spec.rb +15 -0
  264. data/spec/parsanol/atoms/infix_spec.rb +5 -0
  265. data/spec/parsanol/atoms/lookahead_spec.rb +22 -0
  266. data/spec/parsanol/atoms/named_spec.rb +4 -0
  267. data/spec/parsanol/atoms/re_spec.rb +14 -0
  268. data/spec/parsanol/atoms/repetition_spec.rb +24 -0
  269. data/spec/parsanol/atoms/scope_spec.rb +26 -0
  270. data/spec/parsanol/atoms/sequence_spec.rb +28 -0
  271. data/spec/parsanol/atoms/str_spec.rb +15 -0
  272. data/spec/parsanol/atoms/visitor_spec.rb +101 -0
  273. data/spec/parsanol/atoms_spec.rb +488 -0
  274. data/spec/parsanol/auto_optimize_spec.rb +334 -0
  275. data/spec/parsanol/buffer_spec.rb +219 -0
  276. data/spec/parsanol/builder_callbacks_spec.rb +377 -0
  277. data/spec/parsanol/choice_optimizer_spec.rb +231 -0
  278. data/spec/parsanol/convenience_spec.rb +54 -0
  279. data/spec/parsanol/cut_inserter_spec.rb +248 -0
  280. data/spec/parsanol/cut_spec.rb +66 -0
  281. data/spec/parsanol/edit_tracker_spec.rb +218 -0
  282. data/spec/parsanol/error_reporter/contextual_spec.rb +122 -0
  283. data/spec/parsanol/error_reporter/deepest_spec.rb +82 -0
  284. data/spec/parsanol/error_reporter/tree_spec.rb +7 -0
  285. data/spec/parsanol/export_spec.rb +67 -0
  286. data/spec/parsanol/expression/treetop_spec.rb +75 -0
  287. data/spec/parsanol/first_set_spec.rb +298 -0
  288. data/spec/parsanol/interval_tree_spec.rb +205 -0
  289. data/spec/parsanol/lazy_result_spec.rb +288 -0
  290. data/spec/parsanol/lookahead_optimizer_spec.rb +252 -0
  291. data/spec/parsanol/minilisp.citrus +29 -0
  292. data/spec/parsanol/minilisp.tt +29 -0
  293. data/spec/parsanol/optimizer_spec.rb +459 -0
  294. data/spec/parsanol/options/parslet_compat_spec.rb +166 -0
  295. data/spec/parsanol/options/ruby_transform_spec.rb +70 -0
  296. data/spec/parsanol/options/serialized_spec.rb +69 -0
  297. data/spec/parsanol/options/zero_copy_spec.rb +230 -0
  298. data/spec/parsanol/parser_spec.rb +36 -0
  299. data/spec/parsanol/parslet_spec.rb +38 -0
  300. data/spec/parsanol/pattern_spec.rb +272 -0
  301. data/spec/parsanol/pool_spec.rb +392 -0
  302. data/spec/parsanol/pools/array_pool_spec.rb +356 -0
  303. data/spec/parsanol/pools/buffer_pool_spec.rb +365 -0
  304. data/spec/parsanol/pools/position_pool_spec.rb +118 -0
  305. data/spec/parsanol/pools/slice_pool_spec.rb +262 -0
  306. data/spec/parsanol/position_spec.rb +14 -0
  307. data/spec/parsanol/result_builder_spec.rb +391 -0
  308. data/spec/parsanol/rig/rspec_spec.rb +54 -0
  309. data/spec/parsanol/rope_spec.rb +207 -0
  310. data/spec/parsanol/scope_spec.rb +45 -0
  311. data/spec/parsanol/slice_spec.rb +249 -0
  312. data/spec/parsanol/source/line_cache_spec.rb +74 -0
  313. data/spec/parsanol/source_spec.rb +207 -0
  314. data/spec/parsanol/string_view_spec.rb +345 -0
  315. data/spec/parsanol/transform/context_spec.rb +56 -0
  316. data/spec/parsanol/transform_spec.rb +183 -0
  317. data/spec/parsanol/tree_memoization_spec.rb +149 -0
  318. data/spec/parslet_compatibility/expressir_edge_cases_spec.rb +153 -0
  319. data/spec/parslet_compatibility/minimal_reproduction.rb +199 -0
  320. data/spec/parslet_compatibility_spec.rb +399 -0
  321. data/spec/parslet_imported/atom_spec.rb +93 -0
  322. data/spec/parslet_imported/combinator_spec.rb +161 -0
  323. data/spec/parslet_imported/spec_helper.rb +73 -0
  324. data/spec/performance/batch_parsing_benchmark.rb +129 -0
  325. data/spec/performance/complete_optimization_summary.rb +143 -0
  326. data/spec/performance/grammar_caching_analysis.rb +121 -0
  327. data/spec/performance/grammar_caching_benchmark.rb +80 -0
  328. data/spec/performance/native_benchmark_spec.rb +230 -0
  329. data/spec/performance/phase5_benchmark.rb +144 -0
  330. data/spec/performance/profiling_benchmark.rb +131 -0
  331. data/spec/performance/ruby_improvements_benchmark.rb +171 -0
  332. data/spec/performance_spec.rb +374 -0
  333. data/spec/spec_helper.rb +79 -0
  334. data/spec/support/opal.rb +8 -0
  335. data/spec/support/opal.rb.erb +14 -0
  336. 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