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,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Parsanol::Optimizer, '.simplify_lookaheads' do
6
+ let(:str_a) { Parsanol::Atoms::Str.new('a') }
7
+ let(:str_b) { Parsanol::Atoms::Str.new('b') }
8
+
9
+ describe 'double negation simplification' do
10
+ it 'simplifies !(!x) to &x' do
11
+ inner = Parsanol::Atoms::Lookahead.new(str_a, false) # !a
12
+ outer = Parsanol::Atoms::Lookahead.new(inner, false) # !(!a)
13
+
14
+ result = Parsanol::Optimizer.simplify_lookaheads(outer)
15
+
16
+ expect(result).to be_a(Parsanol::Atoms::Lookahead)
17
+ expect(result.positive).to be true
18
+ expect(result.bound_parslet).to eq(str_a)
19
+ end
20
+
21
+ it 'simplifies triple negation !(!(!x)) to !x' do
22
+ inner1 = Parsanol::Atoms::Lookahead.new(str_a, false) # !a
23
+ inner2 = Parsanol::Atoms::Lookahead.new(inner1, false) # !(!a)
24
+ outer = Parsanol::Atoms::Lookahead.new(inner2, false) # !(!(!a))
25
+
26
+ result = Parsanol::Optimizer.simplify_lookaheads(outer)
27
+
28
+ expect(result).to be_a(Parsanol::Atoms::Lookahead)
29
+ expect(result.positive).to be false
30
+ expect(result.bound_parslet).to eq(str_a)
31
+ end
32
+ end
33
+
34
+ describe 'idempotent positive lookahead' do
35
+ it 'simplifies &(&x) to &x' do
36
+ inner = Parsanol::Atoms::Lookahead.new(str_a, true) # &a
37
+ outer = Parsanol::Atoms::Lookahead.new(inner, true) # &(&a)
38
+
39
+ result = Parsanol::Optimizer.simplify_lookaheads(outer)
40
+
41
+ expect(result).to be_a(Parsanol::Atoms::Lookahead)
42
+ expect(result.positive).to be true
43
+ expect(result.bound_parslet).to eq(str_a)
44
+ end
45
+
46
+ it 'simplifies &(&(&x)) to &x' do
47
+ inner1 = Parsanol::Atoms::Lookahead.new(str_a, true) # &a
48
+ inner2 = Parsanol::Atoms::Lookahead.new(inner1, true) # &(&a)
49
+ outer = Parsanol::Atoms::Lookahead.new(inner2, true) # &(&(&a))
50
+
51
+ result = Parsanol::Optimizer.simplify_lookaheads(outer)
52
+
53
+ expect(result).to be_a(Parsanol::Atoms::Lookahead)
54
+ expect(result.positive).to be true
55
+ expect(result.bound_parslet).to eq(str_a)
56
+ end
57
+ end
58
+
59
+ describe 'negative of positive simplification' do
60
+ it 'simplifies !(&x) to !x' do
61
+ inner = Parsanol::Atoms::Lookahead.new(str_a, true) # &a
62
+ outer = Parsanol::Atoms::Lookahead.new(inner, false) # !(&a)
63
+
64
+ result = Parsanol::Optimizer.simplify_lookaheads(outer)
65
+
66
+ expect(result).to be_a(Parsanol::Atoms::Lookahead)
67
+ expect(result.positive).to be false
68
+ expect(result.bound_parslet).to eq(str_a)
69
+ end
70
+ end
71
+
72
+ describe 'positive of negative simplification' do
73
+ it 'simplifies &(!x) to !x' do
74
+ inner = Parsanol::Atoms::Lookahead.new(str_a, false) # !a
75
+ outer = Parsanol::Atoms::Lookahead.new(inner, true) # &(!a)
76
+
77
+ result = Parsanol::Optimizer.simplify_lookaheads(outer)
78
+
79
+ expect(result).to be_a(Parsanol::Atoms::Lookahead)
80
+ expect(result.positive).to be false
81
+ expect(result.bound_parslet).to eq(str_a)
82
+ end
83
+ end
84
+
85
+ describe 'recursive optimization' do
86
+ it 'optimizes lookaheads nested in sequences' do
87
+ # Sequence(!(!a), b) => Sequence(&a, b)
88
+ inner = Parsanol::Atoms::Lookahead.new(str_a, false)
89
+ outer = Parsanol::Atoms::Lookahead.new(inner, false)
90
+ seq = Parsanol::Atoms::Sequence.new(outer, str_b)
91
+
92
+ result = Parsanol::Optimizer.simplify_lookaheads(seq)
93
+
94
+ expect(result).to be_a(Parsanol::Atoms::Sequence)
95
+ expect(result.parslets[0]).to be_a(Parsanol::Atoms::Lookahead)
96
+ expect(result.parslets[0].positive).to be true
97
+ expect(result.parslets[1]).to eq(str_b)
98
+ end
99
+
100
+ it 'optimizes lookaheads nested in alternatives' do
101
+ # Alternative(!(!a), b) => Alternative(&a, b)
102
+ inner = Parsanol::Atoms::Lookahead.new(str_a, false)
103
+ outer = Parsanol::Atoms::Lookahead.new(inner, false)
104
+ alt = Parsanol::Atoms::Alternative.new(outer, str_b)
105
+
106
+ result = Parsanol::Optimizer.simplify_lookaheads(alt)
107
+
108
+ expect(result).to be_a(Parsanol::Atoms::Alternative)
109
+ expect(result.alternatives[0]).to be_a(Parsanol::Atoms::Lookahead)
110
+ expect(result.alternatives[0].positive).to be true
111
+ expect(result.alternatives[1]).to eq(str_b)
112
+ end
113
+
114
+ it 'optimizes lookaheads nested in repetitions' do
115
+ # Repetition(!(!a)) => Repetition(&a)
116
+ inner = Parsanol::Atoms::Lookahead.new(str_a, false)
117
+ outer = Parsanol::Atoms::Lookahead.new(inner, false)
118
+ rep = Parsanol::Atoms::Repetition.new(outer, 0, nil, nil)
119
+
120
+ result = Parsanol::Optimizer.simplify_lookaheads(rep)
121
+
122
+ expect(result).to be_a(Parsanol::Atoms::Repetition)
123
+ expect(result.parslet).to be_a(Parsanol::Atoms::Lookahead)
124
+ expect(result.parslet.positive).to be true
125
+ end
126
+
127
+ it 'optimizes lookaheads nested in named atoms' do
128
+ # Named(!(!a)) => Named(&a)
129
+ inner = Parsanol::Atoms::Lookahead.new(str_a, false)
130
+ outer = Parsanol::Atoms::Lookahead.new(inner, false)
131
+ named = Parsanol::Atoms::Named.new(outer, :test)
132
+
133
+ result = Parsanol::Optimizer.simplify_lookaheads(named)
134
+
135
+ expect(result).to be_a(Parsanol::Atoms::Named)
136
+ expect(result.parslet).to be_a(Parsanol::Atoms::Lookahead)
137
+ expect(result.parslet.positive).to be true
138
+ end
139
+ end
140
+
141
+ describe 'preserving semantics' do
142
+ it 'does not modify leaf atoms' do
143
+ result = Parsanol::Optimizer.simplify_lookaheads(str_a)
144
+ expect(result).to eq(str_a)
145
+ end
146
+
147
+ it 'does not modify single lookahead' do
148
+ la = Parsanol::Atoms::Lookahead.new(str_a, true)
149
+ result = Parsanol::Optimizer.simplify_lookaheads(la)
150
+
151
+ expect(result).to eq(la)
152
+ end
153
+
154
+ it 'does not modify negative single lookahead' do
155
+ la = Parsanol::Atoms::Lookahead.new(str_a, false)
156
+ result = Parsanol::Optimizer.simplify_lookaheads(la)
157
+
158
+ expect(result).to eq(la)
159
+ end
160
+ end
161
+
162
+ describe 'structural verification' do
163
+ it 'double negation creates correct structure' do
164
+ # !(!str('a')) => &str('a')
165
+ inner = Parsanol::Atoms::Lookahead.new(str_a, false)
166
+ outer = Parsanol::Atoms::Lookahead.new(inner, false)
167
+
168
+ optimized = Parsanol::Optimizer.simplify_lookaheads(outer)
169
+
170
+ # Optimized version should be positive lookahead
171
+ expect(optimized).to be_a(Parsanol::Atoms::Lookahead)
172
+ expect(optimized.positive).to be true
173
+ expect(optimized.bound_parslet).to eq(str_a)
174
+ end
175
+
176
+ it 'idempotent positive lookahead creates correct structure' do
177
+ # &(&str('a')) => &str('a')
178
+ inner = Parsanol::Atoms::Lookahead.new(str_a, true)
179
+ outer = Parsanol::Atoms::Lookahead.new(inner, true)
180
+
181
+ optimized = Parsanol::Optimizer.simplify_lookaheads(outer)
182
+
183
+ # Should be simplified to single positive lookahead
184
+ expect(optimized).to be_a(Parsanol::Atoms::Lookahead)
185
+ expect(optimized.positive).to be true
186
+ expect(optimized.bound_parslet).to eq(str_a)
187
+ end
188
+
189
+ it 'negative of positive creates correct structure' do
190
+ # !(&str('a')) => !str('a')
191
+ inner = Parsanol::Atoms::Lookahead.new(str_a, true)
192
+ outer = Parsanol::Atoms::Lookahead.new(inner, false)
193
+
194
+ optimized = Parsanol::Optimizer.simplify_lookaheads(outer)
195
+
196
+ # Should be simplified to negative lookahead
197
+ expect(optimized).to be_a(Parsanol::Atoms::Lookahead)
198
+ expect(optimized.positive).to be false
199
+ expect(optimized.bound_parslet).to eq(str_a)
200
+ end
201
+
202
+ it 'complex nested lookaheads create correct structure' do
203
+ # Sequence(&(!(!str('a'))), str('a')) => Sequence(&str('a'), str('a'))
204
+ inner1 = Parsanol::Atoms::Lookahead.new(str_a, false)
205
+ inner2 = Parsanol::Atoms::Lookahead.new(inner1, false)
206
+ outer = Parsanol::Atoms::Lookahead.new(inner2, true)
207
+ seq = Parsanol::Atoms::Sequence.new(outer, str_a)
208
+
209
+ optimized = Parsanol::Optimizer.simplify_lookaheads(seq)
210
+
211
+ # Should have optimized lookahead in sequence
212
+ expect(optimized).to be_a(Parsanol::Atoms::Sequence)
213
+ expect(optimized.parslets[0]).to be_a(Parsanol::Atoms::Lookahead)
214
+ expect(optimized.parslets[0].positive).to be true
215
+ expect(optimized.parslets[1]).to eq(str_a)
216
+ end
217
+ end
218
+
219
+ describe 'complex optimization scenarios' do
220
+ it 'handles alternating positive and negative lookaheads' do
221
+ # !(&(!(&a))) => &a
222
+ la1 = Parsanol::Atoms::Lookahead.new(str_a, true) # &a
223
+ la2 = Parsanol::Atoms::Lookahead.new(la1, false) # !(&a) => !a
224
+ la3 = Parsanol::Atoms::Lookahead.new(la2, true) # &(!a) => !a
225
+ la4 = Parsanol::Atoms::Lookahead.new(la3, false) # !(!a) => &a
226
+
227
+ result = Parsanol::Optimizer.simplify_lookaheads(la4)
228
+
229
+ expect(result).to be_a(Parsanol::Atoms::Lookahead)
230
+ expect(result.positive).to be true
231
+ expect(result.bound_parslet).to eq(str_a)
232
+ end
233
+
234
+ it 'optimizes lookaheads at multiple levels' do
235
+ # Sequence(Alternative(!(!a), &(&b)), str('c'))
236
+ la1 = Parsanol::Atoms::Lookahead.new(str_a, false)
237
+ la2 = Parsanol::Atoms::Lookahead.new(la1, false) # Should become &a
238
+ la3 = Parsanol::Atoms::Lookahead.new(str_b, true)
239
+ la4 = Parsanol::Atoms::Lookahead.new(la3, true) # Should become &b
240
+ alt = Parsanol::Atoms::Alternative.new(la2, la4)
241
+ seq = Parsanol::Atoms::Sequence.new(alt, Parsanol::Atoms::Str.new('c'))
242
+
243
+ result = Parsanol::Optimizer.simplify_lookaheads(seq)
244
+
245
+ expect(result).to be_a(Parsanol::Atoms::Sequence)
246
+ alt_result = result.parslets[0]
247
+ expect(alt_result).to be_a(Parsanol::Atoms::Alternative)
248
+ expect(alt_result.alternatives[0].positive).to be true
249
+ expect(alt_result.alternatives[1].positive).to be true
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,29 @@
1
+ grammar MiniLisp
2
+ rule root
3
+ (expression)
4
+ end
5
+ rule expression
6
+ ((space_p) "(" (space_p) (body) ")")
7
+ end
8
+ rule space_p
9
+ (space)0*1
10
+ end
11
+ rule body
12
+ ((expression) | (identifier) | (float) | (integer) | (string))0*
13
+ end
14
+ rule space
15
+ \s1*
16
+ end
17
+ rule identifier
18
+ (([a-zA-Z=*] [a-zA-Z=*_]0*) (space_p))
19
+ end
20
+ rule float
21
+ (((integer) (("." [0-9]1*) | ("e" [0-9]1*))) (space_p))
22
+ end
23
+ rule integer
24
+ ((("+" | "-")0*1 [0-9]1*) (space_p))
25
+ end
26
+ rule string
27
+ ("\"" (("\\" .) | (!"\"" .))0* "\"" (space_p))
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ grammar MiniLisp
2
+ rule root
3
+ (expression)
4
+ end
5
+ rule expression
6
+ ((space_p) "(" (space_p) (body) ")")
7
+ end
8
+ rule space_p
9
+ (space)0..1
10
+ end
11
+ rule body
12
+ ((expression) / (identifier) / (float) / (integer) / (string))0..
13
+ end
14
+ rule space
15
+ \s1..
16
+ end
17
+ rule identifier
18
+ (([a-zA-Z=*] [a-zA-Z=*_]0..) (space_p))
19
+ end
20
+ rule float
21
+ (((integer) (("." [0-9]1..) / ("e" [0-9]1..))) (space_p))
22
+ end
23
+ rule integer
24
+ ((("+" / "-")0..1 [0-9]1..) (space_p))
25
+ end
26
+ rule string
27
+ ("\"" (("\\" .) / (!"\"" .))0.. "\"" (space_p))
28
+ end
29
+ end