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,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Parsanol::Pools::PositionPool do
6
+ let(:pool) { described_class.new(size: 10) }
7
+
8
+ describe '#initialize' do
9
+ it 'creates a PositionPool' do
10
+ expect(pool).to be_a(Parsanol::Pools::PositionPool)
11
+ end
12
+
13
+ it 'inherits from ObjectPool' do
14
+ expect(pool).to be_a(Parsanol::ObjectPool)
15
+ end
16
+ end
17
+
18
+ describe '#acquire_with' do
19
+ it 'returns a Position with specified values' do
20
+ pos = pool.acquire_with(string: "test", bytepos: 42, charpos: 42)
21
+ expect(pos).to be_a(Parsanol::Position)
22
+ expect(pos.bytepos).to eq(42)
23
+ expect(pos.charpos).to eq(42)
24
+ end
25
+
26
+ it 'works without charpos' do
27
+ pos = pool.acquire_with(string: "test", bytepos: 10)
28
+ expect(pos).to be_a(Parsanol::Position)
29
+ expect(pos.bytepos).to eq(10)
30
+ end
31
+ end
32
+
33
+ describe '#release' do
34
+ it 'resets position before returning to pool' do
35
+ pos = pool.acquire_with(string: "test", bytepos: 42, charpos: 42)
36
+ pool.release(pos)
37
+
38
+ # Next acquire should get a fresh position that can be reinitialized
39
+ pos2 = pool.acquire_with(string: "new", bytepos: 1, charpos: 1)
40
+ expect(pos2.bytepos).to eq(1)
41
+ expect(pos2.charpos).to eq(1)
42
+ end
43
+
44
+ it 'returns true if returned to pool' do
45
+ pos = pool.acquire_with(string: "test", bytepos: 0, charpos: 0)
46
+ expect(pool.release(pos)).to eq(true)
47
+ end
48
+
49
+ it 'returns false if pool is full' do
50
+ # Fill the pool to capacity
51
+ positions = 10.times.map { pool.acquire_with(string: "test", bytepos: 0) }
52
+ positions.each { |p| pool.release(p) }
53
+
54
+ # Create a new position outside the pool
55
+ external_pos = Parsanol::Position.new("external", 99, 99)
56
+
57
+ # Try to release it - should be rejected since pool is full
58
+ expect(pool.release(external_pos)).to eq(false)
59
+ end
60
+ end
61
+
62
+ describe 'reuse' do
63
+ it 'reuses positions from the pool' do
64
+ pos1 = pool.acquire_with(string: "test1", bytepos: 1, charpos: 1)
65
+ pool.release(pos1)
66
+
67
+ pos2 = pool.acquire_with(string: "test2", bytepos: 2, charpos: 2)
68
+
69
+ # Should reuse the same object
70
+ expect(pos2.object_id).to eq(pos1.object_id)
71
+ # But with new values
72
+ expect(pos2.bytepos).to eq(2)
73
+ expect(pos2.charpos).to eq(2)
74
+ end
75
+
76
+ it 'tracks reuse statistics' do
77
+ # Acquire and release to populate pool
78
+ pos1 = pool.acquire_with(string: "test", bytepos: 0)
79
+ pool.release(pos1)
80
+
81
+ # Acquire again - should be reused
82
+ pos2 = pool.acquire_with(string: "test", bytepos: 0)
83
+
84
+ stats = pool.statistics
85
+ expect(stats[:reused]).to eq(1)
86
+ expect(stats[:created]).to eq(1)
87
+ end
88
+ end
89
+
90
+ describe '#statistics' do
91
+ it 'provides utilization percentage' do
92
+ # Create one position
93
+ pos = pool.acquire_with(string: "test", bytepos: 0)
94
+ pool.release(pos)
95
+
96
+ # Reuse it
97
+ pool.acquire_with(string: "test", bytepos: 0)
98
+
99
+ stats = pool.statistics
100
+ expect(stats[:utilization]).to eq(50.0) # 1 reused out of 2 total acquires
101
+ end
102
+
103
+ it 'tracks created, reused, released, and discarded counts' do
104
+ pos1 = pool.acquire_with(string: "test", bytepos: 0)
105
+ pos2 = pool.acquire_with(string: "test", bytepos: 0)
106
+
107
+ pool.release(pos1)
108
+ pool.release(pos2)
109
+
110
+ pos3 = pool.acquire_with(string: "test", bytepos: 0)
111
+
112
+ stats = pool.statistics
113
+ expect(stats[:created]).to eq(2)
114
+ expect(stats[:reused]).to eq(1)
115
+ expect(stats[:released]).to eq(2)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,262 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Parsanol::Pools::SlicePool do
6
+ describe '#initialize' do
7
+ it 'creates a pool with default size' do
8
+ pool = described_class.new
9
+ expect(pool.size).to eq(1000)
10
+ end
11
+
12
+ it 'creates a pool with specified size' do
13
+ pool = described_class.new(size: 500)
14
+ expect(pool.size).to eq(500)
15
+ end
16
+
17
+ it 'pre-allocates Slice objects by default' do
18
+ pool = described_class.new(size: 10)
19
+ stats = pool.statistics
20
+ expect(stats[:available]).to eq(10)
21
+ end
22
+
23
+ it 'can disable pre-allocation' do
24
+ pool = described_class.new(size: 10, preallocate: false)
25
+ stats = pool.statistics
26
+ expect(stats[:available]).to eq(0)
27
+ end
28
+ end
29
+
30
+ describe '#acquire' do
31
+ it 'returns a Slice instance' do
32
+ pool = described_class.new(size: 10, preallocate: false)
33
+ slice = pool.acquire
34
+ expect(slice).to be_a(Parsanol::Slice)
35
+ end
36
+
37
+ it 'reuses slices from the pool' do
38
+ pool = described_class.new(size: 10, preallocate: true)
39
+ slice1 = pool.acquire
40
+ slice1_id = slice1.object_id
41
+ pool.release(slice1)
42
+
43
+ slice2 = pool.acquire
44
+ expect(slice2.object_id).to eq(slice1_id)
45
+ end
46
+
47
+ it 'creates new slices when pool is empty' do
48
+ pool = described_class.new(size: 2, preallocate: false)
49
+ slice1 = pool.acquire
50
+ slice2 = pool.acquire
51
+
52
+ stats = pool.statistics
53
+ expect(stats[:created]).to eq(2)
54
+ end
55
+ end
56
+
57
+ describe '#acquire_with' do
58
+ it 'acquires and initializes slice in one step' do
59
+ pool = described_class.new(size: 10, preallocate: false)
60
+ slice = pool.acquire_with(42, "hello")
61
+
62
+ expect(slice).to be_a(Parsanol::Slice)
63
+ expect(slice.offset).to eq(42)
64
+ expect(slice.to_s).to eq("hello")
65
+ end
66
+
67
+ it 'accepts optional line cache' do
68
+ pool = described_class.new(size: 10, preallocate: false)
69
+ line_cache = double('line_cache')
70
+ slice = pool.acquire_with(0, "test", line_cache)
71
+
72
+ expect(slice.line_cache).to eq(line_cache)
73
+ end
74
+
75
+ it 'reuses slices with new values' do
76
+ pool = described_class.new(size: 10, preallocate: false)
77
+
78
+ # First use
79
+ slice1 = pool.acquire_with(0, "first")
80
+ expect(slice1.to_s).to eq("first")
81
+ slice1_id = slice1.object_id
82
+
83
+ # Return to pool
84
+ pool.release(slice1)
85
+
86
+ # Reuse with different values
87
+ slice2 = pool.acquire_with(10, "second")
88
+ expect(slice2.object_id).to eq(slice1_id) # Same object
89
+ expect(slice2.to_s).to eq("second") # Different content
90
+ expect(slice2.offset).to eq(10) # Different position
91
+ end
92
+
93
+ it 'properly initializes all slice attributes' do
94
+ pool = described_class.new(size: 10, preallocate: false)
95
+ line_cache = double('line_cache')
96
+
97
+ slice = pool.acquire_with(123, "content", line_cache)
98
+
99
+ expect(slice.offset).to eq(123)
100
+ expect(slice.to_s).to eq("content")
101
+ expect(slice.line_cache).to eq(line_cache)
102
+ end
103
+ end
104
+
105
+ describe '#release' do
106
+ it 'returns slice to pool' do
107
+ pool = described_class.new(size: 10, preallocate: false)
108
+ slice = pool.acquire
109
+
110
+ pool.release(slice)
111
+
112
+ stats = pool.statistics
113
+ expect(stats[:available]).to eq(1)
114
+ end
115
+
116
+ it 'resets slice before pooling' do
117
+ pool = described_class.new(size: 10, preallocate: false)
118
+ slice = pool.acquire_with(99, "original")
119
+
120
+ pool.release(slice)
121
+
122
+ # Slice should be reset to defaults
123
+ expect(slice.offset).to eq(0)
124
+ expect(slice.to_s).to eq("")
125
+ expect(slice.line_cache).to be_nil
126
+ end
127
+
128
+ it 'discards slices when pool is full' do
129
+ pool = described_class.new(size: 1, preallocate: true)
130
+
131
+ new_slice = Parsanol::Slice.new(0, "extra")
132
+ result = pool.release(new_slice)
133
+
134
+ expect(result).to be false
135
+ stats = pool.statistics
136
+ expect(stats[:discarded]).to eq(1)
137
+ end
138
+ end
139
+
140
+ describe 'integration with Slice' do
141
+ it 'works with real parsing scenario' do
142
+ pool = described_class.new(size: 5, preallocate: false)
143
+
144
+ # Simulate parsing multiple tokens
145
+ slices = []
146
+
147
+ # Parse "hello world"
148
+ slices << pool.acquire_with(0, "hello")
149
+ slices << pool.acquire_with(6, "world")
150
+
151
+ expect(slices[0].to_s).to eq("hello")
152
+ expect(slices[1].to_s).to eq("world")
153
+
154
+ # Release all slices
155
+ slices.each { |s| pool.release(s) }
156
+
157
+ # Pool should have 2 slices available
158
+ stats = pool.statistics
159
+ expect(stats[:available]).to eq(2)
160
+ expect(stats[:created]).to eq(2)
161
+ expect(stats[:reused]).to eq(0)
162
+ end
163
+
164
+ it 'maintains slice equality after pooling' do
165
+ pool = described_class.new(size: 5, preallocate: false)
166
+
167
+ slice1 = pool.acquire_with(0, "test")
168
+ pool.release(slice1)
169
+
170
+ slice2 = pool.acquire_with(0, "test")
171
+
172
+ # Should be same object
173
+ expect(slice1.object_id).to eq(slice2.object_id)
174
+ # Should be equal
175
+ expect(slice1).to eq(slice2)
176
+ end
177
+ end
178
+
179
+ describe 'performance characteristics' do
180
+ it 'shows high reuse rate with pooling' do
181
+ pool = described_class.new(size: 100, preallocate: false)
182
+
183
+ # Create initial slices
184
+ slices = 50.times.map { |i| pool.acquire_with(i, "slice#{i}") }
185
+
186
+ # Release all
187
+ slices.each { |s| pool.release(s) }
188
+
189
+ # Acquire again - should all be reused
190
+ 50.times { pool.acquire }
191
+
192
+ stats = pool.statistics
193
+ expect(stats[:created]).to eq(50)
194
+ expect(stats[:reused]).to eq(50)
195
+ expect(stats[:utilization]).to eq(50.0)
196
+ end
197
+
198
+ it 'handles rapid acquire/release cycles' do
199
+ pool = described_class.new(size: 10, preallocate: false)
200
+
201
+ 100.times do |i|
202
+ slice = pool.acquire_with(i, "test#{i}")
203
+ pool.release(slice)
204
+ end
205
+
206
+ stats = pool.statistics
207
+ # Should have high reuse, low creation
208
+ expect(stats[:created]).to be <= 10
209
+ expect(stats[:reused]).to be >= 90
210
+ end
211
+ end
212
+
213
+ describe 'statistics' do
214
+ it 'tracks slice-specific operations' do
215
+ pool = described_class.new(size: 5, preallocate: false)
216
+
217
+ # Create 3 slices
218
+ s1 = pool.acquire_with(0, "a")
219
+ s2 = pool.acquire_with(1, "b")
220
+ s3 = pool.acquire_with(2, "c")
221
+
222
+ # Release 2
223
+ pool.release(s1)
224
+ pool.release(s2)
225
+
226
+ # Reuse 1
227
+ pool.acquire
228
+
229
+ stats = pool.statistics
230
+ expect(stats[:created]).to eq(3)
231
+ expect(stats[:reused]).to eq(1)
232
+ expect(stats[:released]).to eq(2)
233
+ expect(stats[:available]).to eq(1)
234
+ end
235
+ end
236
+
237
+ describe 'edge cases' do
238
+ it 'handles empty string slices' do
239
+ pool = described_class.new(size: 5, preallocate: false)
240
+ slice = pool.acquire_with(0, "")
241
+
242
+ expect(slice.to_s).to eq("")
243
+ expect(slice.size).to eq(0)
244
+ end
245
+
246
+ it 'handles large string slices' do
247
+ pool = described_class.new(size: 5, preallocate: false)
248
+ large_str = "x" * 10000
249
+ slice = pool.acquire_with(0, large_str)
250
+
251
+ expect(slice.to_s).to eq(large_str)
252
+ expect(slice.size).to eq(10000)
253
+ end
254
+
255
+ it 'handles unicode content' do
256
+ pool = described_class.new(size: 5, preallocate: false)
257
+ slice = pool.acquire_with(0, "hello 世界 мир")
258
+
259
+ expect(slice.to_s).to eq("hello 世界 мир")
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,14 @@
1
+ # Encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Parsanol::Position do
6
+ slet(:position) { described_class.new("öäüö", 4, 2) }
7
+
8
+ it 'should have a charpos of 2' do
9
+ position.charpos.should == 2
10
+ end
11
+ it 'should have a bytepos of 4' do
12
+ position.bytepos.should == 4
13
+ end
14
+ end