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,205 @@
1
+ require 'spec_helper'
2
+ require 'parsanol/interval_tree'
3
+
4
+ describe Parsanol::IntervalTree do
5
+ let(:tree) { Parsanol::IntervalTree.new }
6
+
7
+ describe '#initialize' do
8
+ it 'creates an empty tree' do
9
+ expect(tree.empty?).to be true
10
+ expect(tree.size).to eq 0
11
+ end
12
+ end
13
+
14
+ describe '#insert and #query_exact' do
15
+ it 'inserts and retrieves a single interval' do
16
+ tree.insert(0, 10, 'data1')
17
+ expect(tree.size).to eq 1
18
+ expect(tree.query_exact(0, 10)).to eq 'data1'
19
+ end
20
+
21
+ it 'returns nil for non-existent intervals' do
22
+ tree.insert(0, 10, 'data1')
23
+ expect(tree.query_exact(0, 5)).to be_nil
24
+ expect(tree.query_exact(5, 10)).to be_nil
25
+ end
26
+
27
+ it 'handles multiple insertions' do
28
+ tree.insert(0, 10, 'data1')
29
+ tree.insert(20, 30, 'data2')
30
+ tree.insert(10, 20, 'data3')
31
+
32
+ expect(tree.size).to eq 3
33
+ expect(tree.query_exact(0, 10)).to eq 'data1'
34
+ expect(tree.query_exact(20, 30)).to eq 'data2'
35
+ expect(tree.query_exact(10, 20)).to eq 'data3'
36
+ end
37
+ end
38
+
39
+ describe '#query_overlapping' do
40
+ before do
41
+ # Create intervals: [0,10), [5,15), [20,30), [25,35)
42
+ tree.insert(0, 10, 'interval1')
43
+ tree.insert(5, 15, 'interval2')
44
+ tree.insert(20, 30, 'interval3')
45
+ tree.insert(25, 35, 'interval4')
46
+ end
47
+
48
+ it 'finds intervals that overlap with query range' do
49
+ # Query [0,5) should overlap with [0,10)
50
+ results = tree.query_overlapping(0, 5)
51
+ expect(results).to include('interval1')
52
+ expect(results.size).to eq 1
53
+ end
54
+
55
+ it 'finds multiple overlapping intervals' do
56
+ # Query [7,12) should overlap with [0,10) and [5,15)
57
+ results = tree.query_overlapping(7, 12)
58
+ expect(results).to include('interval1', 'interval2')
59
+ expect(results.size).to eq 2
60
+ end
61
+
62
+ it 'returns empty array when no overlaps' do
63
+ # Query [15,20) should have no overlaps
64
+ results = tree.query_overlapping(15, 20)
65
+ expect(results).to be_empty
66
+ end
67
+
68
+ it 'finds overlaps at boundaries' do
69
+ # Query [22,28) should overlap with [20,30) and [25,35)
70
+ results = tree.query_overlapping(22, 28)
71
+ expect(results).to include('interval3', 'interval4')
72
+ expect(results.size).to eq 2
73
+ end
74
+
75
+ it 'handles query that encompasses multiple intervals' do
76
+ # Query [0,40) should overlap with all intervals
77
+ results = tree.query_overlapping(0, 40)
78
+ expect(results.size).to eq 4
79
+ end
80
+
81
+ it 'handles point queries (zero-length intervals)' do
82
+ # Query [8,8) is empty, should have no overlaps
83
+ results = tree.query_overlapping(8, 8)
84
+ expect(results).to be_empty
85
+ end
86
+ end
87
+
88
+ describe '#delete_overlapping' do
89
+ before do
90
+ tree.insert(0, 10, 'interval1')
91
+ tree.insert(5, 15, 'interval2')
92
+ tree.insert(20, 30, 'interval3')
93
+ tree.insert(25, 35, 'interval4')
94
+ end
95
+
96
+ it 'deletes overlapping intervals' do
97
+ # Delete intervals overlapping with [7,12)
98
+ deleted = tree.delete_overlapping(7, 12)
99
+
100
+ expect(deleted).to include('interval1', 'interval2')
101
+ expect(deleted.size).to eq 2
102
+ expect(tree.size).to eq 2
103
+
104
+ # Verify they're actually deleted
105
+ expect(tree.query_exact(0, 10)).to be_nil
106
+ expect(tree.query_exact(5, 15)).to be_nil
107
+
108
+ # Verify others remain
109
+ expect(tree.query_exact(20, 30)).to eq 'interval3'
110
+ expect(tree.query_exact(25, 35)).to eq 'interval4'
111
+ end
112
+
113
+ it 'returns empty array when no overlaps to delete' do
114
+ deleted = tree.delete_overlapping(15, 20)
115
+ expect(deleted).to be_empty
116
+ expect(tree.size).to eq 4
117
+ end
118
+
119
+ it 'can delete all intervals' do
120
+ deleted = tree.delete_overlapping(0, 40)
121
+ expect(deleted.size).to eq 4
122
+ expect(tree.empty?).to be true
123
+ end
124
+ end
125
+
126
+ describe '#clear' do
127
+ it 'removes all intervals' do
128
+ tree.insert(0, 10, 'data1')
129
+ tree.insert(20, 30, 'data2')
130
+
131
+ tree.clear
132
+
133
+ expect(tree.empty?).to be true
134
+ expect(tree.size).to eq 0
135
+ expect(tree.query_exact(0, 10)).to be_nil
136
+ end
137
+ end
138
+
139
+ describe 'complex scenarios' do
140
+ it 'handles many intervals efficiently' do
141
+ # Insert 100 intervals
142
+ 100.times do |i|
143
+ tree.insert(i * 10, i * 10 + 15, "data#{i}")
144
+ end
145
+
146
+ expect(tree.size).to eq 100
147
+
148
+ # Query should find overlapping intervals efficiently
149
+ results = tree.query_overlapping(50, 65)
150
+ expect(results.size).to be > 0
151
+ end
152
+
153
+ it 'handles overlapping insertions correctly' do
154
+ tree.insert(0, 100, 'big')
155
+ tree.insert(10, 20, 'small1')
156
+ tree.insert(30, 40, 'small2')
157
+ tree.insert(50, 60, 'small3')
158
+
159
+ # Query in middle should find big + one small
160
+ results = tree.query_overlapping(15, 25)
161
+ expect(results).to include('big', 'small1')
162
+ expect(results.size).to eq 2
163
+ end
164
+
165
+ it 'maintains correctness after deletions' do
166
+ # Insert intervals
167
+ tree.insert(0, 10, 'a')
168
+ tree.insert(20, 30, 'b')
169
+ tree.insert(40, 50, 'c')
170
+ tree.insert(60, 70, 'd')
171
+
172
+ # Delete middle range - should delete b and c
173
+ deleted = tree.delete_overlapping(15, 55)
174
+
175
+ # Should have deleted b and c
176
+ expect(deleted).to include('b', 'c')
177
+ expect(deleted.size).to eq 2
178
+ expect(tree.query_exact(20, 30)).to be_nil
179
+ expect(tree.query_exact(40, 50)).to be_nil
180
+
181
+ # Should still have a and d
182
+ expect(tree.query_exact(0, 10)).to eq 'a'
183
+ expect(tree.query_exact(60, 70)).to eq 'd'
184
+ end
185
+ end
186
+
187
+ describe 'edge cases' do
188
+ it 'handles zero-length intervals' do
189
+ tree.insert(10, 10, 'zero-length')
190
+ expect(tree.size).to eq 1
191
+ expect(tree.query_exact(10, 10)).to eq 'zero-length'
192
+ end
193
+
194
+ it 'handles large position values' do
195
+ large_pos = 1_000_000
196
+ tree.insert(large_pos, large_pos + 100, 'large')
197
+ expect(tree.query_exact(large_pos, large_pos + 100)).to eq 'large'
198
+ end
199
+
200
+ it 'handles negative positions' do
201
+ tree.insert(-10, 0, 'negative')
202
+ expect(tree.query_exact(-10, 0)).to eq 'negative'
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,288 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Parsanol::LazyResult do
6
+ let(:context) { Parsanol::Atoms::Context.new }
7
+ let(:buffer) do
8
+ buf = context.acquire_buffer(size: 4)
9
+ buf.push("a")
10
+ buf.push("b")
11
+ buf.push("c")
12
+ buf
13
+ end
14
+ let(:lazy) { described_class.new(buffer, context) }
15
+
16
+ describe '#initialize' do
17
+ it 'stores buffer and context' do
18
+ expect(lazy.buffer).to eq(buffer)
19
+ expect(lazy.context).to eq(context)
20
+ expect(lazy.materialized).to be_nil
21
+ end
22
+ end
23
+
24
+ describe '#to_a' do
25
+ it 'materializes buffer to array' do
26
+ result = lazy.to_a
27
+ expect(result).to be_a(Array)
28
+ expect(result).to eq(["a", "b", "c"])
29
+ end
30
+
31
+ it 'caches materialized array' do
32
+ arr1 = lazy.to_a
33
+ arr2 = lazy.to_a
34
+ expect(arr2.object_id).to eq(arr1.object_id)
35
+ end
36
+
37
+ it 'only materializes once' do
38
+ expect(buffer).to receive(:to_a).once.and_call_original
39
+ lazy.to_a
40
+ lazy.to_a
41
+ lazy.to_a
42
+ end
43
+ end
44
+
45
+ describe '#[]' do
46
+ it 'accesses elements by index' do
47
+ expect(lazy[0]).to eq("a")
48
+ expect(lazy[1]).to eq("b")
49
+ expect(lazy[2]).to eq("c")
50
+ end
51
+
52
+ it 'materializes on first access' do
53
+ expect(lazy.materialized).to be_nil
54
+ lazy[0]
55
+ expect(lazy.materialized).not_to be_nil
56
+ end
57
+
58
+ it 'returns nil for out of bounds index' do
59
+ expect(lazy[10]).to be_nil
60
+ end
61
+ end
62
+
63
+ describe '#size' do
64
+ it 'returns buffer size without materializing' do
65
+ expect(lazy.size).to eq(3)
66
+ expect(lazy.materialized).to be_nil
67
+ end
68
+ end
69
+
70
+ describe '#length' do
71
+ it 'returns buffer size without materializing' do
72
+ expect(lazy.length).to eq(3)
73
+ expect(lazy.materialized).to be_nil
74
+ end
75
+ end
76
+
77
+ describe '#empty?' do
78
+ it 'returns false for non-empty buffer' do
79
+ expect(lazy.empty?).to be false
80
+ expect(lazy.materialized).to be_nil
81
+ end
82
+
83
+ it 'returns true for empty buffer' do
84
+ empty_buffer = context.acquire_buffer(size: 2)
85
+ empty_lazy = described_class.new(empty_buffer, context)
86
+ expect(empty_lazy.empty?).to be true
87
+ end
88
+ end
89
+
90
+ describe '#each' do
91
+ it 'iterates over elements' do
92
+ result = []
93
+ lazy.each { |e| result << e }
94
+ expect(result).to eq(["a", "b", "c"])
95
+ end
96
+
97
+ it 'returns enumerator without block' do
98
+ enum = lazy.each
99
+ expect(enum).to be_a(Enumerator)
100
+ expect(enum.to_a).to eq(["a", "b", "c"])
101
+ end
102
+
103
+ it 'returns self when block given' do
104
+ result = lazy.each { |e| }
105
+ expect(result).to eq(lazy)
106
+ end
107
+ end
108
+
109
+ describe '#is_a?' do
110
+ it 'reports as Array' do
111
+ expect(lazy.is_a?(Array)).to be true
112
+ end
113
+
114
+ it 'reports as LazyResult' do
115
+ expect(lazy.is_a?(Parsanol::LazyResult)).to be true
116
+ end
117
+
118
+ it 'reports as Object' do
119
+ expect(lazy.is_a?(Object)).to be true
120
+ end
121
+ end
122
+
123
+ describe '#kind_of?' do
124
+ it 'is an alias for is_a?' do
125
+ expect(lazy.kind_of?(Array)).to be true
126
+ expect(lazy.kind_of?(Parsanol::LazyResult)).to be true
127
+ end
128
+ end
129
+
130
+ describe '#respond_to?' do
131
+ it 'responds to array methods' do
132
+ expect(lazy.respond_to?(:map)).to be true
133
+ expect(lazy.respond_to?(:select)).to be true
134
+ expect(lazy.respond_to?(:first)).to be true
135
+ expect(lazy.respond_to?(:last)).to be true
136
+ end
137
+
138
+ it 'responds to LazyResult methods' do
139
+ expect(lazy.respond_to?(:to_a)).to be true
140
+ expect(lazy.respond_to?(:size)).to be true
141
+ expect(lazy.respond_to?(:empty?)).to be true
142
+ end
143
+ end
144
+
145
+ describe '#method_missing' do
146
+ it 'delegates map to materialized array' do
147
+ expect(lazy.map { |e| e.upcase }).to eq(["A", "B", "C"])
148
+ end
149
+
150
+ it 'delegates select to materialized array' do
151
+ expect(lazy.select { |e| e > "a" }).to eq(["b", "c"])
152
+ end
153
+
154
+ it 'delegates first to materialized array' do
155
+ expect(lazy.first).to eq("a")
156
+ end
157
+
158
+ it 'delegates last to materialized array' do
159
+ expect(lazy.last).to eq("c")
160
+ end
161
+
162
+ it 'delegates join to materialized array' do
163
+ expect(lazy.join(", ")).to eq("a, b, c")
164
+ end
165
+
166
+ it 'raises NoMethodError for unknown methods' do
167
+ expect { lazy.unknown_method }.to raise_error(NoMethodError)
168
+ end
169
+ end
170
+
171
+ describe '#respond_to_missing?' do
172
+ it 'returns true for array methods' do
173
+ expect(lazy.respond_to?(:map)).to be true
174
+ expect(lazy.respond_to?(:flatten)).to be true
175
+ end
176
+
177
+ it 'returns false for non-existent methods' do
178
+ expect(lazy.respond_to?(:nonexistent_method)).to be false
179
+ end
180
+ end
181
+
182
+ describe '#==' do
183
+ it 'compares equal to array with same content' do
184
+ expect(lazy == ["a", "b", "c"]).to be true
185
+ expect(lazy).to eq(["a", "b", "c"])
186
+ end
187
+
188
+ it 'compares not equal to array with different content' do
189
+ expect(lazy == ["x", "y", "z"]).to be false
190
+ expect(lazy).not_to eq(["x", "y", "z"])
191
+ end
192
+
193
+ it 'compares equal to another LazyResult with same content' do
194
+ buffer2 = context.acquire_buffer(size: 4)
195
+ buffer2.push("a")
196
+ buffer2.push("b")
197
+ buffer2.push("c")
198
+ lazy2 = described_class.new(buffer2, context)
199
+
200
+ expect(lazy == lazy2).to be true
201
+ expect(lazy).to eq(lazy2)
202
+ end
203
+
204
+ it 'works with rspec eq matcher' do
205
+ expect(lazy).to eq(["a", "b", "c"])
206
+ end
207
+ end
208
+
209
+ describe '#eql?' do
210
+ it 'is an alias for ==' do
211
+ expect(lazy.eql?(["a", "b", "c"])).to be true
212
+ end
213
+ end
214
+
215
+ describe '#hash' do
216
+ it 'returns same hash as equivalent array' do
217
+ array = ["a", "b", "c"]
218
+ expect(lazy.hash).to eq(array.hash)
219
+ end
220
+ end
221
+
222
+ describe 'materialization behavior' do
223
+ it 'defers materialization until needed' do
224
+ new_lazy = described_class.new(buffer, context)
225
+ expect(new_lazy.materialized).to be_nil
226
+
227
+ # These don't materialize
228
+ new_lazy.size
229
+ new_lazy.empty?
230
+ new_lazy.length
231
+ expect(new_lazy.materialized).to be_nil
232
+
233
+ # This does materialize
234
+ new_lazy.to_a
235
+ expect(new_lazy.materialized).not_to be_nil
236
+ end
237
+
238
+ it 'materializes on array method calls' do
239
+ new_lazy = described_class.new(buffer, context)
240
+ expect(new_lazy.materialized).to be_nil
241
+
242
+ # Array access materializes
243
+ new_lazy[0]
244
+ expect(new_lazy.materialized).not_to be_nil
245
+ end
246
+
247
+ it 'materializes on enumerable methods' do
248
+ new_lazy = described_class.new(buffer, context)
249
+ expect(new_lazy.materialized).to be_nil
250
+
251
+ # map materializes
252
+ new_lazy.map { |x| x }
253
+ expect(new_lazy.materialized).not_to be_nil
254
+ end
255
+ end
256
+
257
+ describe '#inspect' do
258
+ it 'shows buffer size when not materialized' do
259
+ new_lazy = described_class.new(buffer, context)
260
+ expect(new_lazy.inspect).to include("buffer.size=3")
261
+ expect(new_lazy.inspect).not_to include("materialized=")
262
+ end
263
+
264
+ it 'shows materialized array after materialization' do
265
+ lazy.to_a
266
+ expect(lazy.inspect).to include("materialized=")
267
+ expect(lazy.inspect).to include('["a", "b", "c"]')
268
+ end
269
+ end
270
+
271
+ describe 'integration with parsing' do
272
+ it 'can be used in place of arrays' do
273
+ # LazyResult should act like an array in all contexts
274
+ result_array = lazy.to_a
275
+ expect(lazy.size).to eq(result_array.size)
276
+ expect(lazy.first).to eq(result_array.first)
277
+ expect(lazy.last).to eq(result_array.last)
278
+ expect(lazy.empty?).to eq(result_array.empty?)
279
+ end
280
+
281
+ it 'supports array destructuring' do
282
+ a, b, c = lazy
283
+ expect(a).to eq("a")
284
+ expect(b).to eq("b")
285
+ expect(c).to eq("c")
286
+ end
287
+ end
288
+ end