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.
- checksums.yaml +7 -0
- data/HISTORY.txt +25 -0
- data/LICENSE +23 -0
- data/README.adoc +643 -0
- data/Rakefile +189 -0
- data/example/balanced-parens/basic.rb +42 -0
- data/example/balanced-parens/basic.rb.md +86 -0
- data/example/balanced-parens/parens.rb +42 -0
- data/example/balanced-parens/ruby_transform.rb +162 -0
- data/example/big.erb +73 -0
- data/example/boolean-algebra/basic.rb +70 -0
- data/example/boolean-algebra/basic.rb.md +108 -0
- data/example/boolean-algebra/ruby_transform.rb +263 -0
- data/example/calculator/basic.rb +153 -0
- data/example/calculator/basic.rb.md +120 -0
- data/example/calculator/pattern.rb +153 -0
- data/example/calculator/ruby_transform.rb +156 -0
- data/example/calculator/ruby_transform.rb.md +32 -0
- data/example/calculator/serialized.rb +257 -0
- data/example/calculator/serialized.rb.md +32 -0
- data/example/calculator/transform.rb +153 -0
- data/example/calculator/zero_copy.rb +269 -0
- data/example/calculator/zero_copy.rb.md +36 -0
- data/example/capture/basic.rb +49 -0
- data/example/capture/basic.rb.md +106 -0
- data/example/capture/example.json +39 -0
- data/example/comments/basic.rb +35 -0
- data/example/comments/basic.rb.md +110 -0
- data/example/csv/ruby_transform.rb +148 -0
- data/example/csv/ruby_transform.rb.md +131 -0
- data/example/csv/serialized.rb +201 -0
- data/example/csv/serialized.rb.md +31 -0
- data/example/csv/zero_copy.rb +276 -0
- data/example/csv/zero_copy.rb.md +36 -0
- data/example/custom_atoms/indent_atom.rb +79 -0
- data/example/deepest-errors/basic.rb +131 -0
- data/example/deepest-errors/basic.rb.md +152 -0
- data/example/documentation/basic.rb +18 -0
- data/example/documentation/basic.rb.md +97 -0
- data/example/email/basic.rb +55 -0
- data/example/email/basic.rb.md +102 -0
- data/example/email/ruby_transform.rb +106 -0
- data/example/empty/basic.rb +13 -0
- data/example/empty/basic.rb.md +73 -0
- data/example/empty/example.json +38 -0
- data/example/erb/basic.rb +47 -0
- data/example/erb/basic.rb.md +103 -0
- data/example/erb/optimized.rb +42 -0
- data/example/error-reporting/basic.rb +132 -0
- data/example/error-reporting/basic.rb.md +122 -0
- data/example/expression-evaluator/basic.rb +284 -0
- data/example/expression-evaluator/basic.rb.md +138 -0
- data/example/ini/basic.rb +154 -0
- data/example/ini/basic.rb.md +129 -0
- data/example/ini/ruby_transform.rb +154 -0
- data/example/ip-address/basic.rb +125 -0
- data/example/ip-address/basic.rb.md +139 -0
- data/example/iso-6709/basic.rb +231 -0
- data/example/iso-6709/basic.rb.md +143 -0
- data/example/iso-8601/basic.rb +275 -0
- data/example/iso-8601/basic.rb.md +149 -0
- data/example/json/basic.rb +128 -0
- data/example/json/basic.rb.md +121 -0
- data/example/json/pattern.rb +128 -0
- data/example/json/ruby_transform.rb +200 -0
- data/example/json/ruby_transform.rb.md +32 -0
- data/example/json/serialized.rb +233 -0
- data/example/json/serialized.rb.md +31 -0
- data/example/json/transform.rb +128 -0
- data/example/json/zero_copy.rb +316 -0
- data/example/json/zero_copy.rb.md +36 -0
- data/example/local/basic.rb +34 -0
- data/example/local/basic.rb.md +91 -0
- data/example/local/example.json +38 -0
- data/example/markdown/basic.rb +287 -0
- data/example/markdown/basic.rb.md +160 -0
- data/example/markup/basic.rb +173 -0
- data/example/markup/basic.rb.md +118 -0
- data/example/mathn/basic.rb +47 -0
- data/example/mathn/basic.rb.md +96 -0
- data/example/mathn/example.json +39 -0
- data/example/minilisp/basic.rb +94 -0
- data/example/minilisp/basic.rb.md +133 -0
- data/example/modularity/basic.rb +47 -0
- data/example/modularity/basic.rb.md +152 -0
- data/example/nested-errors/basic.rb +132 -0
- data/example/nested-errors/basic.rb.md +157 -0
- data/example/output/boolean_algebra.out +4 -0
- data/example/output/calc.out +1 -0
- data/example/output/capture.out +3 -0
- data/example/output/comments.out +8 -0
- data/example/output/deepest_errors.out +54 -0
- data/example/output/documentation.err +4 -0
- data/example/output/documentation.out +1 -0
- data/example/output/email_parser.out +2 -0
- data/example/output/empty.err +1 -0
- data/example/output/erb.out +7 -0
- data/example/output/ignore.out +1 -0
- data/example/output/ignore_whitespace.out +1 -0
- data/example/output/ip_address.out +9 -0
- data/example/output/json.out +5 -0
- data/example/output/local.out +3 -0
- data/example/output/mathn.out +4 -0
- data/example/output/minilisp.out +5 -0
- data/example/output/modularity.out +0 -0
- data/example/output/nested_errors.out +54 -0
- data/example/output/optimized_erb.out +1 -0
- data/example/output/parens.out +8 -0
- data/example/output/prec_calc.out +5 -0
- data/example/output/readme.out +1 -0
- data/example/output/scopes.out +1 -0
- data/example/output/seasons.out +28 -0
- data/example/output/sentence.out +1 -0
- data/example/output/simple_xml.out +2 -0
- data/example/output/string_parser.out +3 -0
- data/example/prec-calc/basic.rb +71 -0
- data/example/prec-calc/basic.rb.md +114 -0
- data/example/readme/basic.rb +30 -0
- data/example/readme/basic.rb.md +80 -0
- data/example/scopes/basic.rb +15 -0
- data/example/scopes/basic.rb.md +73 -0
- data/example/scopes/example.json +38 -0
- data/example/seasons/basic.rb +46 -0
- data/example/seasons/basic.rb.md +117 -0
- data/example/seasons/example.json +40 -0
- data/example/sentence/basic.rb +36 -0
- data/example/sentence/basic.rb.md +81 -0
- data/example/sexp/ruby_transform.rb +180 -0
- data/example/sexp/ruby_transform.rb.md +143 -0
- data/example/simple-xml/basic.rb +54 -0
- data/example/simple-xml/basic.rb.md +125 -0
- data/example/simple.lit +3 -0
- data/example/string-literal/basic.rb +77 -0
- data/example/string-literal/basic.rb.md +128 -0
- data/example/test.lit +4 -0
- data/example/toml/basic.rb +226 -0
- data/example/toml/basic.rb.md +173 -0
- data/example/url/basic.rb +219 -0
- data/example/url/basic.rb.md +142 -0
- data/example/url/ruby_transform.rb +219 -0
- data/example/yaml/basic.rb +216 -0
- data/example/yaml/basic.rb.md +148 -0
- data/ext/parsanol_native/extconf.rb +4 -0
- data/lib/parsanol/accelerator/application.rb +62 -0
- data/lib/parsanol/accelerator/engine.rb +112 -0
- data/lib/parsanol/accelerator.rb +162 -0
- data/lib/parsanol/ast_visitor.rb +122 -0
- data/lib/parsanol/atoms/alternative.rb +97 -0
- data/lib/parsanol/atoms/base.rb +214 -0
- data/lib/parsanol/atoms/can_flatten.rb +192 -0
- data/lib/parsanol/atoms/capture.rb +41 -0
- data/lib/parsanol/atoms/context.rb +351 -0
- data/lib/parsanol/atoms/context_optimized.rb +42 -0
- data/lib/parsanol/atoms/custom.rb +110 -0
- data/lib/parsanol/atoms/cut.rb +62 -0
- data/lib/parsanol/atoms/dsl.rb +130 -0
- data/lib/parsanol/atoms/dynamic.rb +33 -0
- data/lib/parsanol/atoms/entity.rb +55 -0
- data/lib/parsanol/atoms/ignored.rb +28 -0
- data/lib/parsanol/atoms/infix.rb +121 -0
- data/lib/parsanol/atoms/lookahead.rb +64 -0
- data/lib/parsanol/atoms/named.rb +50 -0
- data/lib/parsanol/atoms/re.rb +61 -0
- data/lib/parsanol/atoms/repetition.rb +241 -0
- data/lib/parsanol/atoms/scope.rb +28 -0
- data/lib/parsanol/atoms/sequence.rb +157 -0
- data/lib/parsanol/atoms/str.rb +90 -0
- data/lib/parsanol/atoms/visitor.rb +91 -0
- data/lib/parsanol/atoms.rb +36 -0
- data/lib/parsanol/buffer.rb +130 -0
- data/lib/parsanol/builder_callbacks.rb +353 -0
- data/lib/parsanol/cause.rb +101 -0
- data/lib/parsanol/context.rb +23 -0
- data/lib/parsanol/convenience.rb +35 -0
- data/lib/parsanol/edit_tracker.rb +107 -0
- data/lib/parsanol/error_reporter/contextual.rb +122 -0
- data/lib/parsanol/error_reporter/deepest.rb +106 -0
- data/lib/parsanol/error_reporter/tree.rb +68 -0
- data/lib/parsanol/error_reporter.rb +98 -0
- data/lib/parsanol/export.rb +163 -0
- data/lib/parsanol/expression/treetop.rb +94 -0
- data/lib/parsanol/expression.rb +51 -0
- data/lib/parsanol/fast_mode.rb +145 -0
- data/lib/parsanol/first_set.rb +75 -0
- data/lib/parsanol/grammar_builder.rb +177 -0
- data/lib/parsanol/graphviz.rb +97 -0
- data/lib/parsanol/incremental_parser.rb +179 -0
- data/lib/parsanol/interval_tree.rb +215 -0
- data/lib/parsanol/lazy_result.rb +178 -0
- data/lib/parsanol/lexer.rb +146 -0
- data/lib/parsanol/native/parser.rb +630 -0
- data/lib/parsanol/native/serializer.rb +245 -0
- data/lib/parsanol/native/transformer.rb +438 -0
- data/lib/parsanol/native/types.rb +41 -0
- data/lib/parsanol/native.rb +217 -0
- data/lib/parsanol/optimizer.rb +86 -0
- data/lib/parsanol/optimizers/choice_optimizer.rb +78 -0
- data/lib/parsanol/optimizers/cut_inserter.rb +175 -0
- data/lib/parsanol/optimizers/lookahead_optimizer.rb +58 -0
- data/lib/parsanol/optimizers/quantifier_optimizer.rb +62 -0
- data/lib/parsanol/optimizers/sequence_optimizer.rb +97 -0
- data/lib/parsanol/options/ruby_transform.rb +109 -0
- data/lib/parsanol/options/serialized.rb +94 -0
- data/lib/parsanol/options/zero_copy.rb +130 -0
- data/lib/parsanol/options.rb +20 -0
- data/lib/parsanol/parallel.rb +133 -0
- data/lib/parsanol/parsanol_native.bundle +0 -0
- data/lib/parsanol/parser.rb +151 -0
- data/lib/parsanol/parslet.rb +148 -0
- data/lib/parsanol/parslet_native.bundle +0 -0
- data/lib/parsanol/pattern/binding.rb +49 -0
- data/lib/parsanol/pattern.rb +115 -0
- data/lib/parsanol/pool.rb +220 -0
- data/lib/parsanol/pools/array_pool.rb +75 -0
- data/lib/parsanol/pools/buffer_pool.rb +173 -0
- data/lib/parsanol/pools/position_pool.rb +92 -0
- data/lib/parsanol/pools/slice_pool.rb +64 -0
- data/lib/parsanol/position.rb +89 -0
- data/lib/parsanol/result.rb +44 -0
- data/lib/parsanol/result_builder.rb +208 -0
- data/lib/parsanol/result_stream.rb +262 -0
- data/lib/parsanol/rig/rspec.rb +52 -0
- data/lib/parsanol/rope.rb +78 -0
- data/lib/parsanol/scope.rb +42 -0
- data/lib/parsanol/slice.rb +172 -0
- data/lib/parsanol/source/line_cache.rb +99 -0
- data/lib/parsanol/source.rb +171 -0
- data/lib/parsanol/source_location.rb +164 -0
- data/lib/parsanol/streaming_parser.rb +124 -0
- data/lib/parsanol/string_view.rb +192 -0
- data/lib/parsanol/transform.rb +267 -0
- data/lib/parsanol/version.rb +5 -0
- data/lib/parsanol/wasm/README.md +80 -0
- data/lib/parsanol/wasm/package.json +51 -0
- data/lib/parsanol/wasm/parsanol.js +252 -0
- data/lib/parsanol/wasm/parslet.d.ts +129 -0
- data/lib/parsanol/wasm_parser.rb +239 -0
- data/lib/parsanol.rb +408 -0
- data/parsanol-ruby.gemspec +56 -0
- data/spec/acceptance/examples_spec.rb +96 -0
- data/spec/acceptance/infix_parser_spec.rb +145 -0
- data/spec/acceptance/mixing_parsers_spec.rb +74 -0
- data/spec/acceptance/regression_spec.rb +329 -0
- data/spec/acceptance/repetition_and_maybe_spec.rb +44 -0
- data/spec/acceptance/unconsumed_input_spec.rb +21 -0
- data/spec/benchmark/comparative/runner_spec.rb +105 -0
- data/spec/integration/array_pooling_spec.rb +193 -0
- data/spec/integration/buffer_allocation_spec.rb +324 -0
- data/spec/integration/position_pooling_spec.rb +184 -0
- data/spec/integration/result_builder_spec.rb +282 -0
- data/spec/integration/rope_stringview_integration_spec.rb +188 -0
- data/spec/integration/slice_pooling_spec.rb +63 -0
- data/spec/integration/string_view_integration_spec.rb +125 -0
- data/spec/lexer_spec.rb +231 -0
- data/spec/parsanol/atom_results_spec.rb +39 -0
- data/spec/parsanol/atoms/alternative_spec.rb +26 -0
- data/spec/parsanol/atoms/base_spec.rb +127 -0
- data/spec/parsanol/atoms/capture_spec.rb +21 -0
- data/spec/parsanol/atoms/combinations_spec.rb +5 -0
- data/spec/parsanol/atoms/custom_spec.rb +79 -0
- data/spec/parsanol/atoms/dsl_spec.rb +7 -0
- data/spec/parsanol/atoms/entity_spec.rb +77 -0
- data/spec/parsanol/atoms/ignored_spec.rb +15 -0
- data/spec/parsanol/atoms/infix_spec.rb +5 -0
- data/spec/parsanol/atoms/lookahead_spec.rb +22 -0
- data/spec/parsanol/atoms/named_spec.rb +4 -0
- data/spec/parsanol/atoms/re_spec.rb +14 -0
- data/spec/parsanol/atoms/repetition_spec.rb +24 -0
- data/spec/parsanol/atoms/scope_spec.rb +26 -0
- data/spec/parsanol/atoms/sequence_spec.rb +28 -0
- data/spec/parsanol/atoms/str_spec.rb +15 -0
- data/spec/parsanol/atoms/visitor_spec.rb +101 -0
- data/spec/parsanol/atoms_spec.rb +488 -0
- data/spec/parsanol/auto_optimize_spec.rb +334 -0
- data/spec/parsanol/buffer_spec.rb +219 -0
- data/spec/parsanol/builder_callbacks_spec.rb +377 -0
- data/spec/parsanol/choice_optimizer_spec.rb +231 -0
- data/spec/parsanol/convenience_spec.rb +54 -0
- data/spec/parsanol/cut_inserter_spec.rb +248 -0
- data/spec/parsanol/cut_spec.rb +66 -0
- data/spec/parsanol/edit_tracker_spec.rb +218 -0
- data/spec/parsanol/error_reporter/contextual_spec.rb +122 -0
- data/spec/parsanol/error_reporter/deepest_spec.rb +82 -0
- data/spec/parsanol/error_reporter/tree_spec.rb +7 -0
- data/spec/parsanol/export_spec.rb +67 -0
- data/spec/parsanol/expression/treetop_spec.rb +75 -0
- data/spec/parsanol/first_set_spec.rb +298 -0
- data/spec/parsanol/interval_tree_spec.rb +205 -0
- data/spec/parsanol/lazy_result_spec.rb +288 -0
- data/spec/parsanol/lookahead_optimizer_spec.rb +252 -0
- data/spec/parsanol/minilisp.citrus +29 -0
- data/spec/parsanol/minilisp.tt +29 -0
- data/spec/parsanol/optimizer_spec.rb +459 -0
- data/spec/parsanol/options/parslet_compat_spec.rb +166 -0
- data/spec/parsanol/options/ruby_transform_spec.rb +70 -0
- data/spec/parsanol/options/serialized_spec.rb +69 -0
- data/spec/parsanol/options/zero_copy_spec.rb +230 -0
- data/spec/parsanol/parser_spec.rb +36 -0
- data/spec/parsanol/parslet_spec.rb +38 -0
- data/spec/parsanol/pattern_spec.rb +272 -0
- data/spec/parsanol/pool_spec.rb +392 -0
- data/spec/parsanol/pools/array_pool_spec.rb +356 -0
- data/spec/parsanol/pools/buffer_pool_spec.rb +365 -0
- data/spec/parsanol/pools/position_pool_spec.rb +118 -0
- data/spec/parsanol/pools/slice_pool_spec.rb +262 -0
- data/spec/parsanol/position_spec.rb +14 -0
- data/spec/parsanol/result_builder_spec.rb +391 -0
- data/spec/parsanol/rig/rspec_spec.rb +54 -0
- data/spec/parsanol/rope_spec.rb +207 -0
- data/spec/parsanol/scope_spec.rb +45 -0
- data/spec/parsanol/slice_spec.rb +249 -0
- data/spec/parsanol/source/line_cache_spec.rb +74 -0
- data/spec/parsanol/source_spec.rb +207 -0
- data/spec/parsanol/string_view_spec.rb +345 -0
- data/spec/parsanol/transform/context_spec.rb +56 -0
- data/spec/parsanol/transform_spec.rb +183 -0
- data/spec/parsanol/tree_memoization_spec.rb +149 -0
- data/spec/parslet_compatibility/expressir_edge_cases_spec.rb +153 -0
- data/spec/parslet_compatibility/minimal_reproduction.rb +199 -0
- data/spec/parslet_compatibility_spec.rb +399 -0
- data/spec/parslet_imported/atom_spec.rb +93 -0
- data/spec/parslet_imported/combinator_spec.rb +161 -0
- data/spec/parslet_imported/spec_helper.rb +73 -0
- data/spec/performance/batch_parsing_benchmark.rb +129 -0
- data/spec/performance/complete_optimization_summary.rb +143 -0
- data/spec/performance/grammar_caching_analysis.rb +121 -0
- data/spec/performance/grammar_caching_benchmark.rb +80 -0
- data/spec/performance/native_benchmark_spec.rb +230 -0
- data/spec/performance/phase5_benchmark.rb +144 -0
- data/spec/performance/profiling_benchmark.rb +131 -0
- data/spec/performance/ruby_improvements_benchmark.rb +171 -0
- data/spec/performance_spec.rb +374 -0
- data/spec/spec_helper.rb +79 -0
- data/spec/support/opal.rb +8 -0
- data/spec/support/opal.rb.erb +14 -0
- 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
|