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,249 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parsanol::Slice do
4
+ def cslice(string, bytepos, cache = nil)
5
+ described_class.new(
6
+ bytepos,
7
+ string,
8
+ cache
9
+ )
10
+ end
11
+
12
+ describe 'construction' do
13
+ it 'constructs from a byte position and a string' do
14
+ cslice("foobar", 40)
15
+ end
16
+ end
17
+
18
+ context "('foobar', 40, 'foobar')" do
19
+ let(:slice) { cslice('foobar', 40) }
20
+
21
+ describe 'comparison' do
22
+ it 'is equal to other slices with the same attributes' do
23
+ other = cslice('foobar', 40)
24
+ slice.should == other
25
+ other.should == slice
26
+ end
27
+
28
+ it "is equal to other slices (offset is irrelevant for comparison)" do
29
+ other = cslice("foobar", 41)
30
+ slice.should == other
31
+ other.should == slice
32
+ end
33
+
34
+ it 'is equal to a string with the same content' do
35
+ slice.should == 'foobar'
36
+
37
+ end
38
+
39
+ it 'is equal to a string (inversed operands)' do
40
+ 'foobar'.should == slice
41
+ end
42
+
43
+ it 'is not equal to a string' do
44
+ slice.should_not equal('foobar')
45
+ end
46
+
47
+ it 'is not eql to a string' do
48
+ # In Opal, eql? must handle String comparison for Hash/Array equality
49
+ skip if RUBY_ENGINE == 'opal'
50
+
51
+ slice.should_not eql('foobar')
52
+ end
53
+
54
+ it 'does not hash to the same number' do
55
+ slice.hash.should_not == 'foobar'.hash
56
+ end
57
+ end
58
+
59
+ describe 'offset' do
60
+ it 'returns the associated offset' do
61
+ slice.offset.should == 40
62
+ end
63
+
64
+ it 'fails to return a line and column' do
65
+ lambda {
66
+ slice.line_and_column
67
+ }.should raise_error(ArgumentError)
68
+ end
69
+
70
+ context 'when constructed with a source' do
71
+ let(:cache) { double(:cache, line_and_column: [13, 14]) }
72
+ let(:slice) { cslice('foobar', 40, cache) }
73
+
74
+ it 'returns proper line and column' do
75
+ slice.line_and_column.should == [13, 14]
76
+ end
77
+ end
78
+ end
79
+
80
+ describe '#bytepos' do
81
+ it 'returns byte position' do
82
+ slice.bytepos.should == 40
83
+ end
84
+ end
85
+
86
+ describe '#charpos' do
87
+ it 'returns same as offset (bytepos)' do
88
+ slice.charpos.should == 40
89
+ end
90
+ end
91
+
92
+ describe 'string methods' do
93
+ describe 'matching' do
94
+ it 'matches as a string would' do
95
+ slice.should match(/bar/)
96
+ slice.should match(/foo/)
97
+
98
+ md = slice.match(/f(o)o/)
99
+ md.captures.first.should == 'o'
100
+ end
101
+ end
102
+
103
+ describe '<- #size' do
104
+ subject { slice.size }
105
+
106
+ it { is_expected.to eq(6) }
107
+ end
108
+
109
+ describe '<- #length' do
110
+ subject { slice.length }
111
+
112
+ it { is_expected.to eq(6) }
113
+ end
114
+
115
+ describe '<- #+' do
116
+ subject { slice + other }
117
+
118
+ let(:other) { cslice("baz", 10) }
119
+
120
+ it 'concats like string does' do
121
+ subject.size.should == 9
122
+ subject.should == 'foobarbaz'
123
+ subject.offset.should == 40
124
+ end
125
+ end
126
+ end
127
+
128
+ describe 'conversion' do
129
+ describe '<- #to_slice' do
130
+ it 'returns self' do
131
+ slice.to_slice.should eq(slice)
132
+ end
133
+ end
134
+
135
+ describe '<- #to_sym' do
136
+ it 'returns :foobar' do
137
+ slice.to_sym.should == :foobar
138
+ end
139
+ end
140
+
141
+ describe 'cast to Float' do
142
+ it 'returns a float' do
143
+ Float(cslice('1.345', 11)).should == 1.345
144
+ end
145
+ end
146
+
147
+ describe 'cast to Integer' do
148
+ it 'casts to integer as a string would' do
149
+ s = cslice('1234', 40)
150
+ Integer(s).should == 1234
151
+ s.to_i.should == 1234
152
+ end
153
+
154
+ it 'fails when Integer would fail on a string' do
155
+ -> { Integer(slice.to_s) }.should raise_error(ArgumentError, /invalid value/)
156
+ end
157
+
158
+ it 'turns into zero when a string would' do
159
+ slice.to_i.should == 0
160
+ end
161
+ end
162
+ end
163
+
164
+ describe 'inspection and string conversion' do
165
+ describe '#inspect' do
166
+ subject { slice.inspect }
167
+
168
+
169
+ it {
170
+ # For Opal we have redefined inspect to return the string itself
171
+ skip if RUBY_ENGINE == 'opal'
172
+
173
+ is_expected.to eq('"foobar"@40')
174
+ }
175
+ end
176
+
177
+ describe '#to_s' do
178
+ subject { slice.to_s }
179
+
180
+ it { is_expected.to eq('foobar') }
181
+ end
182
+ end
183
+
184
+ describe 'serializability' do
185
+ it 'serializes' do
186
+ Marshal.dump(slice)
187
+ end
188
+
189
+ context 'when storing a line cache' do
190
+ let(:slice) { cslice('foobar', 40, Parsanol::Source::LineCache.new) }
191
+
192
+ it 'serializes' do
193
+ Marshal.dump(slice)
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ describe '.from_rope' do
200
+ let(:bytepos) { 0 }
201
+
202
+ it 'creates slice from rope' do
203
+ rope = Parsanol::Rope.new.append('hello').append(' world')
204
+ slice = described_class.from_rope(rope, bytepos)
205
+ expect(slice.str).to eq('hello world')
206
+ expect(slice.offset).to eq(0)
207
+ end
208
+
209
+ it 'handles empty rope' do
210
+ rope = Parsanol::Rope.new
211
+ slice = described_class.from_rope(rope, bytepos)
212
+ expect(slice.str).to eq('')
213
+ expect(slice.offset).to eq(0)
214
+ end
215
+
216
+ it 'handles rope with single segment' do
217
+ rope = Parsanol::Rope.new.append('single')
218
+ slice = described_class.from_rope(rope, bytepos)
219
+ expect(slice.str).to eq('single')
220
+ end
221
+
222
+ it 'preserves position information' do
223
+ rope = Parsanol::Rope.new.append('test')
224
+ slice = described_class.from_rope(rope, 5)
225
+ expect(slice.offset).to eq(5)
226
+ end
227
+
228
+ it 'preserves line cache' do
229
+ cache = double(:cache, line_and_column: [10, 15])
230
+ rope = Parsanol::Rope.new.append('test')
231
+ slice = described_class.from_rope(rope, 0, cache)
232
+ expect(slice.line_and_column).to eq([10, 15])
233
+ end
234
+
235
+ it 'handles rope with Slice segments' do
236
+ rope = Parsanol::Rope.new
237
+ rope.append(cslice('hello', 0))
238
+ rope.append(cslice(' world', 5))
239
+ slice = described_class.from_rope(rope, bytepos)
240
+ expect(slice.str).to eq('hello world')
241
+ end
242
+
243
+ it 'creates a proper Slice instance' do
244
+ rope = Parsanol::Rope.new.append('test')
245
+ slice = described_class.from_rope(rope, bytepos)
246
+ expect(slice).to be_a(Parsanol::Slice)
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parsanol::Source::RangeSearch do
4
+ describe "<- #lbound" do
5
+ context "for a simple array" do
6
+ let(:ary) { [10, 20, 30, 40, 50] }
7
+ before(:each) { ary.extend Parsanol::Source::RangeSearch }
8
+
9
+ it "should return correct answers for numbers not in the array" do
10
+ ary.lbound(5).should == 0
11
+ ary.lbound(15).should == 1
12
+ ary.lbound(25).should == 2
13
+ ary.lbound(35).should == 3
14
+ ary.lbound(45).should == 4
15
+ end
16
+ it "should return correct answers for numbers in the array" do
17
+ ary.lbound(10).should == 1
18
+ ary.lbound(20).should == 2
19
+ ary.lbound(30).should == 3
20
+ ary.lbound(40).should == 4
21
+ end
22
+ it "should cover right edge case" do
23
+ ary.lbound(50).should be_nil
24
+ ary.lbound(51).should be_nil
25
+ end
26
+ it "should cover left edge case" do
27
+ ary.lbound(0).should == 0
28
+ end
29
+ end
30
+ context "for an empty array" do
31
+ let(:ary) { [] }
32
+ before(:each) { ary.extend Parsanol::Source::RangeSearch }
33
+
34
+ it "should return nil" do
35
+ ary.lbound(1).should be_nil
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ describe Parsanol::Source::LineCache do
42
+ describe "<- scan_for_line_endings" do
43
+ context "calculating the line_and_columns" do
44
+ let(:str) { "foo\nbar\nbazd" }
45
+
46
+ it "should return the first line if we have no line ends" do
47
+ subject.scan_for_line_endings(0, nil)
48
+ subject.line_and_column(3).should == [1, 4]
49
+
50
+ subject.scan_for_line_endings(0, "")
51
+ subject.line_and_column(5).should == [1, 6]
52
+ end
53
+
54
+ it "should find the right line starting from pos 0" do
55
+ subject.scan_for_line_endings(0, str)
56
+ subject.line_and_column(5).should == [2, 2]
57
+ subject.line_and_column(9).should == [3, 2]
58
+ end
59
+
60
+ it "should find the right line starting from pos 5" do
61
+ subject.scan_for_line_endings(5, str)
62
+ subject.line_and_column(11).should == [2, 3]
63
+ end
64
+
65
+ it "should find the right line if scannning the string multiple times" do
66
+ subject.scan_for_line_endings(0, str)
67
+ subject.scan_for_line_endings(0, "#{str}\nthe quick\nbrown fox")
68
+ subject.line_and_column(10).should == [3,3]
69
+ subject.line_and_column(24).should == [5,2]
70
+ end
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,207 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parsanol::Source do
4
+ describe 'using simple input' do
5
+ let(:str) { 'a' * 100 + "\n" + 'a' * 100 + "\n" }
6
+ let(:source) { described_class.new(str) }
7
+
8
+ describe '<- #read(n)' do
9
+ it 'does not raise error when the return value is nil' do
10
+ described_class.new('').consume(1)
11
+ end
12
+
13
+ it "returns 100 'a's when reading 100 chars" do
14
+ source.consume(100).should == 'a' * 100
15
+ end
16
+ end
17
+
18
+ describe '<- #chars_left' do
19
+ subject { source.chars_left }
20
+
21
+ it { is_expected.to eq(202) }
22
+
23
+ context 'after depleting the source' do
24
+ before { source.consume(10_000) }
25
+
26
+ it { is_expected.to eq(0) }
27
+ end
28
+ end
29
+
30
+ describe '<- #pos' do
31
+ subject { source.pos }
32
+
33
+ it { is_expected.to eq(0) }
34
+
35
+ context 'after reading a few bytes' do
36
+ it 'stills be correct' do
37
+ pos = 0
38
+ 10.times do
39
+ pos += (n = rand(1..10))
40
+ source.consume(n)
41
+
42
+ source.pos.should == pos
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ describe '<- #pos=(n)' do
49
+ subject { source.pos }
50
+
51
+ 10.times do
52
+ pos = rand(200)
53
+ context "setting position #{pos}" do
54
+ before { source.bytepos = pos }
55
+
56
+ it { is_expected.to eq(pos) }
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#chars_until' do
62
+ it 'returns 100 chars before line end' do
63
+ source.chars_until("\n").should == 100
64
+ end
65
+ end
66
+
67
+ describe '<- #column & #line' do
68
+ subject { source.line_and_column }
69
+
70
+ it { is_expected.to eq([1, 1]) }
71
+
72
+ context 'on the first line' do
73
+ it 'increases column with every read' do
74
+ 10.times do |i|
75
+ source.line_and_column.last.should == 1 + i
76
+ source.consume(1)
77
+ end
78
+ end
79
+ end
80
+
81
+ context 'on the second line' do
82
+ before { source.consume(101) }
83
+
84
+ it { is_expected.to eq([2, 1]) }
85
+ end
86
+
87
+ context 'after reading everything' do
88
+ before { source.consume(10_000) }
89
+
90
+ context 'when seeking to 9' do
91
+ before { source.bytepos = 9 }
92
+
93
+ it { is_expected.to eq([1, 10]) }
94
+ end
95
+
96
+ context 'when seeking to 100' do
97
+ before { source.bytepos = 100 }
98
+
99
+ it { is_expected.to eq([1, 101]) }
100
+ end
101
+
102
+ context 'when seeking to 101' do
103
+ before { source.bytepos = 101 }
104
+
105
+ it { is_expected.to eq([2, 1]) }
106
+ end
107
+
108
+ context 'when seeking to 102' do
109
+ before { source.bytepos = 102 }
110
+
111
+ it { is_expected.to eq([2, 2]) }
112
+ end
113
+
114
+ context 'when seeking beyond eof' do
115
+ it 'does not throw an error' do
116
+ source.bytepos = 1000
117
+ end
118
+ end
119
+ end
120
+
121
+ context 'reading char by char, storing the results' do
122
+ attr_reader :results
123
+
124
+ before do
125
+ @results = {}
126
+ while source.chars_left > 0
127
+ pos = source.pos
128
+ @results[pos] = source.line_and_column
129
+ source.consume(1)
130
+ end
131
+
132
+ @results.entries.size.should == 202
133
+ @results
134
+ end
135
+
136
+ context 'when using pos argument' do
137
+ it 'returns the same results' do
138
+ results.each do |pos, result|
139
+ source.line_and_column(pos).should == result
140
+ end
141
+ end
142
+ end
143
+
144
+ it 'gives the same results when seeking' do
145
+ results.each do |pos, result|
146
+ source.bytepos = pos
147
+ source.line_and_column.should == result
148
+ end
149
+ end
150
+
151
+ it 'gives the same results when reading' do
152
+ cur = source.bytepos = 0
153
+ while source.chars_left > 0
154
+ source.line_and_column.should == results[cur]
155
+ cur += 1
156
+ source.consume(1)
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ describe 'reading encoded input' do
164
+ let(:source) { described_class.new('éö変わる') }
165
+
166
+ def r(str)
167
+ Regexp.new(Regexp.escape(str))
168
+ end
169
+
170
+ it 'reads characters, not bytes' do
171
+ source.should match(r('é'))
172
+ source.consume(1)
173
+
174
+ # Note: pos now returns bytepos directly (not charpos)
175
+ # For UTF-8: é is 2 bytes, ö is 2 bytes
176
+ source.pos.should == if RUBY_ENGINE == 'opal'
177
+ # In Opal/JavaScript, string indexing is character-based
178
+ 1
179
+ else
180
+ # In Ruby, multi-byte characters use multiple bytes
181
+ 2
182
+ end
183
+
184
+ # TODO This needs to be fixed in code with Opal
185
+ if RUBY_ENGINE == 'opal'
186
+ skip "Opal does not support byte positions and char positions correctly for multi-byte characters"
187
+ end
188
+
189
+ source.bytepos.should == 2 # Ruby: 2 bytes for 'é'
190
+
191
+ source.should match(r('ö'))
192
+ source.consume(1)
193
+
194
+ # After consuming 'é' (2 bytes) and 'ö' (2 bytes) = 4 bytes total
195
+ source.pos.should == 4
196
+
197
+ source.bytepos.should == 4
198
+
199
+ source.should match(r('変'))
200
+ source.consume(1)
201
+
202
+ source.consume(2)
203
+ source.chars_left.should == 0
204
+ source.chars_left.should == 0
205
+ end
206
+ end
207
+ end