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
@@ -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
|