parslet 1.7.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
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