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,316 @@
1
+ # JSON Parser Example - ZeroCopy: Mirrored Objects (Direct FFI)
2
+ #
3
+ # This example demonstrates ZeroCopy for parsing JSON:
4
+ # 1. Rust parser (parsanol-rs) does the parsing
5
+ # 2. Rust constructs typed JSON value objects
6
+ # 3. Direct Ruby object construction via FFI (no serialization!)
7
+ # 4. Maximum performance with zero-copy
8
+ #
9
+ # This option provides the best performance for JSON parsing.
10
+
11
+ $:.unshift File.dirname(__FILE__) + "/../lib"
12
+
13
+ require 'parsanol'
14
+
15
+ # NOTE: This example requires:
16
+ # 1. ZeroCopy extension support for parse_to_objects
17
+ # 2. #[derive(RubyObject)] proc macro in Rust
18
+ # 3. Matching Ruby class definitions
19
+ #
20
+ # This serves as an API preview.
21
+
22
+ # Step 1: Define Ruby classes that mirror Rust struct definitions
23
+ module Json
24
+ class Value
25
+ def to_ruby
26
+ raise NotImplementedError
27
+ end
28
+ end
29
+
30
+ class Null < Value
31
+ def to_ruby = nil
32
+ def to_s = "null"
33
+ end
34
+
35
+ class Bool < Value
36
+ attr_reader :value
37
+
38
+ def initialize(value)
39
+ @value = value
40
+ end
41
+
42
+ def to_ruby = @value
43
+ def to_s = @value ? "true" : "false"
44
+ end
45
+
46
+ class Number < Value
47
+ attr_reader :value
48
+
49
+ def initialize(value)
50
+ @value = value
51
+ end
52
+
53
+ def to_ruby = @value
54
+ def to_s = @value.to_s
55
+ end
56
+
57
+ class String < Value
58
+ attr_reader :value
59
+
60
+ def initialize(value)
61
+ @value = value
62
+ end
63
+
64
+ def to_ruby = @value
65
+ def to_s = "\"#{@value}\""
66
+ end
67
+
68
+ class Array < Value
69
+ attr_reader :elements
70
+
71
+ def initialize(elements)
72
+ @elements = elements
73
+ end
74
+
75
+ def to_ruby = @elements.map(&:to_ruby)
76
+ def to_s = "[#{@elements.map(&:to_s).join(', ')}]"
77
+ end
78
+
79
+ class Object < Value
80
+ attr_reader :members
81
+
82
+ def initialize(members)
83
+ @members = members
84
+ end
85
+
86
+ def to_ruby = @members.transform_values(&:to_ruby)
87
+
88
+ def to_s
89
+ pairs = @members.map { |k, v| "\"#{k}\": #{v}" }
90
+ "{#{pairs.join(', ')}}"
91
+ end
92
+
93
+ def [](key)
94
+ @members[key]
95
+ end
96
+
97
+ def keys
98
+ @members.keys
99
+ end
100
+ end
101
+ end
102
+
103
+ # Step 2: Define the parser with output type mapping
104
+ class JsonParser < Parsanol::Parser
105
+ # Include ZeroCopy module (planned)
106
+ # include Parsanol::ZeroCopy
107
+
108
+ root :json
109
+
110
+ rule(:json) { space? >> value >> space? }
111
+
112
+ rule(:value) {
113
+ object |
114
+ array |
115
+ string |
116
+ number |
117
+ true_value |
118
+ false_value |
119
+ null_value
120
+ }
121
+
122
+ rule(:object) {
123
+ str('{') >> space? >>
124
+ (entry >> (comma >> entry).repeat).maybe.as(:object) >>
125
+ space? >> str('}')
126
+ }
127
+
128
+ rule(:entry) {
129
+ (string.as(:key) >> space? >> colon >> space? >> value.as(:val)).as(:entry)
130
+ }
131
+
132
+ rule(:array) {
133
+ str('[') >> space? >>
134
+ (value >> (comma >> value).repeat).maybe.as(:array) >>
135
+ space? >> str(']')
136
+ }
137
+
138
+ rule(:string) {
139
+ str('"') >> (
140
+ str('\\') >> any | str('"').absent? >> any
141
+ ).repeat.as(:string) >> str('"')
142
+ }
143
+
144
+ rule(:number) {
145
+ (
146
+ str('-').maybe >>
147
+ (str('0') | (match('[1-9]') >> digit.repeat)) >>
148
+ (str('.') >> digit.repeat(1)).maybe >>
149
+ (match('[eE]') >> (str('+') | str('-')).maybe >> digit.repeat(1)).maybe
150
+ ).as(:number)
151
+ }
152
+
153
+ rule(:true_value) { str('true').as(:true) }
154
+ rule(:false_value) { str('false').as(:false) }
155
+ rule(:null_value) { str('null').as(:null) }
156
+
157
+ rule(:digit) { match('[0-9]') }
158
+ rule(:space) { match('\s').repeat(1) }
159
+ rule(:space?) { space.maybe }
160
+ rule(:comma) { space? >> str(',') >> space? }
161
+ rule(:colon) { str(':') }
162
+
163
+ # Output type mapping (planned feature)
164
+ # output_types(
165
+ # null: Json::Null,
166
+ # bool: Json::Bool,
167
+ # number: Json::Number,
168
+ # string: Json::String,
169
+ # array: Json::Array,
170
+ # object: Json::Object
171
+ # )
172
+ end
173
+
174
+ # Step 3: Parse with direct object construction
175
+ def parse_json(input)
176
+ parser = JsonParser.new
177
+
178
+ # ZeroCopy: Parse and get direct Ruby objects
179
+ # NOTE: This requires native extension support
180
+ # value = parser.parse(input)
181
+ # # value is already a Json::String, Json::Number, etc.!
182
+ # # No transform needed, no JSON serialization!
183
+
184
+ # For demonstration, simulate what ZeroCopy would return
185
+ value = simulate_parse(input)
186
+ puts "Parsed: #{value.class}"
187
+ puts "Value: #{value.to_s}"
188
+
189
+ result = value.to_ruby
190
+ puts "Ruby: #{result.inspect[0..100]}..."
191
+
192
+ result
193
+ end
194
+
195
+ # Simulated parsing for demonstration
196
+ def simulate_parse(input)
197
+ input = input.strip
198
+
199
+ case input
200
+ when 'null'
201
+ Json::Null.new
202
+ when 'true'
203
+ Json::Bool.new(true)
204
+ when 'false'
205
+ Json::Bool.new(false)
206
+ when /^"(.*)"$/
207
+ Json::String.new($1)
208
+ when /^-?\d+$/
209
+ Json::Number.new(input.to_i)
210
+ when /^-?\d+\.\d+$/
211
+ Json::Number.new(input.to_f)
212
+ when /^\[(.*)\]$/
213
+ inner = $1.strip
214
+ return Json::Array.new([]) if inner.empty?
215
+
216
+ # Simple split for demonstration
217
+ elements = inner.split(',').map { |e| simulate_parse(e.strip) }
218
+ Json::Array.new(elements)
219
+ when /^\{(.*)\}$/
220
+ inner = $1.strip
221
+ return Json::Object.new({}) if inner.empty?
222
+
223
+ # Simple parse for demonstration
224
+ members = {}
225
+ inner.scan(/"([^"]+)":\s*([^,}]+)/) do |key, val|
226
+ members[key] = simulate_parse(val.strip)
227
+ end
228
+ Json::Object.new(members)
229
+ else
230
+ raise "Cannot parse: #{input}"
231
+ end
232
+ end
233
+
234
+ # Example usage
235
+ if __FILE__ == $0
236
+ puts "=" * 60
237
+ puts "JSON Parser Example - ZeroCopy: Mirrored Objects"
238
+ puts "=" * 60
239
+ puts
240
+ puts "NOTE: This example shows the planned API for ZeroCopy."
241
+ puts "The native extension support for parse_to_objects is coming soon."
242
+ puts
243
+
244
+ test_cases = [
245
+ ['"hello"', "hello"],
246
+ ['42', 42],
247
+ ['true', true],
248
+ ['null', nil],
249
+ ['[1, 2, 3]', [1, 2, 3]],
250
+ ['{"a": 1}', { "a" => 1 }],
251
+ ]
252
+
253
+ test_cases.each do |input, expected|
254
+ puts
255
+ puts "-" * 40
256
+ puts "Input: #{input}"
257
+ begin
258
+ result = parse_json(input)
259
+ status = result == expected ? "✓ PASS" : "✗ FAIL"
260
+ puts "Expected: #{expected.inspect}, Got: #{result.inspect} - #{status}"
261
+ rescue => e
262
+ puts "Error: #{e.message}"
263
+ puts "✗ FAIL"
264
+ end
265
+ end
266
+
267
+ # Show type safety benefit
268
+ puts
269
+ puts "-" * 40
270
+ puts "Type Safety Example:"
271
+ json_obj = simulate_parse('{"name": "Alice", "age": 30}')
272
+ puts "Parsed object type: #{json_obj.class}"
273
+ puts "Name field type: #{json_obj['name'].class}"
274
+ puts "Age field type: #{json_obj['age'].class}"
275
+
276
+ puts
277
+ puts "=" * 60
278
+ puts "ZeroCopy Benefits for JSON:"
279
+ puts "- FASTEST: No serialization overhead"
280
+ puts "- Type-safe: Each JSON value type is a different class"
281
+ puts "- Methods: Can add custom methods to Json::Object, etc."
282
+ puts "- Zero-copy: Direct construction from Rust"
283
+ puts
284
+ puts "When to use ZeroCopy for JSON:"
285
+ puts "- High-throughput JSON parsing"
286
+ puts "- When you need typed access to values"
287
+ puts "- When you want custom methods on JSON objects"
288
+ puts "=" * 60
289
+ end
290
+
291
+ # Rust code that would be needed (for reference):
292
+ #
293
+ # // In parsanol-rs
294
+ # use parsanol_ruby_derive::RubyObject;
295
+ #
296
+ # #[derive(Debug, Clone, RubyObject)]
297
+ # #[ruby_class("Json::Value")]
298
+ # pub enum JsonValue {
299
+ # #[ruby_variant("null")]
300
+ # Null,
301
+ #
302
+ # #[ruby_variant("bool")]
303
+ # Bool(bool),
304
+ #
305
+ # #[ruby_variant("number")]
306
+ # Number(f64),
307
+ #
308
+ # #[ruby_variant("string")]
309
+ # String(String),
310
+ #
311
+ # #[ruby_variant("array")]
312
+ # Array(Vec<JsonValue>),
313
+ #
314
+ # #[ruby_variant("object")]
315
+ # Object(HashMap<String, JsonValue>),
316
+ # }
@@ -0,0 +1,36 @@
1
+ # JSON (Zero-Copy - Option C)
2
+
3
+ ## Purpose
4
+
5
+ This implementation demonstrates direct FFI object construction for JSON
6
+ parsing without serialization overhead.
7
+
8
+ ## When to Use
9
+
10
+ - Maximum performance required
11
+ - Production systems
12
+ - When zero-copy is critical
13
+
14
+ ## Key Concepts
15
+
16
+ 1. **Direct FFI**: No serialization overhead
17
+ 2. **Ruby Object Construction**: Direct via rb_funcall
18
+ 3. **Type Safety**: Mirrored types on both sides
19
+
20
+ ## Running
21
+
22
+ ```bash
23
+ ruby example/json/zero_copy.rb
24
+ ```
25
+
26
+ ## Output
27
+
28
+ ```
29
+ Input: {"key": "value"}
30
+ Result: Hash
31
+ Value: {"key" => "value"}
32
+ ```
33
+
34
+ ## Note
35
+
36
+ This is the fastest option but requires more complex FFI setup.
@@ -0,0 +1,34 @@
1
+
2
+ # An exploration of two ideas:
3
+ # a) Constructing a whole parser inline, without the artificial class around
4
+ # it.
5
+ # and:
6
+ # b) Constructing non-greedy or non-blind parsers by transforming the
7
+ # grammar.
8
+
9
+ $:.unshift File.dirname(__FILE__) + "/../lib"
10
+
11
+ require 'parsanol/parslet'
12
+ include Parsanol::Parslet
13
+
14
+ a = str('a').repeat >> str('aa')
15
+
16
+ # E1% E2
17
+ #
18
+ # S = E2 | E1 S
19
+
20
+ def this(name, &block); return Parsanol::Atoms::Entity.new(name, &block) end
21
+ def epsilon; any.absent? end
22
+
23
+ # Traditional repetition will try as long as the pattern can be matched and
24
+ # then give up. This is greedy and blind.
25
+ a = str('a').as(:e) >> this('a') { a }.as(:rec) | epsilon
26
+
27
+ # Here's a pattern match that is greedy and non-blind. The first pattern
28
+ # 'a'* will be tried as many times as possible, while still matching the
29
+ # end pattern 'aa'.
30
+ b = str('aa').as(:e2) >> epsilon | str('a').as(:e1) >> this('b') { b }.as(:rec)
31
+
32
+ p a.parse('aaaa')
33
+ p b
34
+ p b.parse('aaaa')
@@ -0,0 +1,91 @@
1
+ # Non-Greedy Parsing Patterns - Ruby Implementation
2
+
3
+ ## How to Run
4
+
5
+ ```bash
6
+ cd parsanol-ruby/example/local
7
+ ruby basic.rb
8
+ ```
9
+
10
+ ## Code Walkthrough
11
+
12
+ ### The Problem with Greedy Parsing
13
+
14
+ Traditional repetition is greedy and blind:
15
+
16
+ ```ruby
17
+ a = str('a').repeat >> str('aa')
18
+ # Input: 'aaaa'
19
+ # Fails! repeat consumes all 'a's, leaving nothing for 'aa'
20
+ ```
21
+
22
+ Greedy repetition takes as much as possible without considering what follows.
23
+
24
+ ### Non-Blind Pattern
25
+
26
+ Transform the grammar to look ahead:
27
+
28
+ ```ruby
29
+ # E1% E2 transformation:
30
+ # S = E2 | E1 S
31
+
32
+ def this(name, &block)
33
+ Parsanol::Atoms::Entity.new(name, &block)
34
+ end
35
+ def epsilon
36
+ any.absent?
37
+ end
38
+
39
+ a = str('a').as(:e) >> this('a') { a }.as(:rec) | epsilon
40
+ ```
41
+
42
+ This recursively matches while checking alternatives.
43
+
44
+ ### Greedy Non-Blind Alternative
45
+
46
+ Put the terminal first:
47
+
48
+ ```ruby
49
+ b = str('aa').as(:e2) >> epsilon |
50
+ str('a').as(:e1) >> this('b') { b }.as(:rec)
51
+ ```
52
+
53
+ Try to match the end (`aa`) first, then recurse if that fails.
54
+
55
+ ### Entity for Recursion
56
+
57
+ The `this()` helper enables recursive rule definition:
58
+
59
+ ```ruby
60
+ def this(name, &block)
61
+ Parsanol::Atoms::Entity.new(name, &block)
62
+ end
63
+ ```
64
+
65
+ Entity delays evaluation until parse time, allowing forward references.
66
+
67
+ ## Output Types
68
+
69
+ ```ruby
70
+ # Greedy blind (fails):
71
+ a.parse('aaaa') # => ParseFailed
72
+
73
+ # Greedy non-blind (succeeds):
74
+ b.parse('aaaa')
75
+ # => {:e2=>"aa"@0}
76
+ # or with proper structure showing the match
77
+ ```
78
+
79
+ ## Design Decisions
80
+
81
+ ### Why Transform Instead of Modify Parser?
82
+
83
+ Grammar transformation is more flexible than modifying the parser engine. Different transformations achieve different behaviors.
84
+
85
+ ### Why Entity for Recursion?
86
+
87
+ Ruby blocks capture variables at definition time. Entity provides a way to reference rules that aren't defined yet.
88
+
89
+ ### Ruby-Only Feature
90
+
91
+ These advanced PEG patterns use Parslet's Ruby-specific constructs. They demonstrate theoretical parsing concepts more than practical usage.
@@ -0,0 +1,38 @@
1
+ {
2
+ "id": "local",
3
+ "title": "Local Variable Parsing",
4
+ "description": "Demonstrate non-greedy parsing patterns using local variable bindings.",
5
+ "category": "conceptual",
6
+ "tags": ["local", "non-greedy", "parslet", "context"],
7
+ "difficulty": "intermediate",
8
+ "concepts": ["local binding", "non-greedy matching", "context", "delimiters"],
9
+
10
+ "motivation": {
11
+ "why": "Local provides non-greedy parsing within a specific context. This enables matching up to delimiters without consuming them.",
12
+ "useCases": [
13
+ "Matching up to delimiters",
14
+ "Context-sensitive parsing",
15
+ "Non-greedy content extraction"
16
+ ]
17
+ },
18
+
19
+ "inputFormat": {
20
+ "description": "Content with delimiters to parse non-greedily.",
21
+ "examples": [
22
+ { "input": "content", "description": "Content to parse", "valid": true }
23
+ ]
24
+ },
25
+
26
+ "outputFormat": {
27
+ "description": "Non-greedily matched content.",
28
+ "structure": {
29
+ "content": { "description": "The locally matched content" }
30
+ }
31
+ },
32
+
33
+ "rubyOnly": true,
34
+ "parsletCompatible": true,
35
+ "implementations": {
36
+ "ruby": { "basic": "basic.rb" }
37
+ }
38
+ }