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
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'parslet/convenience' do
|
4
|
+
require 'parslet/convenience'
|
5
|
+
include Parslet
|
6
|
+
|
7
|
+
class FooParser < Parslet::Parser
|
8
|
+
rule(:foo) { str('foo') }
|
9
|
+
root(:foo)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'parse_with_debug' do
|
13
|
+
let(:parser) { flexmock FooParser.new }
|
14
|
+
context 'internal' do
|
15
|
+
before(:each) do
|
16
|
+
# Suppress output.
|
17
|
+
#
|
18
|
+
parser.should_receive(:puts)
|
19
|
+
end
|
20
|
+
it 'should exist' do
|
21
|
+
lambda { parser.parse_with_debug('anything') }.should_not raise_error
|
22
|
+
end
|
23
|
+
it 'should catch ParseFailed exceptions' do
|
24
|
+
lambda { parser.parse_with_debug('bar') }.should_not raise_error
|
25
|
+
end
|
26
|
+
it 'should parse correct input like #parse' do
|
27
|
+
lambda { parser.parse_with_debug('foo') }.should_not raise_error
|
28
|
+
end
|
29
|
+
end
|
30
|
+
context 'output' do
|
31
|
+
it 'should puts once for tree output' do
|
32
|
+
parser.should_receive(:puts).once
|
33
|
+
|
34
|
+
parser.parse_with_debug('incorrect')
|
35
|
+
end
|
36
|
+
it "should puts once for the error on unconsumed input" do
|
37
|
+
parser.should_receive(:puts).once
|
38
|
+
|
39
|
+
parser.parse_with_debug('foobar')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should work for all parslets" do
|
44
|
+
str('foo').parse_with_debug('foo')
|
45
|
+
match['bar'].parse_with_debug('a')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Parslet::ErrorReporter::Contextual do
|
4
|
+
|
5
|
+
let(:reporter) { described_class.new }
|
6
|
+
let(:fake_source) { flexmock('source') }
|
7
|
+
let(:fake_atom) { flexmock('atom') }
|
8
|
+
let(:fake_cause) { flexmock('cause') }
|
9
|
+
|
10
|
+
describe '#err' do
|
11
|
+
before(:each) { fake_source.should_receive(
|
12
|
+
:pos => 13,
|
13
|
+
:line_and_column => [1,1]) }
|
14
|
+
|
15
|
+
it "returns the deepest cause" do
|
16
|
+
flexmock(reporter).
|
17
|
+
should_receive(:deepest).and_return(:deepest)
|
18
|
+
reporter.err('parslet', fake_source, 'message').
|
19
|
+
should == :deepest
|
20
|
+
end
|
21
|
+
end
|
22
|
+
describe '#err_at' do
|
23
|
+
before(:each) { fake_source.should_receive(
|
24
|
+
:pos => 13,
|
25
|
+
:line_and_column => [1,1]) }
|
26
|
+
|
27
|
+
it "returns the deepest cause" do
|
28
|
+
flexmock(reporter).
|
29
|
+
should_receive(:deepest).and_return(:deepest)
|
30
|
+
reporter.err('parslet', fake_source, 'message', 13).
|
31
|
+
should == :deepest
|
32
|
+
end
|
33
|
+
end
|
34
|
+
describe '#deepest(cause)' do
|
35
|
+
def fake_cause(pos=13, children=nil)
|
36
|
+
flexmock('cause' + pos.to_s, :pos => pos, :children => children)
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when there is no deepest cause yet" do
|
40
|
+
let(:cause) { fake_cause }
|
41
|
+
it "returns the given cause" do
|
42
|
+
reporter.deepest(cause).should == cause
|
43
|
+
end
|
44
|
+
end
|
45
|
+
context "when the previous cause is deeper (no relationship)" do
|
46
|
+
let(:previous) { fake_cause }
|
47
|
+
before(:each) {
|
48
|
+
reporter.deepest(previous) }
|
49
|
+
|
50
|
+
it "returns the previous cause" do
|
51
|
+
reporter.deepest(fake_cause(12)).
|
52
|
+
should == previous
|
53
|
+
end
|
54
|
+
end
|
55
|
+
context "when the previous cause is deeper (child)" do
|
56
|
+
let(:previous) { fake_cause }
|
57
|
+
before(:each) {
|
58
|
+
reporter.deepest(previous) }
|
59
|
+
|
60
|
+
it "returns the given cause" do
|
61
|
+
given = fake_cause(12, [previous])
|
62
|
+
reporter.deepest(given).should == given
|
63
|
+
end
|
64
|
+
end
|
65
|
+
context "when the previous cause is shallower" do
|
66
|
+
before(:each) {
|
67
|
+
reporter.deepest(fake_cause) }
|
68
|
+
|
69
|
+
it "stores the cause as deepest" do
|
70
|
+
deeper = fake_cause(14)
|
71
|
+
reporter.deepest(deeper)
|
72
|
+
reporter.deepest_cause.should == deeper
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
describe '#reset' do
|
77
|
+
before(:each) { fake_source.should_receive(
|
78
|
+
:pos => Parslet::Position.new('source', 13),
|
79
|
+
:line_and_column => [1,1]) }
|
80
|
+
|
81
|
+
it "resets deepest cause on success of sibling expression" do
|
82
|
+
flexmock(reporter).
|
83
|
+
should_receive(:deepest).and_return(:deepest)
|
84
|
+
reporter.err('parslet', fake_source, 'message').
|
85
|
+
should == :deepest
|
86
|
+
flexmock(reporter).
|
87
|
+
should_receive(:reset).once
|
88
|
+
reporter.succ(fake_source)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'label' do
|
93
|
+
before(:each) { fake_source.should_receive(
|
94
|
+
:pos => Parslet::Position.new('source', 13),
|
95
|
+
:line_and_column => [1,1]) }
|
96
|
+
|
97
|
+
it "sets label if atom has one" do
|
98
|
+
fake_atom.should_receive(:label).once.and_return('label')
|
99
|
+
fake_cause.should_receive(:set_label).once
|
100
|
+
flexmock(reporter).
|
101
|
+
should_receive(:deepest).and_return(fake_cause)
|
102
|
+
reporter.err(fake_atom, fake_source, 'message').
|
103
|
+
should == fake_cause
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'does not set label if atom does not have one' do
|
107
|
+
flexmock(reporter).
|
108
|
+
should_receive(:deepest).and_return(:deepest)
|
109
|
+
fake_atom.should_receive(:update_label).never
|
110
|
+
reporter.err(fake_atom, fake_source, 'message').
|
111
|
+
should == :deepest
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Parslet::ErrorReporter::Deepest do
|
4
|
+
let(:reporter) { described_class.new }
|
5
|
+
let(:fake_source) { flexmock('source') }
|
6
|
+
|
7
|
+
describe '#err' do
|
8
|
+
before(:each) { fake_source.should_receive(
|
9
|
+
:pos => 13,
|
10
|
+
:line_and_column => [1,1]) }
|
11
|
+
|
12
|
+
it "returns the deepest cause" do
|
13
|
+
flexmock(reporter).
|
14
|
+
should_receive(:deepest).and_return(:deepest)
|
15
|
+
reporter.err('parslet', fake_source, 'message').
|
16
|
+
should == :deepest
|
17
|
+
end
|
18
|
+
end
|
19
|
+
describe '#err_at' do
|
20
|
+
before(:each) { fake_source.should_receive(
|
21
|
+
:pos => 13,
|
22
|
+
:line_and_column => [1,1]) }
|
23
|
+
|
24
|
+
it "returns the deepest cause" do
|
25
|
+
flexmock(reporter).
|
26
|
+
should_receive(:deepest).and_return(:deepest)
|
27
|
+
reporter.err('parslet', fake_source, 'message', 13).
|
28
|
+
should == :deepest
|
29
|
+
end
|
30
|
+
end
|
31
|
+
describe '#deepest(cause)' do
|
32
|
+
def fake_cause(pos=13, children=nil)
|
33
|
+
flexmock('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
|
+
it "returns the given cause" do
|
39
|
+
reporter.deepest(cause).should == cause
|
40
|
+
end
|
41
|
+
end
|
42
|
+
context "when the previous cause is deeper (no relationship)" do
|
43
|
+
let(:previous) { fake_cause }
|
44
|
+
before(:each) {
|
45
|
+
reporter.deepest(previous) }
|
46
|
+
|
47
|
+
it "returns the previous cause" do
|
48
|
+
reporter.deepest(fake_cause(12)).
|
49
|
+
should == previous
|
50
|
+
end
|
51
|
+
end
|
52
|
+
context "when the previous cause is deeper (child)" do
|
53
|
+
let(:previous) { fake_cause }
|
54
|
+
before(:each) {
|
55
|
+
reporter.deepest(previous) }
|
56
|
+
|
57
|
+
it "returns the given cause" do
|
58
|
+
given = fake_cause(12, [previous])
|
59
|
+
reporter.deepest(given).should == given
|
60
|
+
end
|
61
|
+
end
|
62
|
+
context "when the previous cause is shallower" do
|
63
|
+
before(:each) {
|
64
|
+
reporter.deepest(fake_cause) }
|
65
|
+
|
66
|
+
it "stores the cause as deepest" do
|
67
|
+
deeper = fake_cause(14)
|
68
|
+
reporter.deepest(deeper)
|
69
|
+
reporter.deepest_cause.should == deeper
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Parslet::Parser, "exporting to other lingos" do
|
4
|
+
class MiniLisp < Parslet::Parser
|
5
|
+
root :expression
|
6
|
+
rule(:expression) {
|
7
|
+
space? >> str('(') >> space? >> body >> str(')')
|
8
|
+
}
|
9
|
+
|
10
|
+
rule(:body) {
|
11
|
+
(expression | identifier | float | integer | string).repeat.as(:exp)
|
12
|
+
}
|
13
|
+
|
14
|
+
rule(:space) {
|
15
|
+
match('\s').repeat(1)
|
16
|
+
}
|
17
|
+
rule(:space?) {
|
18
|
+
space.maybe
|
19
|
+
}
|
20
|
+
|
21
|
+
rule(:identifier) {
|
22
|
+
(match('[a-zA-Z=*]') >> match('[a-zA-Z=*_]').repeat).as(:identifier) >> space?
|
23
|
+
}
|
24
|
+
|
25
|
+
rule(:float) {
|
26
|
+
(
|
27
|
+
integer >> (
|
28
|
+
str('.') >> match('[0-9]').repeat(1) |
|
29
|
+
str('e') >> match('[0-9]').repeat(1)
|
30
|
+
).as(:e)
|
31
|
+
).as(:float) >> space?
|
32
|
+
}
|
33
|
+
|
34
|
+
rule(:integer) {
|
35
|
+
((str('+') | str('-')).maybe >> match("[0-9]").repeat(1)).as(:integer) >> space?
|
36
|
+
}
|
37
|
+
|
38
|
+
rule(:string) {
|
39
|
+
str('"') >> (
|
40
|
+
str('\\') >> any |
|
41
|
+
str('"').absent? >> any
|
42
|
+
).repeat.as(:string) >> str('"') >> space?
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
# I only update the files once I've verified the new syntax to work with
|
47
|
+
# the respective tools. This is more an acceptance test than a real spec.
|
48
|
+
|
49
|
+
describe "<- #to_citrus" do
|
50
|
+
let(:citrus) { File.read(
|
51
|
+
File.join(File.dirname(__FILE__), 'minilisp.citrus'))
|
52
|
+
}
|
53
|
+
it "should be valid citrus syntax" do
|
54
|
+
# puts MiniLisp.new.to_citrus
|
55
|
+
MiniLisp.new.to_citrus.should == citrus
|
56
|
+
end
|
57
|
+
end
|
58
|
+
describe "<- #to_treetop" do
|
59
|
+
let(:treetop) { File.read(
|
60
|
+
File.join(File.dirname(__FILE__), 'minilisp.tt'))
|
61
|
+
}
|
62
|
+
it "should be valid treetop syntax" do
|
63
|
+
# puts MiniLisp.new.to_treetop
|
64
|
+
MiniLisp.new.to_treetop.should == treetop
|
65
|
+
end
|
66
|
+
end
|
67
|
+
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
|
2
|
+
rule root
|
3
|
+
(expression)
|
4
|
+
end
|
5
|
+
rule expression
|
6
|
+
((space_p) "(" (space_p) (body) ")")
|
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
|
2
|
+
rule root
|
3
|
+
(expression)
|
4
|
+
end
|
5
|
+
rule expression
|
6
|
+
((space_p) "(" (space_p) (body) ")")
|
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
|