plurimath-parslet 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.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY.txt +284 -0
  3. data/LICENSE +23 -0
  4. data/README.adoc +454 -0
  5. data/Rakefile +71 -0
  6. data/lib/parslet/accelerator/application.rb +62 -0
  7. data/lib/parslet/accelerator/engine.rb +112 -0
  8. data/lib/parslet/accelerator.rb +162 -0
  9. data/lib/parslet/atoms/alternative.rb +53 -0
  10. data/lib/parslet/atoms/base.rb +157 -0
  11. data/lib/parslet/atoms/can_flatten.rb +137 -0
  12. data/lib/parslet/atoms/capture.rb +38 -0
  13. data/lib/parslet/atoms/context.rb +103 -0
  14. data/lib/parslet/atoms/dsl.rb +112 -0
  15. data/lib/parslet/atoms/dynamic.rb +32 -0
  16. data/lib/parslet/atoms/entity.rb +45 -0
  17. data/lib/parslet/atoms/ignored.rb +26 -0
  18. data/lib/parslet/atoms/infix.rb +115 -0
  19. data/lib/parslet/atoms/lookahead.rb +52 -0
  20. data/lib/parslet/atoms/named.rb +32 -0
  21. data/lib/parslet/atoms/re.rb +41 -0
  22. data/lib/parslet/atoms/repetition.rb +87 -0
  23. data/lib/parslet/atoms/scope.rb +26 -0
  24. data/lib/parslet/atoms/sequence.rb +48 -0
  25. data/lib/parslet/atoms/str.rb +42 -0
  26. data/lib/parslet/atoms/visitor.rb +89 -0
  27. data/lib/parslet/atoms.rb +34 -0
  28. data/lib/parslet/cause.rb +101 -0
  29. data/lib/parslet/context.rb +21 -0
  30. data/lib/parslet/convenience.rb +33 -0
  31. data/lib/parslet/error_reporter/contextual.rb +120 -0
  32. data/lib/parslet/error_reporter/deepest.rb +100 -0
  33. data/lib/parslet/error_reporter/tree.rb +63 -0
  34. data/lib/parslet/error_reporter.rb +8 -0
  35. data/lib/parslet/export.rb +163 -0
  36. data/lib/parslet/expression/treetop.rb +92 -0
  37. data/lib/parslet/expression.rb +51 -0
  38. data/lib/parslet/graphviz.rb +97 -0
  39. data/lib/parslet/parser.rb +68 -0
  40. data/lib/parslet/pattern/binding.rb +49 -0
  41. data/lib/parslet/pattern.rb +113 -0
  42. data/lib/parslet/position.rb +21 -0
  43. data/lib/parslet/rig/rspec.rb +52 -0
  44. data/lib/parslet/scope.rb +42 -0
  45. data/lib/parslet/slice.rb +105 -0
  46. data/lib/parslet/source/line_cache.rb +99 -0
  47. data/lib/parslet/source.rb +96 -0
  48. data/lib/parslet/transform.rb +265 -0
  49. data/lib/parslet/version.rb +5 -0
  50. data/lib/parslet.rb +314 -0
  51. data/plurimath-parslet.gemspec +42 -0
  52. data/spec/acceptance/infix_parser_spec.rb +145 -0
  53. data/spec/acceptance/mixing_parsers_spec.rb +74 -0
  54. data/spec/acceptance/regression_spec.rb +329 -0
  55. data/spec/acceptance/repetition_and_maybe_spec.rb +44 -0
  56. data/spec/acceptance/unconsumed_input_spec.rb +21 -0
  57. data/spec/examples/boolean_algebra_spec.rb +257 -0
  58. data/spec/examples/calc_spec.rb +278 -0
  59. data/spec/examples/capture_spec.rb +137 -0
  60. data/spec/examples/comments_spec.rb +186 -0
  61. data/spec/examples/deepest_errors_spec.rb +420 -0
  62. data/spec/examples/documentation_spec.rb +205 -0
  63. data/spec/examples/email_parser_spec.rb +275 -0
  64. data/spec/examples/empty_spec.rb +37 -0
  65. data/spec/examples/erb_spec.rb +482 -0
  66. data/spec/examples/ip_address_spec.rb +153 -0
  67. data/spec/examples/json_spec.rb +413 -0
  68. data/spec/examples/local_spec.rb +302 -0
  69. data/spec/examples/mathn_spec.rb +151 -0
  70. data/spec/examples/minilisp_spec.rb +492 -0
  71. data/spec/examples/modularity_spec.rb +340 -0
  72. data/spec/examples/nested_errors_spec.rb +322 -0
  73. data/spec/examples/optimized_erb_spec.rb +299 -0
  74. data/spec/examples/parens_spec.rb +239 -0
  75. data/spec/examples/prec_calc_spec.rb +525 -0
  76. data/spec/examples/readme_spec.rb +228 -0
  77. data/spec/examples/scopes_spec.rb +187 -0
  78. data/spec/examples/seasons_spec.rb +196 -0
  79. data/spec/examples/sentence_spec.rb +119 -0
  80. data/spec/examples/simple_xml_spec.rb +250 -0
  81. data/spec/examples/string_parser_spec.rb +407 -0
  82. data/spec/fixtures/examples/boolean_algebra.rb +62 -0
  83. data/spec/fixtures/examples/calc.rb +86 -0
  84. data/spec/fixtures/examples/capture.rb +36 -0
  85. data/spec/fixtures/examples/comments.rb +22 -0
  86. data/spec/fixtures/examples/deepest_errors.rb +99 -0
  87. data/spec/fixtures/examples/documentation.rb +32 -0
  88. data/spec/fixtures/examples/email_parser.rb +42 -0
  89. data/spec/fixtures/examples/empty.rb +10 -0
  90. data/spec/fixtures/examples/erb.rb +39 -0
  91. data/spec/fixtures/examples/ip_address.rb +103 -0
  92. data/spec/fixtures/examples/json.rb +107 -0
  93. data/spec/fixtures/examples/local.rb +60 -0
  94. data/spec/fixtures/examples/mathn.rb +47 -0
  95. data/spec/fixtures/examples/minilisp.rb +75 -0
  96. data/spec/fixtures/examples/modularity.rb +60 -0
  97. data/spec/fixtures/examples/nested_errors.rb +95 -0
  98. data/spec/fixtures/examples/optimized_erb.rb +105 -0
  99. data/spec/fixtures/examples/parens.rb +25 -0
  100. data/spec/fixtures/examples/prec_calc.rb +71 -0
  101. data/spec/fixtures/examples/readme.rb +59 -0
  102. data/spec/fixtures/examples/scopes.rb +43 -0
  103. data/spec/fixtures/examples/seasons.rb +40 -0
  104. data/spec/fixtures/examples/sentence.rb +18 -0
  105. data/spec/fixtures/examples/simple_xml.rb +51 -0
  106. data/spec/fixtures/examples/string_parser.rb +77 -0
  107. data/spec/parslet/atom_results_spec.rb +39 -0
  108. data/spec/parslet/atoms/alternative_spec.rb +26 -0
  109. data/spec/parslet/atoms/base_spec.rb +127 -0
  110. data/spec/parslet/atoms/capture_spec.rb +21 -0
  111. data/spec/parslet/atoms/combinations_spec.rb +5 -0
  112. data/spec/parslet/atoms/dsl_spec.rb +7 -0
  113. data/spec/parslet/atoms/entity_spec.rb +77 -0
  114. data/spec/parslet/atoms/ignored_spec.rb +15 -0
  115. data/spec/parslet/atoms/infix_spec.rb +5 -0
  116. data/spec/parslet/atoms/lookahead_spec.rb +22 -0
  117. data/spec/parslet/atoms/named_spec.rb +4 -0
  118. data/spec/parslet/atoms/re_spec.rb +14 -0
  119. data/spec/parslet/atoms/repetition_spec.rb +24 -0
  120. data/spec/parslet/atoms/scope_spec.rb +26 -0
  121. data/spec/parslet/atoms/sequence_spec.rb +28 -0
  122. data/spec/parslet/atoms/str_spec.rb +15 -0
  123. data/spec/parslet/atoms/visitor_spec.rb +101 -0
  124. data/spec/parslet/atoms_spec.rb +488 -0
  125. data/spec/parslet/convenience_spec.rb +54 -0
  126. data/spec/parslet/error_reporter/contextual_spec.rb +118 -0
  127. data/spec/parslet/error_reporter/deepest_spec.rb +82 -0
  128. data/spec/parslet/error_reporter/tree_spec.rb +7 -0
  129. data/spec/parslet/export_spec.rb +40 -0
  130. data/spec/parslet/expression/treetop_spec.rb +74 -0
  131. data/spec/parslet/minilisp.citrus +29 -0
  132. data/spec/parslet/minilisp.tt +29 -0
  133. data/spec/parslet/parser_spec.rb +36 -0
  134. data/spec/parslet/parslet_spec.rb +38 -0
  135. data/spec/parslet/pattern_spec.rb +272 -0
  136. data/spec/parslet/position_spec.rb +14 -0
  137. data/spec/parslet/rig/rspec_spec.rb +54 -0
  138. data/spec/parslet/scope_spec.rb +45 -0
  139. data/spec/parslet/slice_spec.rb +186 -0
  140. data/spec/parslet/source/line_cache_spec.rb +74 -0
  141. data/spec/parslet/source_spec.rb +210 -0
  142. data/spec/parslet/transform/context_spec.rb +56 -0
  143. data/spec/parslet/transform_spec.rb +183 -0
  144. data/spec/spec_helper.rb +74 -0
  145. data/spec/support/opal.rb +8 -0
  146. data/spec/support/opal.rb.erb +14 -0
  147. data/spec/support/parslet_matchers.rb +96 -0
  148. metadata +240 -0
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parslet::ErrorReporter::Deepest do
4
+ let(:reporter) { described_class.new }
5
+ let(:fake_source) { double('source') }
6
+
7
+ describe '#err' do
8
+ before do
9
+ allow(fake_source).to receive(:pos).and_return(13)
10
+ allow(fake_source).to receive(:line_and_column).and_return([1, 1])
11
+ end
12
+
13
+ it 'returns the deepest cause' do
14
+ expect(reporter).to receive(:deepest).and_return(:deepest)
15
+ expect(reporter.err('parslet', fake_source, 'message')).to eq(:deepest)
16
+ end
17
+ end
18
+
19
+ describe '#err_at' do
20
+ before do
21
+ allow(fake_source).to receive(:pos).and_return(13)
22
+ allow(fake_source).to receive(:line_and_column).and_return([1, 1])
23
+ end
24
+
25
+ it 'returns the deepest cause' do
26
+ expect(reporter).to receive(:deepest).and_return(:deepest)
27
+ expect(reporter.err('parslet', fake_source, 'message', 13)).to eq(:deepest)
28
+ end
29
+ end
30
+
31
+ describe '#deepest(cause)' do
32
+ def fake_cause(pos = 13, children = nil)
33
+ double('cause' + pos.to_s, pos: pos, children: children)
34
+ end
35
+
36
+ context 'when there is no deepest cause yet' do
37
+ let(:cause) { fake_cause }
38
+
39
+ it 'returns the given cause' do
40
+ reporter.deepest(cause).should == cause
41
+ end
42
+ end
43
+
44
+ context 'when the previous cause is deeper (no relationship)' do
45
+ let(:previous) { fake_cause }
46
+
47
+ before do
48
+ reporter.deepest(previous)
49
+ end
50
+
51
+ it 'returns the previous cause' do
52
+ reporter.deepest(fake_cause(12))
53
+ .should == previous
54
+ end
55
+ end
56
+
57
+ context 'when the previous cause is deeper (child)' do
58
+ let(:previous) { fake_cause }
59
+
60
+ before do
61
+ reporter.deepest(previous)
62
+ end
63
+
64
+ it 'returns the given cause' do
65
+ given = fake_cause(12, [previous])
66
+ reporter.deepest(given).should == given
67
+ end
68
+ end
69
+
70
+ context 'when the previous cause is shallower' do
71
+ before do
72
+ reporter.deepest(fake_cause)
73
+ end
74
+
75
+ it 'stores the cause as deepest' do
76
+ deeper = fake_cause(14)
77
+ reporter.deepest(deeper)
78
+ reporter.deepest_cause.should == deeper
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ require 'parslet/error_reporter'
4
+
5
+ describe Parslet::ErrorReporter::Tree do
6
+
7
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require_relative '../fixtures/examples/minilisp'
3
+
4
+ RSpec.describe Parslet::Parser, "exporting to other lingos" do
5
+ let(:parser) { MiniLisp::Parser.new }
6
+
7
+ # I only update the files once I've verified the new syntax to work with
8
+ # the respective tools. This is more an acceptance test than a real spec.
9
+
10
+ describe "<- #to_citrus" do
11
+ let(:citrus) { File.read(
12
+ File.join(File.dirname(__FILE__), 'minilisp.citrus'))
13
+ }
14
+
15
+ it "should be valid citrus syntax" do
16
+
17
+ if RUBY_ENGINE == 'opal'
18
+ skip "Citrus export not supported in Opal, somehow?"
19
+ end
20
+ # puts parser.to_citrus
21
+ parser.to_citrus.should == citrus
22
+ end
23
+ end
24
+
25
+ describe "<- #to_treetop" do
26
+ let(:treetop) { File.read(
27
+ File.join(File.dirname(__FILE__), 'minilisp.tt'))
28
+ }
29
+
30
+ it "should be valid treetop syntax" do
31
+
32
+ if RUBY_ENGINE == 'opal'
33
+ skip "Treetop export not supported in Opal, somehow?"
34
+ end
35
+
36
+ # puts parser.to_treetop
37
+ parser.to_treetop.should == treetop
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ require 'parslet'
4
+
5
+ describe Parslet::Expression::Treetop do
6
+ include Parslet
7
+
8
+ describe "positive samples" do
9
+ [ # pattern # input
10
+ "'abc'", 'abc',
11
+ "...", 'abc',
12
+ "[1-4]", '3',
13
+
14
+ "'abc'?", 'abc',
15
+ "'abc'?", '',
16
+
17
+ "('abc')", 'abc',
18
+
19
+ "'a' 'b'", 'ab',
20
+ "'a' ('b')", 'ab',
21
+
22
+ "'a' / 'b'", 'a',
23
+ "'a' / 'b'", 'b',
24
+
25
+ "'a'*", 'aaa',
26
+ "'a'*", '',
27
+
28
+ "'a'+", 'aa',
29
+ "'a'+", 'a',
30
+
31
+ "'a'{1,2}", 'a',
32
+ "'a'{1,2}", 'aa',
33
+
34
+ "'a'{1,}", 'a',
35
+ "'a'{1,}", 'aa',
36
+
37
+ "'a'{,2}", '',
38
+ "'a'{,2}", 'a',
39
+ "'a'{,2}", 'aa',
40
+ ].each_slice(2) do |pattern, input|
41
+ context "exp(#{pattern.inspect})" do
42
+ let(:parslet) { exp(pattern) }
43
+ subject { parslet }
44
+ it { should parse(input) }
45
+ context "string representation" do
46
+ subject { exp(parslet.to_s) }
47
+ it { should parse(input, :trace => true) }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ describe "negative samples" do
53
+ [ # pattern # input
54
+ "'abc'", 'cba',
55
+ "[1-4]", '5',
56
+
57
+ "'a' / 'b'", 'c',
58
+
59
+ "'a'+", '',
60
+
61
+ "'a'{1,2}", '',
62
+ "'a'{1,2}", 'aaa',
63
+
64
+ "'a'{1,}", '',
65
+
66
+ "'a'{,2}", 'aaa',
67
+ ].each_slice(2) do |pattern, input|
68
+ context "exp(#{pattern.inspect})" do
69
+ subject { exp(pattern) }
70
+ it { should_not parse(input) }
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,29 @@
1
+ grammar MiniLisp::Parser
2
+ rule root
3
+ (expression)
4
+ end
5
+ rule expression
6
+ ((space_p) "(" (space_p) (body) ")" (space_p))
7
+ end
8
+ rule space_p
9
+ (space)0*1
10
+ end
11
+ rule body
12
+ ((expression) | (identifier) | (float) | (integer) | (string))0*
13
+ end
14
+ rule space
15
+ \s1*
16
+ end
17
+ rule identifier
18
+ (([a-zA-Z=*] [a-zA-Z=*_]0*) (space_p))
19
+ end
20
+ rule float
21
+ (((integer) (("." [0-9]1*) | ("e" [0-9]1*))) (space_p))
22
+ end
23
+ rule integer
24
+ ((("+" | "-")0*1 [0-9]1*) (space_p))
25
+ end
26
+ rule string
27
+ ("\"" (("\\" .) | (!"\"" .))0* "\"" (space_p))
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ grammar MiniLisp::Parser
2
+ rule root
3
+ (expression)
4
+ end
5
+ rule expression
6
+ ((space_p) "(" (space_p) (body) ")" (space_p))
7
+ end
8
+ rule space_p
9
+ (space)0..1
10
+ end
11
+ rule body
12
+ ((expression) / (identifier) / (float) / (integer) / (string))0..
13
+ end
14
+ rule space
15
+ \s1..
16
+ end
17
+ rule identifier
18
+ (([a-zA-Z=*] [a-zA-Z=*_]0..) (space_p))
19
+ end
20
+ rule float
21
+ (((integer) (("." [0-9]1..) / ("e" [0-9]1..))) (space_p))
22
+ end
23
+ rule integer
24
+ ((("+" / "-")0..1 [0-9]1..) (space_p))
25
+ end
26
+ rule string
27
+ ("\"" (("\\" .) / (!"\"" .))0.. "\"" (space_p))
28
+ end
29
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parslet::Parser do
4
+ include Parslet
5
+ class FooParser < Parslet::Parser
6
+ rule(:foo) { str('foo') }
7
+ root(:foo)
8
+ end
9
+
10
+ describe '<- .root' do
11
+ parser = Class.new(Parslet::Parser) do
12
+ def root_parslet
13
+ :answer
14
+ end
15
+ end
16
+ parser.root :root_parslet
17
+
18
+ it "has defined a 'root' method, returning the root" do
19
+ parser_instance = parser.new
20
+ expect(parser_instance.root).to eq(:answer)
21
+ end
22
+ end
23
+
24
+ it "parses 'foo'" do
25
+ FooParser.new.parse('foo').should == 'foo'
26
+ end
27
+
28
+ context 'composition' do
29
+ let(:parser) { FooParser.new }
30
+
31
+ it 'allows concatenation' do
32
+ composite = parser >> str('bar')
33
+ composite.should parse('foobar')
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parslet do
4
+ include Parslet
5
+
6
+ describe Parslet::ParseFailed do
7
+ it "should be caught by an empty rescue" do
8
+ begin
9
+ raise Parslet::ParseFailed
10
+ rescue
11
+ # Success! Ignore this.
12
+ end
13
+ end
14
+ end
15
+ describe "<- .rule" do
16
+ # Rules define methods. This can be easily tested by defining them right
17
+ # here.
18
+ context "empty rule" do
19
+ rule(:empty) { }
20
+
21
+ it "should raise a NotImplementedError" do
22
+ lambda {
23
+ empty.parslet
24
+ }.should raise_error(NotImplementedError)
25
+ end
26
+ end
27
+
28
+ context "containing 'any'" do
29
+ rule(:any_rule) { any }
30
+ subject { any_rule }
31
+
32
+ it { should be_a Parslet::Atoms::Entity }
33
+ it "should memoize the returned instance" do
34
+ any_rule.object_id.should == any_rule.object_id
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,272 @@
1
+ require 'spec_helper'
2
+
3
+ require 'parslet'
4
+
5
+ describe Parslet::Pattern do
6
+ include Parslet
7
+
8
+ # These two factory methods help make the specs more robust to interface
9
+ # changes. They also help to label trees (t) and patterns (p).
10
+ def p(pattern)
11
+ Parslet::Pattern.new(pattern)
12
+ end
13
+ def t(obj)
14
+ obj
15
+ end
16
+
17
+ # Tries to match pattern to the tree, and verifies the bindings hash. Don't
18
+ # use this for new examples.
19
+ #
20
+ RSpec::Matchers.define :match_with_bind do |pattern, exp_bindings|
21
+ unless respond_to?(:failure_message)
22
+ alias_method :failure_message_for_should, :failure_message
23
+ end
24
+
25
+ failure_message do |tree|
26
+ "expected #{pattern.inspect} to match #{tree.inspect}, but didn't. (block wasn't called or not correctly)"
27
+ end
28
+ match do |tree|
29
+ bindings = Parslet::Pattern.new(pattern).match(tree)
30
+ bindings && bindings == exp_bindings
31
+ end
32
+ end
33
+
34
+ # This is the more modern version of verifying a match: (uses 'exp'
35
+ # implicitly). Checks for a match of pattern in +exp+ and yields the
36
+ # matched variables.
37
+ #
38
+ def with_match_locals(pattern, &block)
39
+ bindings = p(pattern).match(exp)
40
+ bindings.should_not be_nil
41
+
42
+ block.call(bindings) if block
43
+ end
44
+
45
+ # Can't use #match here, so I went to the Thesaurus.
46
+ #
47
+ RSpec::Matchers.define :detect do |pattern|
48
+ match do |tree|
49
+ bindings = Parslet::Pattern.new(pattern).match(tree)
50
+
51
+ bindings ? true : false
52
+ end
53
+ end
54
+
55
+ describe "<- #match" do
56
+ context "injecting bindings" do
57
+ let(:pattern) { p(simple(:x)) }
58
+
59
+ it "should not modify the original bindings hash" do
60
+ h = {}
61
+ b=pattern.match('a', h)
62
+ h.size.should == 0
63
+ b.size.should == 1
64
+ end
65
+ it "should return nil when no match succeeds" do
66
+ pattern.match([], :foo => :bar).should be_nil
67
+ end
68
+ context "when matching simple(:x) against 'a'" do
69
+ let(:bindings) { pattern.match(t('a'), :foo => :bar) }
70
+
71
+ before(:each) { bindings.should_not be_nil }
72
+ it "should return the injected bindings" do
73
+ bindings[:foo].should == :bar
74
+ end
75
+ it "should return the new bindings" do
76
+ bindings[:x].should == 'a'
77
+ end
78
+ end
79
+ end
80
+ context "simple strings" do
81
+ let(:exp) { 'aaaa' }
82
+
83
+ it "should match simple strings" do
84
+ exp.should match_with_bind(simple(:x), :x => 'aaaa')
85
+ end
86
+ end
87
+ context "simple hash {:a => 'b'}" do
88
+ attr_reader :exp
89
+ before(:each) do
90
+ @exp = t(:a => 'b')
91
+ end
92
+
93
+ it "should not match {:a => simple(:x), :b => simple(:y)}" do
94
+ exp.should_not detect(:a => simple(:x), :b => simple(:y))
95
+ end
96
+ it "should match {:a => simple(:x)}, binding 'x' to the first argument" do
97
+ exp.should match_with_bind({:a => simple(:x)}, :x => 'b')
98
+ end
99
+ it "should match {:a => 'b'} with no binds" do
100
+ exp.should match_with_bind({:a => 'b'}, {})
101
+ end
102
+ end
103
+ context "a more complex hash {:a => {:b => 'c'}}" do
104
+ attr_reader :exp
105
+ before(:each) do
106
+ @exp = t(:a => {:b => 'c'})
107
+ end
108
+
109
+ it "should match wholly with {:a => {:b => simple(:x)}}" do
110
+ exp.should match_with_bind({:a => {:b => simple(:x)}}, :x => 'c')
111
+ end
112
+ it "should match wholly with {:a => subtree(:t)}" do
113
+ with_match_locals(:a => subtree(:t)) do |dict|
114
+ dict[:t].should == {:b => 'c'}
115
+ end
116
+ end
117
+ it "should not bind subtrees to variables in {:a => simple(:x)}" do
118
+ p(:a => simple(:x)).should_not detect(exp)
119
+ end
120
+ end
121
+ context "a more complex hash {:a => 'a', :b => 'b'}" do
122
+ attr_reader :exp
123
+ before(:each) do
124
+ @exp = t({:a => 'a', :b => 'b'})
125
+ end
126
+
127
+ it "should not match partially" do
128
+ Parslet::Pattern.new(:a => simple(:x)).match(exp).should be_nil
129
+ end
130
+ it "should match completely" do
131
+ exp.should match_with_bind({:a => simple(:x), :b => simple(:y)},
132
+ :x => 'a',
133
+ :y => 'b')
134
+ end
135
+ end
136
+ context "an array of 'a', 'b', 'c'" do
137
+ let(:exp) { ['a', 'b', 'c'] }
138
+
139
+ it "should match all elements at once" do
140
+ exp.should match_with_bind(
141
+ [simple(:x), simple(:y), simple(:z)],
142
+ :x => 'a', :y => 'b', :z => 'c')
143
+ end
144
+ end
145
+ context "{:a => 'a', :b => 'b'}" do
146
+ attr_reader :exp
147
+ before(:each) do
148
+ @exp = t(:a => 'a', :b => 'b')
149
+ end
150
+
151
+ it "should match both elements simple(:x), simple(:y)" do
152
+ exp.should match_with_bind(
153
+ {:a => simple(:x), :b => simple(:y)},
154
+ :x => 'a', :y => 'b')
155
+ end
156
+ it "should not match a constrained match (simple(:x) != simple(:y))" do
157
+ exp.should_not detect({:a => simple(:x), :b => simple(:x)})
158
+ end
159
+ end
160
+ context "{:a => 'a', :b => 'a'}" do
161
+ attr_reader :exp
162
+ before(:each) do
163
+ @exp = t(:a => 'a', :b => 'a')
164
+ end
165
+
166
+ it "should match constrained pattern" do
167
+ exp.should match_with_bind(
168
+ {:a => simple(:x), :b => simple(:x)},
169
+ :x => 'a')
170
+ end
171
+ end
172
+ context "{:sub1 => {:a => 'a'}, :sub2 => {:a => 'a'}}" do
173
+ attr_reader :exp
174
+ before(:each) do
175
+ @exp = t({
176
+ :sub1 => {:a => 'a'},
177
+ :sub2 => {:a => 'a'}
178
+ })
179
+ end
180
+
181
+ it "should verify constraints over several subtrees" do
182
+ exp.should match_with_bind({
183
+ :sub1 => {:a => simple(:x)},
184
+ :sub2 => {:a => simple(:x)}
185
+ }, :x => 'a')
186
+ end
187
+ it "should return both bind variables simple(:x), simple(:y)" do
188
+ exp.should match_with_bind({
189
+ :sub1 => {:a => simple(:x)},
190
+ :sub2 => {:a => simple(:y)}
191
+ }, :x => 'a', :y => 'a')
192
+ end
193
+ end
194
+ context "{:sub1 => {:a => 'a'}, :sub2 => {:a => 'b'}}" do
195
+ attr_reader :exp
196
+ before(:each) do
197
+ @exp = t({
198
+ :sub1 => {:a => 'a'},
199
+ :sub2 => {:a => 'b'}
200
+ })
201
+ end
202
+
203
+ it "should verify constraints over several subtrees" do
204
+ exp.should_not match_with_bind({
205
+ :sub1 => {:a => simple(:x)},
206
+ :sub2 => {:a => simple(:x)}
207
+ }, :x => 'a')
208
+ end
209
+ it "should return both bind variables simple(:x), simple(:y)" do
210
+ exp.should match_with_bind({
211
+ :sub1 => {:a => simple(:x)},
212
+ :sub2 => {:a => simple(:y)}
213
+ }, :x => 'a', :y => 'b')
214
+ end
215
+ end
216
+ context "[{:a => 'x'}, {:a => 'y'}]" do
217
+ attr_reader :exp
218
+ before(:each) do
219
+ @exp = t([{:a => 'x'}, {:a => 'y'}])
220
+ end
221
+
222
+ it "should not match sequence(:x) (as a whole)" do
223
+ exp.should_not detect(sequence(:x))
224
+ end
225
+ end
226
+ context "['x', 'y', 'z']" do
227
+ attr_reader :exp
228
+ before(:each) do
229
+ @exp = t(['x', 'y', 'z'])
230
+ end
231
+
232
+ it "should match [simple(:x), simple(:y), simple(:z)]" do
233
+ with_match_locals([simple(:x), simple(:y), simple(:z)]) do |dict|
234
+ dict[:x].should == 'x'
235
+ dict[:y].should == 'y'
236
+ dict[:z].should == 'z'
237
+ end
238
+ end
239
+ it "should match %w(x y z)" do
240
+ exp.should match_with_bind(%w(x y z), { })
241
+ end
242
+ it "should not match [simple(:x), simple(:y), simple(:x)]" do
243
+ exp.should_not detect([simple(:x), simple(:y), simple(:x)])
244
+ end
245
+ it "should not match [simple(:x), simple(:y)]" do
246
+ exp.should_not detect([simple(:x), simple(:y), simple(:x)])
247
+ end
248
+ it "should match sequence(:x) (as array)" do
249
+ exp.should match_with_bind(sequence(:x), :x => ['x', 'y', 'z'])
250
+ end
251
+ end
252
+ context "{:a => [1,2,3]}" do
253
+ attr_reader :exp
254
+ before(:each) do
255
+ @exp = t(:a => [1,2,3])
256
+ end
257
+
258
+ it "should match :a => sequence(:x) (binding x to the whole array)" do
259
+ exp.should match_with_bind({:a => sequence(:x)}, {:x => [1,2,3]})
260
+ end
261
+ end
262
+ context "with differently ordered hashes" do
263
+ it "should still match" do
264
+ t(:a => 'a', :b => 'b').should detect(:a => 'a', :b => 'b')
265
+ t(:a => 'a', :b => 'b').should detect(:b => 'b', :a => 'a')
266
+
267
+ t(:b => 'b', :a => 'a').should detect(:b => 'b', :a => 'a')
268
+ t(:b => 'b', :a => 'a').should detect(:a => 'a', :b => 'b')
269
+ end
270
+ end
271
+ end
272
+ end
@@ -0,0 +1,14 @@
1
+ # Encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Parslet::Position do
6
+ slet(:position) { described_class.new('öäüö', 4) }
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
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'parslet/rig/rspec'
3
+
4
+ describe 'rspec integration' do
5
+ include Parslet
6
+ subject { str('example') }
7
+
8
+ it { should parse('example') }
9
+ it { should_not parse('foo') }
10
+ it { should parse('example').as('example') }
11
+ it { should_not parse('foo').as('example') }
12
+ it { should_not parse('example').as('foo') }
13
+
14
+ it { str('foo').as(:bar).should parse('foo').as({:bar => 'foo'}) }
15
+ it { str('foo').as(:bar).should_not parse('foo').as({:b => 'f'}) }
16
+
17
+ it 'accepts a block to assert more specific details about the parsing output' do
18
+ str('foo').as(:bar).should(parse('foo').as { |output|
19
+ output.should have_key(:bar)
20
+ output.values.first.should == 'foo'
21
+ })
22
+ end
23
+
24
+ # Uncomment to test error messages manually:
25
+ # it { str('foo').should parse('foo', :trace => true).as('bar') }
26
+ # it { str('foo').should parse('food', :trace => true) }
27
+ # it { str('foo').should_not parse('foo', :trace => true).as('foo') }
28
+ # it { str('foo').should_not parse('foo', :trace => true) }
29
+ # it 'accepts a block to assert more specific details about the parsing output' do
30
+ # str('foo').as(:bar).should(parse('foo', :trace => true).as { |output|
31
+ # output.should_not have_key(:bar)
32
+ # })
33
+ # end
34
+
35
+ end
36
+
37
+ describe 'rspec3 syntax' do
38
+ include Parslet
39
+
40
+ let(:s) { str('example') }
41
+
42
+ it { expect(s).to parse('example') }
43
+ it { expect(s).not_to parse('foo') }
44
+ it { expect(s).to parse('example').as('example') }
45
+ it { expect(s).not_to parse('foo').as('example') }
46
+
47
+ it { expect(s).not_to parse('example').as('foo') }
48
+
49
+ # Uncomment to test error messages manually:
50
+ # it { expect(str('foo')).to parse('foo', :trace => true).as('bar') }
51
+ # it { expect(str('foo')).to parse('food', :trace => true) }
52
+ # it { expect(str('foo')).not_to parse('foo', :trace => true).as('foo') }
53
+ # it { expect(str('foo')).not_to parse('foo', :trace => true) }
54
+ end