parslet 1.7.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README +1 -1
- data/lib/parslet/context.rb +1 -1
- data/lib/parslet/parser.rb +1 -1
- data/parslet.gemspec +18 -0
- data/spec/acceptance/examples_spec.rb +37 -0
- data/spec/acceptance/infix_parser_spec.rb +112 -0
- data/spec/acceptance/regression_spec.rb +314 -0
- data/spec/acceptance/repetition_and_maybe_spec.rb +42 -0
- data/spec/acceptance/unconsumed_input_spec.rb +21 -0
- data/spec/parslet/atom_results_spec.rb +39 -0
- data/spec/parslet/atoms/alternative_spec.rb +26 -0
- data/spec/parslet/atoms/base_spec.rb +126 -0
- data/spec/parslet/atoms/capture_spec.rb +21 -0
- data/spec/parslet/atoms/combinations_spec.rb +5 -0
- data/spec/parslet/atoms/dsl_spec.rb +25 -0
- data/spec/parslet/atoms/entity_spec.rb +77 -0
- data/spec/parslet/atoms/infix_spec.rb +5 -0
- data/spec/parslet/atoms/lookahead_spec.rb +22 -0
- data/spec/parslet/atoms/named_spec.rb +4 -0
- data/spec/parslet/atoms/re_spec.rb +14 -0
- data/spec/parslet/atoms/repetition_spec.rb +24 -0
- data/spec/parslet/atoms/scope_spec.rb +26 -0
- data/spec/parslet/atoms/sequence_spec.rb +28 -0
- data/spec/parslet/atoms/str_spec.rb +15 -0
- data/spec/parslet/atoms/visitor_spec.rb +80 -0
- data/spec/parslet/atoms_spec.rb +429 -0
- data/spec/parslet/convenience_spec.rb +48 -0
- data/spec/parslet/error_reporter/contextual_spec.rb +115 -0
- data/spec/parslet/error_reporter/deepest_spec.rb +73 -0
- data/spec/parslet/error_reporter/tree_spec.rb +7 -0
- data/spec/parslet/export_spec.rb +67 -0
- data/spec/parslet/expression/treetop_spec.rb +74 -0
- data/spec/parslet/minilisp.citrus +29 -0
- data/spec/parslet/minilisp.tt +29 -0
- data/spec/parslet/parser_spec.rb +31 -0
- data/spec/parslet/parslet_spec.rb +38 -0
- data/spec/parslet/pattern_spec.rb +272 -0
- data/spec/parslet/position_spec.rb +14 -0
- data/spec/parslet/rig/rspec_spec.rb +54 -0
- data/spec/parslet/scope_spec.rb +45 -0
- data/spec/parslet/slice_spec.rb +144 -0
- data/spec/parslet/source/line_cache_spec.rb +74 -0
- data/spec/parslet/source_spec.rb +168 -0
- data/spec/parslet/transform/context_spec.rb +35 -0
- data/spec/parslet/transform_spec.rb +165 -0
- data/spec/spec_helper.rb +38 -0
- metadata +46 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 546221c33c4829eb1bede8a241b40418197462de
|
4
|
+
data.tar.gz: d440cbbac367cf7909bdeeb764b431bc3a32bdf7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: adae2aa91f57458cf679d60c3aefd5545a02f99895745cfc61e05290e86cae69adc1b38c121756e96beb4c6ffb4bce375af04e39050ecfe59154ecd3e187a76a
|
7
|
+
data.tar.gz: f8eeac537a6b4dcc958efeb1355d4ad6040e88e91efa2e0397706803ba58c42f69b970720b62cae7f29ff550f50e652c984853af41cab31da7a6ebb4cccbc4ae
|
data/README
CHANGED
data/lib/parslet/context.rb
CHANGED
data/lib/parslet/parser.rb
CHANGED
@@ -30,7 +30,7 @@
|
|
30
30
|
class Parslet::Parser < Parslet::Atoms::Base
|
31
31
|
include Parslet
|
32
32
|
|
33
|
-
class <<self # class methods
|
33
|
+
class << self # class methods
|
34
34
|
# Define the parsers #root function. This is the place where you start
|
35
35
|
# parsing; if you have a rule for 'file' that describes what should be
|
36
36
|
# in a file, this would be your root declaration:
|
data/parslet.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'parslet'
|
5
|
+
s.version = '1.7.1'
|
6
|
+
|
7
|
+
s.authors = ['Kaspar Schiess']
|
8
|
+
s.email = 'kaspar.schiess@absurd.li'
|
9
|
+
s.extra_rdoc_files = ['README']
|
10
|
+
s.files = %w(HISTORY.txt LICENSE Rakefile README parslet.gemspec) + Dir.glob("{lib,spec,example}/**/*")
|
11
|
+
s.homepage = 'http://kschiess.github.io/parslet'
|
12
|
+
s.license = 'MIT'
|
13
|
+
s.rdoc_options = ['--main', 'README']
|
14
|
+
s.require_paths = ['lib']
|
15
|
+
s.summary = 'Parser construction library with great error reporting in Ruby.'
|
16
|
+
|
17
|
+
s.add_dependency 'blankslate', '>= 2.0', '<= 4.0'
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
describe "Regression on" do
|
5
|
+
Dir["example/*.rb"].each do |example|
|
6
|
+
context example do
|
7
|
+
# Generates a product path for a given example file.
|
8
|
+
def product_path(str, ext)
|
9
|
+
str.
|
10
|
+
gsub('.rb', ".#{ext}").
|
11
|
+
gsub('example/','example/output/')
|
12
|
+
end
|
13
|
+
|
14
|
+
it "runs successfully" do
|
15
|
+
stdin, stdout, stderr = Open3.popen3("ruby #{example}")
|
16
|
+
|
17
|
+
handle_map = {
|
18
|
+
stdout => :out,
|
19
|
+
stderr => :err
|
20
|
+
}
|
21
|
+
expectation_found = handle_map.any? do |io, ext|
|
22
|
+
name = product_path(example, ext)
|
23
|
+
|
24
|
+
if File.exists?(name)
|
25
|
+
io.read.strip.should == File.read(name).strip
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
unless expectation_found
|
31
|
+
fail "Example doesn't have either an .err or an .out file. "+
|
32
|
+
"Please create in examples/output!"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Infix expression parsing' do
|
4
|
+
class InfixExpressionParser < Parslet::Parser
|
5
|
+
rule(:space) { match['\s'] }
|
6
|
+
|
7
|
+
def cts atom
|
8
|
+
atom >> space.repeat
|
9
|
+
end
|
10
|
+
def infix *args
|
11
|
+
Infix.new(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
rule(:mul_op) { match['*/'] >> str(' ').maybe }
|
15
|
+
rule(:add_op) { match['+-'] >> str(' ').maybe }
|
16
|
+
rule(:digit) { match['0-9'] }
|
17
|
+
rule(:integer) { cts digit.repeat(1) }
|
18
|
+
|
19
|
+
rule(:expression) { infix_expression(integer,
|
20
|
+
[mul_op, 2, :left],
|
21
|
+
[add_op, 1, :right]) }
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:p) { InfixExpressionParser.new }
|
25
|
+
describe '#integer' do
|
26
|
+
let(:i) { p.integer }
|
27
|
+
it "parses integers" do
|
28
|
+
i.should parse('1')
|
29
|
+
i.should parse('123')
|
30
|
+
end
|
31
|
+
it "consumes trailing white space" do
|
32
|
+
i.should parse('1 ')
|
33
|
+
i.should parse('134 ')
|
34
|
+
end
|
35
|
+
it "doesn't parse floats" do
|
36
|
+
i.should_not parse('1.3')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
describe '#multiplication' do
|
40
|
+
let(:m) { p.expression }
|
41
|
+
it "parses simple multiplication" do
|
42
|
+
m.should parse('1*2').as(l: '1', o: '*', r: '2')
|
43
|
+
end
|
44
|
+
it "parses simple multiplication with spaces" do
|
45
|
+
m.should parse('1 * 2').as(l: '1 ', o: '* ', r: '2')
|
46
|
+
end
|
47
|
+
it "parses division" do
|
48
|
+
m.should parse('1/2')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
describe '#addition' do
|
52
|
+
let(:a) { p.expression }
|
53
|
+
|
54
|
+
it "parses simple addition" do
|
55
|
+
a.should parse('1+2')
|
56
|
+
end
|
57
|
+
it "parses complex addition" do
|
58
|
+
a.should parse('1+2+3-4')
|
59
|
+
end
|
60
|
+
it "parses a single element" do
|
61
|
+
a.should parse('1')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe 'mixed operations' do
|
66
|
+
let(:mo) { p.expression }
|
67
|
+
|
68
|
+
describe 'inspection' do
|
69
|
+
it 'produces useful expressions' do
|
70
|
+
p.expression.parslet.inspect.should ==
|
71
|
+
"infix_expression(INTEGER, [MUL_OP, ADD_OP])"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
describe 'right associativity' do
|
75
|
+
it 'produces trees that lean right' do
|
76
|
+
mo.should parse('1+2+3').as(
|
77
|
+
l: '1', o: '+', r: {l: '2', o: '+', r: '3'})
|
78
|
+
end
|
79
|
+
end
|
80
|
+
describe 'left associativity' do
|
81
|
+
it 'produces trees that lean left' do
|
82
|
+
mo.should parse('1*2*3').as(
|
83
|
+
l: {l:'1', o:'*', r:'2'}, o:'*', r:'3')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
describe 'error handling' do
|
87
|
+
describe 'incomplete expression' do
|
88
|
+
it 'produces the right error' do
|
89
|
+
cause = catch_failed_parse {
|
90
|
+
mo.parse('1+') }
|
91
|
+
|
92
|
+
cause.ascii_tree.to_s.should == <<-ERROR
|
93
|
+
INTEGER was expected at line 1 char 3.
|
94
|
+
`- Failed to match sequence (DIGIT{1, } SPACE{0, }) at line 1 char 3.
|
95
|
+
`- Expected at least 1 of DIGIT at line 1 char 3.
|
96
|
+
`- Premature end of input at line 1 char 3.
|
97
|
+
ERROR
|
98
|
+
end
|
99
|
+
end
|
100
|
+
describe 'invalid operator' do
|
101
|
+
it 'produces the right error' do
|
102
|
+
cause = catch_failed_parse {
|
103
|
+
mo.parse('1%') }
|
104
|
+
|
105
|
+
cause.ascii_tree.to_s.should == <<-ERROR
|
106
|
+
Don't know what to do with "%" at line 1 char 2.
|
107
|
+
ERROR
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,314 @@
|
|
1
|
+
# Encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
require 'parslet'
|
6
|
+
|
7
|
+
describe "Regressions from real examples" do
|
8
|
+
# This parser piece produces on the left a subtree that is keyed (a hash)
|
9
|
+
# and on the right a subtree that is a repetition of such subtrees. I've
|
10
|
+
# for now decided that these would merge into the repetition such that the
|
11
|
+
# return value is an array. This avoids maybe loosing keys/values in a
|
12
|
+
# hash merge.
|
13
|
+
#
|
14
|
+
class ArgumentListParser
|
15
|
+
include Parslet
|
16
|
+
|
17
|
+
rule :argument_list do
|
18
|
+
expression.as(:argument) >>
|
19
|
+
(comma >> expression.as(:argument)).repeat
|
20
|
+
end
|
21
|
+
rule :expression do
|
22
|
+
string
|
23
|
+
end
|
24
|
+
rule :string do
|
25
|
+
str('"') >>
|
26
|
+
(
|
27
|
+
str('\\') >> any |
|
28
|
+
str('"').absent? >> any
|
29
|
+
).repeat.as(:string) >>
|
30
|
+
str('"') >> space?
|
31
|
+
end
|
32
|
+
rule :comma do
|
33
|
+
str(',') >> space?
|
34
|
+
end
|
35
|
+
rule :space? do
|
36
|
+
space.maybe
|
37
|
+
end
|
38
|
+
rule :space do
|
39
|
+
match("[ \t]").repeat(1)
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse(str)
|
43
|
+
argument_list.parse(str)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
describe ArgumentListParser do
|
47
|
+
let(:instance) { ArgumentListParser.new }
|
48
|
+
it "should have method expression" do
|
49
|
+
instance.should respond_to(:expression)
|
50
|
+
end
|
51
|
+
it 'should parse "arg1", "arg2"' do
|
52
|
+
result = ArgumentListParser.new.parse('"arg1", "arg2"')
|
53
|
+
|
54
|
+
result.size.should == 2
|
55
|
+
result.each do |r|
|
56
|
+
r[:argument]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
it 'should parse "arg1", "arg2", "arg3"' do
|
60
|
+
result = ArgumentListParser.new.parse('"arg1", "arg2", "arg3"')
|
61
|
+
|
62
|
+
result.size.should == 3
|
63
|
+
result.each do |r|
|
64
|
+
r[:argument]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class ParensParser < Parslet::Parser
|
70
|
+
rule(:balanced) {
|
71
|
+
str('(').as(:l) >> balanced.maybe.as(:m) >> str(')').as(:r)
|
72
|
+
}
|
73
|
+
|
74
|
+
root(:balanced)
|
75
|
+
end
|
76
|
+
describe ParensParser do
|
77
|
+
let(:instance) { ParensParser.new }
|
78
|
+
|
79
|
+
context "statefulness: trying several expressions in sequence" do
|
80
|
+
it "should not be stateful" do
|
81
|
+
# NOTE: Since you've come here to read this, I'll explain why
|
82
|
+
# this is broken and not fixed: You're looking at the tuning branch,
|
83
|
+
# which rewrites a bunch of stuff - so I have failing tests to
|
84
|
+
# remind me of what is left to be done. And to remind you not to
|
85
|
+
# trust this code.
|
86
|
+
instance.parse('(())')
|
87
|
+
lambda {
|
88
|
+
instance.parse('((()))')
|
89
|
+
instance.parse('(((())))')
|
90
|
+
}.should_not raise_error
|
91
|
+
end
|
92
|
+
end
|
93
|
+
context "expression '(())'" do
|
94
|
+
let(:result) { instance.parse('(())') }
|
95
|
+
|
96
|
+
it "should yield a doubly nested hash" do
|
97
|
+
result.should be_a(Hash)
|
98
|
+
result.should have_key(:m)
|
99
|
+
result[:m].should be_a(Hash) # This was an array earlier
|
100
|
+
end
|
101
|
+
context "inner hash" do
|
102
|
+
let(:inner) { result[:m] }
|
103
|
+
|
104
|
+
it "should have nil as :m" do
|
105
|
+
inner[:m].should be_nil
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class ALanguage < Parslet::Parser
|
112
|
+
root(:expressions)
|
113
|
+
|
114
|
+
rule(:expressions) { (line >> eol).repeat(1) | line }
|
115
|
+
rule(:line) { space? >> an_expression.as(:exp).repeat }
|
116
|
+
rule(:an_expression) { str('a').as(:a) >> space? }
|
117
|
+
|
118
|
+
rule(:eol) { space? >> match["\n\r"].repeat(1) >> space? }
|
119
|
+
|
120
|
+
rule(:space?) { space.repeat }
|
121
|
+
rule(:space) { multiline_comment.as(:multi) | line_comment.as(:line) | str(' ') }
|
122
|
+
|
123
|
+
rule(:line_comment) { str('//') >> (match["\n\r"].absent? >> any).repeat }
|
124
|
+
rule(:multiline_comment) { str('/*') >> (str('*/').absent? >> any).repeat >> str('*/') }
|
125
|
+
end
|
126
|
+
describe ALanguage do
|
127
|
+
def remove_indent(s)
|
128
|
+
s.to_s.lines.map { |l| l.chomp.strip }.join("\n")
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should count lines correctly" do
|
132
|
+
cause = catch_failed_parse {
|
133
|
+
subject.parse('a
|
134
|
+
a a a
|
135
|
+
aaa // ff
|
136
|
+
/*
|
137
|
+
a
|
138
|
+
*/
|
139
|
+
b
|
140
|
+
')
|
141
|
+
}
|
142
|
+
|
143
|
+
remove_indent(cause.ascii_tree).should == remove_indent(%q(
|
144
|
+
Expected one of [(LINE EOL){1, }, LINE] at line 1 char 1.
|
145
|
+
|- Extra input after last repetition at line 7 char 11.
|
146
|
+
| `- Failed to match sequence (LINE EOL) at line 7 char 11.
|
147
|
+
| `- Failed to match sequence (SPACE? [\n\r]{1, } SPACE?) at line 7 char 11.
|
148
|
+
| `- Expected at least 1 of [\n\r] at line 7 char 11.
|
149
|
+
| `- Failed to match [\n\r] at line 7 char 11.
|
150
|
+
`- Don't know what to do with "\n " at line 1 char 2.).strip)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class BLanguage < Parslet::Parser
|
155
|
+
root :expression
|
156
|
+
rule(:expression) { b.as(:one) >> b.as(:two) }
|
157
|
+
rule(:b) { str('b') }
|
158
|
+
end
|
159
|
+
describe BLanguage do
|
160
|
+
it "should parse 'bb'" do
|
161
|
+
subject.should parse('bb').as(:one => 'b', :two => 'b')
|
162
|
+
end
|
163
|
+
it "should transform with binding constraint" do
|
164
|
+
transform = Parslet::Transform.new do |t|
|
165
|
+
t.rule(:one => simple(:b), :two => simple(:b)) { :ok }
|
166
|
+
end
|
167
|
+
transform.apply(subject.parse('bb')).should == :ok
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class UnicodeLanguage < Parslet::Parser
|
172
|
+
root :gobble
|
173
|
+
rule(:gobble) { any.repeat }
|
174
|
+
end
|
175
|
+
describe UnicodeLanguage do
|
176
|
+
it "should parse UTF-8 strings" do
|
177
|
+
subject.should parse('éèäöü').as('éèäöü')
|
178
|
+
subject.should parse('RubyKaigi2009のテーマは、「変わる/変える」です。 前回の').as('RubyKaigi2009のテーマは、「変わる/変える」です。 前回の')
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class UnicodeSentenceLanguage < Parslet::Parser
|
183
|
+
rule(:sentence) { (match('[^。]').repeat(1) >> str("。")).as(:sentence) }
|
184
|
+
rule(:sentences) { sentence.repeat }
|
185
|
+
root(:sentences)
|
186
|
+
end
|
187
|
+
describe UnicodeSentenceLanguage do
|
188
|
+
let(:string) {
|
189
|
+
"RubyKaigi2009のテーマは、「変わる/変える」です。 前回の" +
|
190
|
+
"RubyKaigi2008のテーマであった「多様性」の言葉の通り、 " +
|
191
|
+
"2008年はRubyそのものに関しても、またRubyの活躍する舞台に関しても、 " +
|
192
|
+
"ますます多様化が進みつつあります。RubyKaigi2008は、そのような " +
|
193
|
+
"Rubyの生態系をあらためて認識する場となりました。 しかし、" +
|
194
|
+
"こうした多様化が進む中、異なる者同士が単純に距離を 置いたままでは、" +
|
195
|
+
"その違いを認識したところであまり意味がありません。 異なる実装、" +
|
196
|
+
"異なる思想、異なる背景といった、様々な多様性を理解しつつ、 " +
|
197
|
+
"すり合わせるべきものをすり合わせ、変えていくべきところを " +
|
198
|
+
"変えていくことが、豊かな未来へとつながる道に違いありません。"
|
199
|
+
}
|
200
|
+
|
201
|
+
it "should parse sentences" do
|
202
|
+
subject.should parse(string)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class TwoCharLanguage < Parslet::Parser
|
207
|
+
root :twochar
|
208
|
+
rule(:twochar) { any >> str('2') }
|
209
|
+
end
|
210
|
+
describe TwoCharLanguage do
|
211
|
+
def di(s)
|
212
|
+
s.strip.to_s.lines.map { |l| l.chomp.strip }.join("\n")
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should raise an error" do
|
216
|
+
error = catch_failed_parse {
|
217
|
+
subject.parse('123') }
|
218
|
+
di(error.ascii_tree).should == di(%q(
|
219
|
+
Failed to match sequence (. '2') at line 1 char 2.
|
220
|
+
`- Don't know what to do with "3" at line 1 char 3.
|
221
|
+
))
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Issue #68: Extra input reporting, written by jmettraux
|
226
|
+
class RepetitionParser < Parslet::Parser
|
227
|
+
rule(:nl) { match('[\s]').repeat(1) }
|
228
|
+
rule(:nl?) { nl.maybe }
|
229
|
+
rule(:sp) { str(' ').repeat(1) }
|
230
|
+
rule(:sp?) { str(' ').repeat(0) }
|
231
|
+
rule(:line) { sp >> str('line') }
|
232
|
+
rule(:body) { ((line | block) >> nl).repeat(0) }
|
233
|
+
rule(:block) { sp? >> str('begin') >> sp >> match('[a-z]') >> nl >>
|
234
|
+
body >> sp? >> str('end') }
|
235
|
+
rule(:blocks) { nl? >> block >> (nl >> block).repeat(0) >> nl? }
|
236
|
+
|
237
|
+
root(:blocks)
|
238
|
+
end
|
239
|
+
describe RepetitionParser do
|
240
|
+
def di(s)
|
241
|
+
s.strip.to_s.lines.map { |l| l.chomp.strip }.join("\n")
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'parses a block' do
|
245
|
+
subject.parse(%q{
|
246
|
+
begin a
|
247
|
+
end
|
248
|
+
})
|
249
|
+
end
|
250
|
+
it 'parses nested blocks' do
|
251
|
+
subject.parse(%q{
|
252
|
+
begin a
|
253
|
+
begin b
|
254
|
+
end
|
255
|
+
end
|
256
|
+
})
|
257
|
+
end
|
258
|
+
it 'parses successive blocks' do
|
259
|
+
subject.parse(%q{
|
260
|
+
begin a
|
261
|
+
end
|
262
|
+
begin b
|
263
|
+
end
|
264
|
+
})
|
265
|
+
end
|
266
|
+
it 'fails gracefully on a missing end' do
|
267
|
+
error = catch_failed_parse {
|
268
|
+
subject.parse(%q{
|
269
|
+
begin a
|
270
|
+
begin b
|
271
|
+
end
|
272
|
+
}) }
|
273
|
+
|
274
|
+
di(error.ascii_tree).should == di(%q(
|
275
|
+
Failed to match sequence (NL? BLOCK (NL BLOCK){0, } NL?) at line 2 char 11.
|
276
|
+
`- Failed to match sequence (SP? 'begin' SP [a-z] NL BODY SP? 'end') at line 5 char 9.
|
277
|
+
`- Premature end of input at line 5 char 9.
|
278
|
+
))
|
279
|
+
end
|
280
|
+
it 'fails gracefully on a missing end (2)' do
|
281
|
+
error = catch_failed_parse {
|
282
|
+
subject.parse(%q{
|
283
|
+
begin a
|
284
|
+
end
|
285
|
+
begin b
|
286
|
+
begin c
|
287
|
+
end
|
288
|
+
}) }
|
289
|
+
|
290
|
+
di(error.ascii_tree).should == di(%q(
|
291
|
+
Failed to match sequence (NL? BLOCK (NL BLOCK){0, } NL?) at line 3 char 14.
|
292
|
+
`- Don't know what to do with "begin b\n " at line 4 char 11.
|
293
|
+
))
|
294
|
+
end
|
295
|
+
it 'fails gracefully on a missing end (deepest reporter)' do
|
296
|
+
error = catch_failed_parse {
|
297
|
+
subject.parse(%q{
|
298
|
+
begin a
|
299
|
+
end
|
300
|
+
begin b
|
301
|
+
begin c
|
302
|
+
li
|
303
|
+
end
|
304
|
+
end
|
305
|
+
},
|
306
|
+
:reporter => Parslet::ErrorReporter::Deepest.new) }
|
307
|
+
|
308
|
+
di(error.ascii_tree).should == di(%q(
|
309
|
+
Failed to match sequence (NL? BLOCK (NL BLOCK){0, } NL?) at line 3 char 16.
|
310
|
+
`- Expected "end", but got "li\n" at line 6 char 17.
|
311
|
+
))
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|