parslet 1.7.0 → 1.7.1
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.
- 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
|