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 @@
1
+ parses 'aba'
@@ -0,0 +1,28 @@
1
+ "And when Spring comes"
2
+ {:bud=>{:stem=>[{:branch=>:leaf}]}}
3
+
4
+ "And when Summer comes"
5
+ {:bud=>{:stem=>[{:branch=>[:leaf, :flower]}]}}
6
+
7
+ "And when Fall comes"
8
+ Fruit!
9
+ Falling Leaves!
10
+ {:bud=>{:stem=>[{:branch=>[]}]}}
11
+
12
+ "And when Winter comes"
13
+ {:bud=>{:stem=>[]}}
14
+
15
+ "And when Spring comes"
16
+ {:bud=>{:stem=>[{:branch=>:leaf}]}}
17
+
18
+ "And when Summer comes"
19
+ {:bud=>{:stem=>[{:branch=>[:leaf, :flower]}]}}
20
+
21
+ "And when Fall comes"
22
+ Fruit!
23
+ Falling Leaves!
24
+ {:bud=>{:stem=>[{:branch=>[]}]}}
25
+
26
+ "And when Winter comes"
27
+ {:bud=>{:stem=>[]}}
28
+
@@ -0,0 +1 @@
1
+ ["RubyKaigi2009のテーマは、「変わる/変える」です。", " 前回のRubyKaigi2008のテーマであった「多様性」の言葉の通り、 2008年はRubyそのものに関しても、またRubyの活躍する舞台に関しても、 ますます多様化が進みつつあります。", "RubyKaigi2008は、そのような Rubyの生態系をあらためて認識する場となりました。", " しかし、こうした多様化が進む中、異なる者同士が単純に距離を 置いたままでは、その違いを認識したところであまり意味がありません。", " 異なる実装、異なる思想、異なる背景といった、様々な多様性を理解しつつ、 すり合わせるべきものをすり合わせ、変えていくべきところを 変えていくことが、豊かな未来へとつながる道に違いありません。"]
@@ -0,0 +1,2 @@
1
+ "verified"
2
+ {:o=>{:name=>"b"@1}, :i=>"verified", :c=>{:name=>"a"@33}}
@@ -0,0 +1,3 @@
1
+ [#<struct IntLit text="123"@0>,
2
+ #<struct IntLit text="12345"@4>,
3
+ #<struct StringLit text=" Some String with \\\"escapes\\\""@11>]
@@ -0,0 +1,71 @@
1
+
2
+ # A demonstration of the new precedence climbing infix expression parser.
3
+
4
+ $:.unshift File.dirname(__FILE__) + "/../lib"
5
+
6
+ require 'pp'
7
+ require 'rspec'
8
+ require 'parsanol/parslet'
9
+ require 'parsanol/rig/rspec'
10
+ require 'parsanol/convenience'
11
+
12
+ class InfixExpressionParser < Parsanol::Parser
13
+ root :variable_assignment_list
14
+
15
+ rule(:space) { match[' '] }
16
+
17
+ def cts atom
18
+ atom >> space.repeat
19
+ end
20
+ def infix *args
21
+ Infix.new(*args)
22
+ end
23
+
24
+ # This is the heart of the infix expression parser: real simple definitions
25
+ # for all the pieces we need.
26
+ rule(:mul_op) { cts match['*/'] }
27
+ rule(:add_op) { cts match['+-'] }
28
+ rule(:digit) { match['0-9'] }
29
+ rule(:integer) { cts digit.repeat(1).as(:int) }
30
+
31
+ rule(:expression) { infix_expression(integer,
32
+ [mul_op, 2, :left],
33
+ [add_op, 1, :right]) }
34
+
35
+ # And now adding variable assignments to that, just to a) demonstrate this
36
+ # embedded in a bigger parser, and b) make the example interesting.
37
+ rule(:variable_assignment_list) {
38
+ variable_assignment.repeat(1) }
39
+ rule(:variable_assignment) {
40
+ identifier.as(:ident) >> equal_sign >> expression.as(:exp) >> eol }
41
+ rule(:identifier) {
42
+ cts (match['a-z'] >> match['a-zA-Z0-9'].repeat) }
43
+ rule(:equal_sign) {
44
+ cts str('=') }
45
+ rule(:eol) {
46
+ cts(str("\n")) | any.absent? }
47
+ end
48
+
49
+ class InfixInterpreter < Parsanol::Transform
50
+ rule(int: simple(:int)) { Integer(int) }
51
+ rule(ident: simple(:ident), exp: simple(:result)) { |d|
52
+ d[:doc][d[:ident].to_s.strip.to_sym] = d[:result] }
53
+
54
+ rule(l: simple(:l), o: /^\*/, r: simple(:r)) { l * r }
55
+ rule(l: simple(:l), o: /^\+/, r: simple(:r)) { l + r }
56
+ end
57
+
58
+ input = <<ASSIGNMENTS
59
+ a = 1
60
+ b = 2
61
+ c = 3 * 25
62
+ d = 100 + 3*4
63
+ ASSIGNMENTS
64
+
65
+ puts input
66
+
67
+ int_tree = InfixExpressionParser.new.parse_with_debug(input)
68
+ bindings = {}
69
+ result = InfixInterpreter.new.apply(int_tree, doc: bindings)
70
+
71
+ pp bindings
@@ -0,0 +1,114 @@
1
+ # Precedence Calculator - Ruby Implementation
2
+
3
+ ## How to Run
4
+
5
+ ```bash
6
+ cd parsanol-ruby/example/prec-calc
7
+ ruby basic.rb
8
+ ```
9
+
10
+ ## Code Walkthrough
11
+
12
+ ### Infix Expression Parser
13
+
14
+ Parslet's Infix helper simplifies precedence handling:
15
+
16
+ ```ruby
17
+ rule(:expression) { infix_expression(integer,
18
+ [mul_op, 2, :left],
19
+ [add_op, 1, :right]) }
20
+ ```
21
+
22
+ Each operator tuple specifies: operator rule, precedence level, and associativity.
23
+
24
+ ### Operator Definitions
25
+
26
+ Operators are simple character matches:
27
+
28
+ ```ruby
29
+ rule(:mul_op) { cts match['*/'] }
30
+ rule(:add_op) { cts match['+-'] }
31
+ ```
32
+
33
+ `cts` (consume trailing space) is a helper that strips whitespace after atoms.
34
+
35
+ ### Helper Methods
36
+
37
+ The `cts` and `infix` methods reduce repetition:
38
+
39
+ ```ruby
40
+ def cts atom
41
+ atom >> space.repeat
42
+ end
43
+ def infix *args
44
+ Infix.new(*args)
45
+ end
46
+ ```
47
+
48
+ These keep the grammar DRY and readable.
49
+
50
+ ### Variable Assignment Rule
51
+
52
+ Assignments combine identifier with expression:
53
+
54
+ ```ruby
55
+ rule(:variable_assignment) {
56
+ identifier.as(:ident) >> equal_sign >> expression.as(:exp) >> eol
57
+ }
58
+ ```
59
+
60
+ Labels (`:ident`, `:exp`) mark captures for transformation.
61
+
62
+ ### Transform Rules
63
+
64
+ The interpreter transforms parse tree to values:
65
+
66
+ ```ruby
67
+ rule(int: simple(:int)) { Integer(int) }
68
+ rule(l: simple(:l), o: /^\*/, r: simple(:r)) { l * r }
69
+ rule(l: simple(:l), o: /^\+/, r: simple(:r)) { l + r }
70
+ ```
71
+
72
+ Regex patterns on `:o` distinguish operators in transform rules.
73
+
74
+ ### Binding Context
75
+
76
+ Transform receives context for variable storage:
77
+
78
+ ```ruby
79
+ rule(ident: simple(:ident), exp: simple(:result)) { |d|
80
+ d[:doc][d[:ident].to_s.strip.to_sym] = d[:result]
81
+ }
82
+ ```
83
+
84
+ The `doc:` parameter passes a hash for accumulating bindings.
85
+
86
+ ## Output Types
87
+
88
+ ```ruby
89
+ # Parse tree
90
+ [{:ident=>"a"@0, :exp=>{:int=>"1"@4}},
91
+ {:ident=>"b"@8, :exp=>{:int=>"2"@12}},
92
+ {:ident=>"c"@16, :exp=>{:l=>{:int=>"3"@20}, :o=>"*"@22, :r=>{:int=>"25"@24}}}]
93
+
94
+ # After transform (bindings)
95
+ {:a=>1, :b=>2, :c=>75}
96
+ ```
97
+
98
+ ## Design Decisions
99
+
100
+ ### Why Infix Helper?
101
+
102
+ The `Infix` class encapsulates precedence climbing logic. Without it, you'd need multiple recursive rules with careful ordering.
103
+
104
+ ### Why Regex in Transform?
105
+
106
+ Parslet's transform doesn't support operator-specific rules directly. Regex matching on the operator string provides clean separation.
107
+
108
+ ### Why Pass Context to Transform?
109
+
110
+ Variable bindings need accumulation across statements. Passing a context hash enables this without global state.
111
+
112
+ ### Why Right Associativity for Addition?
113
+
114
+ This example demonstrates both associativities. In practice, addition is typically left-associative; the right associativity here is for demonstration.
@@ -0,0 +1,30 @@
1
+ # The example from the readme. With this, I am making sure that the readme
2
+ # 'works'. Is this too messy?
3
+
4
+ $:.unshift File.dirname(__FILE__) + "/../lib"
5
+
6
+ # cut here -------------------------------------------------------------------
7
+ require 'parsanol/parslet'
8
+ include Parsanol::Parslet
9
+
10
+ # Constructs a parser using a Parser Expression Grammar like DSL:
11
+ parser = str('"') >>
12
+ (
13
+ str('\\') >> any |
14
+ str('"').absent? >> any
15
+ ).repeat.as(:string) >>
16
+ str('"')
17
+
18
+ # Parse the string and capture parts of the interpretation (:string above)
19
+ tree = parser.parse('"This is a \\"String\\" in which you can escape stuff"')
20
+
21
+ tree # => {:string=>"This is a \\\"String\\\" in which you can escape stuff"}
22
+
23
+ # Here's how you can grab results from that tree:
24
+
25
+ transform = Parsanol::Transform.new do
26
+ rule(:string => simple(:x)) {
27
+ puts "String contents: #{x}" }
28
+ end
29
+ transform.apply(tree)
30
+
@@ -0,0 +1,80 @@
1
+ # Error Demo - Ruby Implementation
2
+
3
+ ## How to Run
4
+
5
+ ```bash
6
+ cd parsanol-ruby/example/readme
7
+ ruby basic.rb
8
+ ```
9
+
10
+ ## Code Walkthrough
11
+
12
+ ### Simple Parser Definition
13
+
14
+ A minimal parser that matches repeated 'a' characters:
15
+
16
+ ```ruby
17
+ class MyParser < Parsanol::Parser
18
+ rule(:a) { str('a').repeat }
19
+
20
+ def parse(str)
21
+ a.parse(str)
22
+ end
23
+ end
24
+ ```
25
+
26
+ The parser only accepts strings of 'a's.
27
+
28
+ ### Successful Parse
29
+
30
+ When input matches the grammar:
31
+
32
+ ```ruby
33
+ MyParser.new.parse('aaaa')
34
+ # => "aaaa"
35
+ ```
36
+
37
+ Returns the matched string.
38
+
39
+ ### Failed Parse
40
+
41
+ When input doesn't match:
42
+
43
+ ```ruby
44
+ MyParser.new.parse('bbbb')
45
+ # => Parsanol::ParseFailed: Expected "a" at line 1, column 1
46
+ ```
47
+
48
+ Raises an exception with error details.
49
+
50
+ ### Error Reporting
51
+
52
+ Parslet provides rich error messages:
53
+
54
+ - What was expected
55
+ - Where the error occurred (line, column)
56
+ - What was found instead
57
+
58
+ ## Output Types
59
+
60
+ ```ruby
61
+ # Success:
62
+ "aaaa"
63
+
64
+ # Failure (raises exception):
65
+ # Parsanol::ParseFailed: Expected "a" at line 1, column 1
66
+ ```
67
+
68
+ ## Design Decisions
69
+
70
+ ### Why This Example?
71
+
72
+ Demonstrates basic error handling behavior. Understanding errors is crucial for debugging parsers.
73
+
74
+ ### Why Repeat?
75
+
76
+ `.repeat` allows zero or more matches. Empty string would also parse successfully.
77
+
78
+ ### Ruby-Only Feature
79
+
80
+ This is a simple demonstration of Parslet-compatible error handling.
@@ -0,0 +1,15 @@
1
+
2
+ $:.unshift File.dirname(__FILE__) + "/../lib"
3
+ require 'parsanol/parslet'
4
+
5
+ include Parsanol::Parslet
6
+
7
+ parser = str('a').capture(:a) >> scope { str('b').capture(:a) } >>
8
+ dynamic { |s,c| str(c.captures[:a]) }
9
+
10
+ begin
11
+ parser.parse('aba')
12
+ puts "parses 'aba'"
13
+ rescue
14
+ puts "exception!"
15
+ end
@@ -0,0 +1,73 @@
1
+ # Scope Handling - Ruby Implementation
2
+
3
+ ## How to Run
4
+
5
+ ```bash
6
+ cd parsanol-ruby/example/scopes
7
+ ruby basic.rb
8
+ ```
9
+
10
+ ## Code Walkthrough
11
+
12
+ ### Scope Blocks
13
+
14
+ Scopes isolate captures:
15
+
16
+ ```ruby
17
+ parser = str('a').capture(:a) >>
18
+ scope { str('b').capture(:a) } >>
19
+ dynamic { |s,c| str(c.captures[:a]) }
20
+ ```
21
+
22
+ The outer capture of 'a' is shadowed by the inner scope's capture of 'b'.
23
+
24
+ ### Shadowing Behavior
25
+
26
+ Inner scopes can shadow outer captures:
27
+
28
+ ```ruby
29
+ # Outer scope captures :a => 'a'
30
+ str('a').capture(:a) >>
31
+ # Inner scope captures :a => 'b' (shadows outer)
32
+ scope { str('b').capture(:a) } >>
33
+ # After scope, :a still refers to inner capture
34
+ dynamic { |s,c| str(c.captures[:a]) } # matches 'b'
35
+ ```
36
+
37
+ ### Isolation Benefits
38
+
39
+ Scopes prevent capture pollution:
40
+
41
+ - Nested constructs don't interfere with outer captures
42
+ - Each level has its own namespace
43
+ - Cleanup is automatic when scope exits
44
+
45
+ ## Output Types
46
+
47
+ ```ruby
48
+ # Input: 'aba'
49
+ # Parses successfully because:
50
+ # - 'a' captured as :a in outer
51
+ # - 'b' captured as :a in inner scope (shadows)
52
+ # - dynamic matches 'b' (inner capture)
53
+
54
+ # Input: 'aaa'
55
+ # Would FAIL because:
56
+ # - 'a' captured as :a
57
+ # - scope captures 'a' as :a
58
+ # - dynamic matches 'a' (not 'b')
59
+ ```
60
+
61
+ ## Design Decisions
62
+
63
+ ### Why Lexical Scoping?
64
+
65
+ Lexical scoping matches how variables work in most programming languages. Users can reason about capture visibility.
66
+
67
+ ### Why Shadow Instead of Merge?
68
+
69
+ Shadowing prevents accidental capture conflicts. Explicit naming would be needed for merging.
70
+
71
+ ### Ruby-Only Feature
72
+
73
+ Scope blocks are a Parslet-specific feature for managing capture context. The Rust implementation uses different patterns for similar functionality.
@@ -0,0 +1,38 @@
1
+ {
2
+ "id": "scopes",
3
+ "title": "Scope DSL",
4
+ "description": "Demonstrate Parslet's scope DSL for managing variable bindings across grammar rules.",
5
+ "category": "conceptual",
6
+ "tags": ["scope", "binding", "parslet", "context"],
7
+ "difficulty": "intermediate",
8
+ "concepts": ["scope", "variable binding", "context", "nested rules"],
9
+
10
+ "motivation": {
11
+ "why": "Scopes provide isolated variable binding contexts within grammar rules. This enables nested language constructs and parameterized rules.",
12
+ "useCases": [
13
+ "Nested language constructs",
14
+ "Parameterized rules",
15
+ "Context-sensitive parsing"
16
+ ]
17
+ },
18
+
19
+ "inputFormat": {
20
+ "description": "Expressions with scoped variable bindings.",
21
+ "examples": [
22
+ { "input": "let x = 1", "description": "Variable declaration in scope", "valid": true }
23
+ ]
24
+ },
25
+
26
+ "outputFormat": {
27
+ "description": "Scoped variable bindings.",
28
+ "structure": {
29
+ "scope": { "description": "The scope context with bound variables" }
30
+ }
31
+ },
32
+
33
+ "rubyOnly": true,
34
+ "parsletCompatible": true,
35
+ "implementations": {
36
+ "ruby": { "basic": "basic.rb" }
37
+ }
38
+ }
@@ -0,0 +1,46 @@
1
+ $:.unshift File.dirname(__FILE__) + "/../lib"
2
+
3
+ require 'parsanol/parslet'
4
+ require 'pp'
5
+
6
+ tree = {:bud => {:stem => []}}
7
+
8
+ class Spring < Parsanol::Transform
9
+ rule(:stem => sequence(:branches)) {
10
+ {:stem => (branches + [{:branch => :leaf}])}
11
+ }
12
+ end
13
+ class Summer < Parsanol::Transform
14
+ rule(:stem => subtree(:branches)) {
15
+ new_branches = branches.map { |b| {:branch => [:leaf, :flower]} }
16
+ {:stem => new_branches}
17
+ }
18
+ end
19
+ class Fall < Parsanol::Transform
20
+ rule(:branch => sequence(:x)) {
21
+ x.each { |e| puts "Fruit!" if e==:flower }
22
+ x.each { |e| puts "Falling Leaves!" if e==:leaf }
23
+ {:branch => []}
24
+ }
25
+ end
26
+ class Winter < Parsanol::Transform
27
+ rule(:stem => subtree(:x)) {
28
+ {:stem => []}
29
+ }
30
+ end
31
+
32
+ def do_seasons(tree)
33
+ [Spring, Summer, Fall, Winter].each do |season|
34
+ p "And when #{season} comes"
35
+ tree = season.new.apply(tree)
36
+ pp tree
37
+ puts
38
+ end
39
+ tree
40
+ end
41
+
42
+ # What marvel of life!
43
+ tree = do_seasons(tree)
44
+ tree = do_seasons(tree)
45
+
46
+
@@ -0,0 +1,117 @@
1
+ # Transform Patterns - Ruby Implementation
2
+
3
+ ## How to Run
4
+
5
+ ```bash
6
+ cd parsanol-ruby/example/seasons
7
+ ruby basic.rb
8
+ ```
9
+
10
+ ## Code Walkthrough
11
+
12
+ ### Transform Class Structure
13
+
14
+ Each season is a separate transform class:
15
+
16
+ ```ruby
17
+ class Spring < Parsanol::Transform
18
+ rule(:stem => sequence(:branches)) {
19
+ {:stem => (branches + [{:branch => :leaf}])}
20
+ }
21
+ end
22
+ ```
23
+
24
+ Different classes encapsulate different transformation behaviors.
25
+
26
+ ### Sequence Pattern
27
+
28
+ `sequence(:x)` matches arrays of simple values:
29
+
30
+ ```ruby
31
+ rule(:stem => sequence(:branches)) {
32
+ {:stem => (branches + [{:branch => :leaf}])}
33
+ }
34
+ ```
35
+
36
+ `branches` is bound to the array content.
37
+
38
+ ### Subtree Pattern
39
+
40
+ `subtree(:x)` matches any nested structure:
41
+
42
+ ```ruby
43
+ rule(:stem => subtree(:branches)) {
44
+ new_branches = branches.map { |b| {:branch => [:leaf, :flower]} }
45
+ {:stem => new_branches}
46
+ }
47
+ ```
48
+
49
+ Recursively applies to children.
50
+
51
+ ### Branch Pattern
52
+
53
+ Match and transform nested arrays:
54
+
55
+ ```ruby
56
+ class Fall < Parsanol::Transform
57
+ rule(:branch => sequence(:x)) {
58
+ x.each { |e| puts "Fruit!" if e == :flower }
59
+ x.each { |e| puts "Falling Leaves!" if e == :leaf }
60
+ {:branch => []}
61
+ }
62
+ end
63
+ ```
64
+
65
+ Iterate over array contents while transforming.
66
+
67
+ ### Composition
68
+
69
+ Multiple transforms are applied in sequence:
70
+
71
+ ```ruby
72
+ def do_seasons(tree)
73
+ [Spring, Summer, Fall, Winter].each do |season|
74
+ tree = season.new.apply(tree)
75
+ end
76
+ tree
77
+ end
78
+ ```
79
+
80
+ Each transform passes its output to the next.
81
+
82
+ ## Output Types
83
+
84
+ ```ruby
85
+ # Initial tree:
86
+ {:bud => {:stem => []}}
87
+
88
+ # After Spring:
89
+ {:bud => {:stem => [{:branch => :leaf}]}}
90
+
91
+ # After Summer:
92
+ {:bud => {:stem => [{:branch => [:leaf, :flower]}]}}
93
+
94
+ # After Fall:
95
+ {:bud => {:stem => [{:branch => []}]}}
96
+
97
+ # After Winter:
98
+ {:bud => {:stem => []}}
99
+ ```
100
+
101
+ ## Design Decisions
102
+
103
+ ### Why Separate Transform Classes?
104
+
105
+ Each transform has a single responsibility. Separation makes transformations testable and reusable.
106
+
107
+ ### Why sequence vs subtree?
108
+
109
+ `sequence` is for homogeneous arrays; `subtree` handles arbitrary nesting. Different patterns for different structures.
110
+
111
+ ### Why Apply in Sequence?
112
+
113
+ Pipelining transforms creates complex behavior from simple pieces. Each step is easy to understand and debug.
114
+
115
+ ### Ruby-Only Feature
116
+
117
+ Transform patterns are Parslet's Ruby DSL for AST manipulation. Rust uses pattern matching and visitor patterns instead.
@@ -0,0 +1,40 @@
1
+ {
2
+ "id": "seasons",
3
+ "title": "Transform Patterns",
4
+ "description": "Demonstrate Parslet transform DSL patterns through seasonal tree transformations.",
5
+ "category": "conceptual",
6
+ "tags": ["transform", "pattern-matching", "parslet", "ast"],
7
+ "difficulty": "intermediate",
8
+ "concepts": ["transform", "pattern matching", "sequence", "subtree", "AST transformation"],
9
+
10
+ "motivation": {
11
+ "why": "Transforms convert parse trees to domain objects using pattern matching. This example shows how multiple transforms can be composed to create complex behavior from simple pieces.",
12
+ "useCases": [
13
+ "AST transformation",
14
+ "Code generation",
15
+ "Data normalization",
16
+ "Multi-stage processing"
17
+ ]
18
+ },
19
+
20
+ "inputFormat": {
21
+ "description": "Tree structure representing a plant through seasons.",
22
+ "examples": [
23
+ { "input": "{bud: {stem: []}}", "description": "Initial tree state", "valid": true }
24
+ ]
25
+ },
26
+
27
+ "outputFormat": {
28
+ "description": "Transformed tree after seasonal transformations.",
29
+ "structure": {
30
+ "season": { "description": "The season transform being applied" },
31
+ "tree": { "description": "The resulting tree structure" }
32
+ }
33
+ },
34
+
35
+ "rubyOnly": true,
36
+ "parsletCompatible": true,
37
+ "implementations": {
38
+ "ruby": { "basic": "basic.rb" }
39
+ }
40
+ }