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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README +1 -1
  3. data/lib/parslet/context.rb +1 -1
  4. data/lib/parslet/parser.rb +1 -1
  5. data/parslet.gemspec +18 -0
  6. data/spec/acceptance/examples_spec.rb +37 -0
  7. data/spec/acceptance/infix_parser_spec.rb +112 -0
  8. data/spec/acceptance/regression_spec.rb +314 -0
  9. data/spec/acceptance/repetition_and_maybe_spec.rb +42 -0
  10. data/spec/acceptance/unconsumed_input_spec.rb +21 -0
  11. data/spec/parslet/atom_results_spec.rb +39 -0
  12. data/spec/parslet/atoms/alternative_spec.rb +26 -0
  13. data/spec/parslet/atoms/base_spec.rb +126 -0
  14. data/spec/parslet/atoms/capture_spec.rb +21 -0
  15. data/spec/parslet/atoms/combinations_spec.rb +5 -0
  16. data/spec/parslet/atoms/dsl_spec.rb +25 -0
  17. data/spec/parslet/atoms/entity_spec.rb +77 -0
  18. data/spec/parslet/atoms/infix_spec.rb +5 -0
  19. data/spec/parslet/atoms/lookahead_spec.rb +22 -0
  20. data/spec/parslet/atoms/named_spec.rb +4 -0
  21. data/spec/parslet/atoms/re_spec.rb +14 -0
  22. data/spec/parslet/atoms/repetition_spec.rb +24 -0
  23. data/spec/parslet/atoms/scope_spec.rb +26 -0
  24. data/spec/parslet/atoms/sequence_spec.rb +28 -0
  25. data/spec/parslet/atoms/str_spec.rb +15 -0
  26. data/spec/parslet/atoms/visitor_spec.rb +80 -0
  27. data/spec/parslet/atoms_spec.rb +429 -0
  28. data/spec/parslet/convenience_spec.rb +48 -0
  29. data/spec/parslet/error_reporter/contextual_spec.rb +115 -0
  30. data/spec/parslet/error_reporter/deepest_spec.rb +73 -0
  31. data/spec/parslet/error_reporter/tree_spec.rb +7 -0
  32. data/spec/parslet/export_spec.rb +67 -0
  33. data/spec/parslet/expression/treetop_spec.rb +74 -0
  34. data/spec/parslet/minilisp.citrus +29 -0
  35. data/spec/parslet/minilisp.tt +29 -0
  36. data/spec/parslet/parser_spec.rb +31 -0
  37. data/spec/parslet/parslet_spec.rb +38 -0
  38. data/spec/parslet/pattern_spec.rb +272 -0
  39. data/spec/parslet/position_spec.rb +14 -0
  40. data/spec/parslet/rig/rspec_spec.rb +54 -0
  41. data/spec/parslet/scope_spec.rb +45 -0
  42. data/spec/parslet/slice_spec.rb +144 -0
  43. data/spec/parslet/source/line_cache_spec.rb +74 -0
  44. data/spec/parslet/source_spec.rb +168 -0
  45. data/spec/parslet/transform/context_spec.rb +35 -0
  46. data/spec/parslet/transform_spec.rb +165 -0
  47. data/spec/spec_helper.rb +38 -0
  48. 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,7 @@
1
+ require 'spec_helper'
2
+
3
+ require 'parslet/error_reporter'
4
+
5
+ describe Parslet::ErrorReporter::Tree do
6
+
7
+ 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