neg 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,30 @@
1
+ #--
2
+ # Copyright (c) 2012-2012, John Mettraux, jmettraux@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ # Made in Japan.
23
+ #++
24
+
25
+
26
+ module Neg
27
+
28
+ VERSION = '0.2.0'
29
+ end
30
+
data/neg.gemspec ADDED
@@ -0,0 +1,33 @@
1
+
2
+ Gem::Specification.new do |s|
3
+
4
+ s.name = 'neg'
5
+
6
+ s.version = File.read(
7
+ File.expand_path('../lib/neg/version.rb', __FILE__)
8
+ ).match(/ VERSION *= *['"]([^'"]+)/)[1]
9
+
10
+ s.platform = Gem::Platform::RUBY
11
+ s.authors = [ 'John Mettraux' ]
12
+ s.email = [ 'jmettraux@gmail.com' ]
13
+ s.homepage = 'https://github.com/jmettraux/leg'
14
+ s.rubyforge_project = 'ruote'
15
+ s.summary = 'a neg narser'
16
+
17
+ s.description = %{
18
+ not a peg parser, just a neg narser
19
+ }.strip
20
+
21
+ #s.files = `git ls-files`.split("\n")
22
+ s.files = Dir[
23
+ 'Rakefile',
24
+ 'lib/**/*.rb', 'spec/**/*.rb', 'test/**/*.rb',
25
+ '*.gemspec', '*.txt', '*.rdoc', '*.md'
26
+ ]
27
+
28
+ s.add_development_dependency 'rake'
29
+ s.add_development_dependency 'rspec', '>= 2.9.0'
30
+
31
+ s.require_path = 'lib'
32
+ end
33
+
@@ -0,0 +1,75 @@
1
+
2
+ require 'spec_helper'
3
+
4
+
5
+ describe Neg::Input do
6
+
7
+ before(:each) do
8
+
9
+ @input = Neg::Input.new("the quick blue fox\n jumped the shark\n")
10
+ end
11
+
12
+ it 'starts at zero' do
13
+
14
+ @input.position.should == [ 0, 1, 1 ]
15
+ end
16
+
17
+ describe '#read' do
18
+
19
+ it "reads and moves" do
20
+
21
+ @input.read(5).should == 'the q'
22
+
23
+ @input.position.should == [ 5, 1, 6 ]
24
+ end
25
+
26
+ it "reads and moves (same line)" do
27
+
28
+ @input.read(9).should == 'the quick'
29
+
30
+ @input.position.should == [ 9, 1, 10 ]
31
+ end
32
+
33
+ it "reads and moves (new line)" do
34
+
35
+ @input.read(21).should == "the quick blue fox\n j"
36
+
37
+ @input.position.should == [ 21, 2, 2 ]
38
+ end
39
+ end
40
+
41
+ describe '#rewind' do
42
+
43
+ it 'rewinds' do
44
+
45
+ @input.read(21)
46
+ @input.rewind
47
+
48
+ @input.position.should == [ 0, 1, 1 ]
49
+ end
50
+
51
+ it 'rewinds with a [ off, line, col ]' do
52
+
53
+ @input.read(21)
54
+ @input.rewind([ 5, 1, 6 ])
55
+
56
+ @input.read(4).should == 'uick'
57
+ end
58
+ end
59
+
60
+ describe '#eoi?' do
61
+
62
+ it 'returns false if the end of input has not yet been reached' do
63
+
64
+ @input.eoi?.should == false
65
+ end
66
+
67
+ it 'returns true if the end of input has been reached' do
68
+
69
+ @input.read(37)
70
+
71
+ @input.eoi?.should == true
72
+ end
73
+ end
74
+ end
75
+
@@ -0,0 +1,48 @@
1
+
2
+ require 'spec_helper'
3
+
4
+
5
+ describe Neg::Parser::AlternativeParser do
6
+
7
+ class AltParser < Neg::Parser
8
+ text == `x` | `y`
9
+ end
10
+
11
+ it 'parses' do
12
+
13
+ AltParser.parse('x').should ==
14
+ [ :text, true, [ 0, 1, 1 ], [
15
+ [ nil, true, [ 0, 1, 1 ], 'x' ] ] ]
16
+ end
17
+
18
+ it 'parses (2nd alternative succeeds)' do
19
+
20
+ AltParser.parse('y').should ==
21
+ [ :text, true, [ 0, 1, 1 ], [
22
+ [ nil, false, [ 0, 1, 1 ], 'expected "x", got "y"' ],
23
+ [ nil, true, [ 0, 1, 1 ], 'y' ] ] ]
24
+ end
25
+
26
+ it 'fails gracefully' do
27
+
28
+ AltParser.parse('z').should ==
29
+ [ :text, false, [ 0, 1, 1 ], [
30
+ [ nil, false, [ 0, 1, 1 ], 'expected "x", got "z"' ],
31
+ [ nil, false, [ 0, 1, 1 ], 'expected "y", got "z"' ] ] ]
32
+ end
33
+
34
+ it 'goes beyond two elements' do
35
+
36
+ parser = Class.new(Neg::Parser) do
37
+ text == `x` | `y` | `z`
38
+ end
39
+
40
+ text = parser.text
41
+
42
+ text.class.should ==
43
+ Neg::Parser::NonTerminalParser
44
+ text.child.children.collect(&:class).should ==
45
+ [ Neg::Parser::StringParser ] * 3
46
+ end
47
+ end
48
+
@@ -0,0 +1,72 @@
1
+
2
+ require 'spec_helper'
3
+
4
+
5
+ describe Neg::Parser::CharacterParser do
6
+
7
+ context '_ (any)' do
8
+
9
+ let(:parser) {
10
+ Class.new(Neg::Parser) do
11
+ text == `x` + _
12
+ end
13
+ }
14
+
15
+ it 'parses "xy"' do
16
+
17
+ parser.parse('xy').should ==
18
+ [ :text,
19
+ true,
20
+ [ 0, 1, 1 ],
21
+ [ [ nil, true, [ 0, 1, 1 ], "x" ],
22
+ [ nil, true, [ 1, 1, 2 ], "y" ] ] ]
23
+ end
24
+
25
+ it 'fails gracefully' do
26
+
27
+ parser.parse('x').should ==
28
+ [ :text,
29
+ false,
30
+ [ 0, 1, 1],
31
+ [ [ nil, true, [ 0, 1, 1 ], "x" ],
32
+ [ nil, false, [ 1, 1, 2 ], "\"\" doesn't match nil" ] ] ]
33
+ end
34
+
35
+ it 'is rendered correctly via #to_s' do
36
+
37
+ parser.to_s.strip.should == %q{
38
+ :
39
+ text == (`x` + _)
40
+ root: text
41
+ }.strip
42
+ end
43
+ end
44
+
45
+ context "_('0-9-') (ranges)" do
46
+
47
+ let(:parser) {
48
+ Class.new(Neg::Parser) do
49
+ text == `tel:` + _('0-9-') * 1
50
+ end
51
+ }
52
+
53
+ it 'parses "tel:0-99"' do
54
+
55
+ parser.parse('tel:0-99').should ==
56
+ [ :text,
57
+ true,
58
+ [ 0, 1, 1 ],
59
+ [ [ nil, true, [ 0, 1, 1 ], "tel:" ],
60
+ [ nil,
61
+ true,
62
+ [ 4, 1, 5 ],
63
+ [ [ nil, true, [ 4, 1, 5 ], "0" ],
64
+ [ nil, true, [ 5, 1, 6 ], "-" ],
65
+ [ nil, true, [ 6, 1, 7 ], "9" ],
66
+ [ nil, true, [ 7, 1, 8 ], "9" ] ] ] ] ]
67
+ end
68
+
69
+ it 'fails gracefully'
70
+ end
71
+ end
72
+
@@ -0,0 +1,85 @@
1
+
2
+ require 'spec_helper'
3
+
4
+
5
+ describe Neg::Parser::NonTerminalParser do
6
+
7
+ context 'name == ...' do
8
+
9
+ it 'parses' do
10
+
11
+ parser = Class.new(Neg::Parser) do
12
+ text == x | z
13
+ x == `x`
14
+ z == `zz` | `z`
15
+ end
16
+
17
+ parser.parse('x')[1].should == true
18
+ parser.parse('z')[1].should == true
19
+ parser.parse('zz')[1].should == true
20
+ parser.parse('y')[1].should == false
21
+ end
22
+
23
+ it 'sets its name in the result (as a Symbol)' do
24
+
25
+ parser = Class.new(Neg::Parser) do
26
+ x == `x` | `X` | `xx`
27
+ end
28
+
29
+ parser.parse('X').should ==
30
+ [ :x, true, [ 0, 1, 1 ], [
31
+ [ nil, false, [ 0, 1, 1 ], "expected \"x\", got \"X\"" ],
32
+ [ nil, true, [ 0, 1, 1 ], "X" ] ] ]
33
+ end
34
+
35
+ it 'is rendered as x when on the right side' do
36
+
37
+ parser = Class.new(Neg::Parser) do
38
+ word == car | bus
39
+ car == `car`
40
+ bus == `bus`
41
+ end
42
+
43
+ parser.to_s.strip.should == %q{
44
+ :
45
+ bus == `bus`
46
+ car == `car`
47
+ word == (car | bus)
48
+ root: word
49
+ }.strip
50
+ end
51
+ end
52
+
53
+ context '...["name"]' do
54
+
55
+ let(:parser) {
56
+ Class.new(Neg::Parser) do
57
+ transportation ==
58
+ (`car` | `bus`)['vehicle'] +
59
+ `_` +
60
+ (`cluj` | `split`)['city']
61
+ end
62
+ }
63
+
64
+ it 'is rendered as []' do
65
+
66
+ parser.to_s.strip.should == %q{
67
+ :
68
+ transportation == ((`car` | `bus`)["vehicle"] + `_` + (`cluj` | `split`)["city"])
69
+ root: transportation
70
+ }.strip
71
+ end
72
+
73
+ it 'sets the name (as a string) in the result' do
74
+
75
+ parser.parse('car_cluj').should ==
76
+ [ :transportation,
77
+ true,
78
+ [ 0, 1, 1],
79
+ [ [ 'vehicle', true, [ 0, 1, 1 ], [ [ nil, true, [ 0, 1, 1 ], 'car' ] ] ],
80
+ [ nil, true, [ 3, 1, 4 ], '_'],
81
+ [ 'city', true, [ 4, 1, 5 ], [ [ nil, true, [ 4, 1, 5 ], 'cluj' ] ] ] ] ]
82
+ end
83
+ end
84
+ end
85
+
@@ -0,0 +1,142 @@
1
+
2
+ require 'spec_helper'
3
+
4
+
5
+ describe Neg::Parser::RepetitionParser do
6
+
7
+ context '`x` * -1 (maybe)' do
8
+
9
+ let(:parser) {
10
+ Class.new(Neg::Parser) do
11
+ text == `x` * -1
12
+ end
13
+ }
14
+
15
+ it 'parses the empty string' do
16
+
17
+ parser.parse('').should ==
18
+ [ :text, true, [ 0, 1, 1 ], [] ]
19
+ end
20
+
21
+ it 'fails gracefully' do
22
+
23
+ lambda {
24
+ parser.parse('xx')
25
+ }.should raise_error(
26
+ Neg::UnconsumedInputError,
27
+ 'remaining: "x"')
28
+ end
29
+
30
+ it 'is rendered correctly via #to_s' do
31
+
32
+ parser.to_s.strip.should == %q{
33
+ :
34
+ text == `x` * -1
35
+ root: text
36
+ }.strip
37
+ end
38
+ end
39
+
40
+ context '`x` * 0 (0 or more)' do
41
+
42
+ let(:parser) {
43
+ Class.new(Neg::Parser) do
44
+ text == `x` * 0
45
+ end
46
+ }
47
+
48
+ it 'parses the empty string' do
49
+
50
+ parser.parse('').should ==
51
+ [ :text, true, [ 0, 1, 1 ], [] ]
52
+ end
53
+
54
+ it 'parses' do
55
+
56
+ parser.parse('xxx').should ==
57
+ [ :text, true, [ 0, 1, 1 ], [
58
+ [ nil, true, [ 0, 1, 1 ], 'x' ],
59
+ [ nil, true, [ 1, 1, 2 ], 'x' ],
60
+ [ nil, true, [ 2, 1, 3 ], 'x' ] ] ]
61
+ end
62
+
63
+ it 'fails gracefully' do
64
+
65
+ lambda {
66
+ parser.parse('a')
67
+ }.should raise_error(
68
+ Neg::UnconsumedInputError,
69
+ 'remaining: "a"')
70
+ end
71
+ end
72
+
73
+ context '`x` * 2 (at least 2)' do
74
+
75
+ let(:parser) {
76
+ Class.new(Neg::Parser) do
77
+ text == `x` * 2
78
+ end
79
+ }
80
+
81
+ it 'parses' do
82
+
83
+ parser.parse('xxx').should ==
84
+ [ :text, true, [ 0, 1, 1 ], [
85
+ [ nil, true, [ 0, 1, 1 ], 'x' ],
86
+ [ nil, true, [ 1, 1, 2 ], 'x' ],
87
+ [ nil, true, [ 2, 1, 3 ], 'x' ] ] ]
88
+ end
89
+
90
+ it 'fails gracefully' do
91
+
92
+ parser.parse('x').should ==
93
+ [ :text, false, [ 0, 1, 1 ], [
94
+ [ nil, true, [ 0, 1, 1 ], 'x' ],
95
+ [ nil, false, [ 1, 1, 2 ], 'expected "x", got ""' ] ] ]
96
+ end
97
+ end
98
+
99
+ context '`x` * [ 3, 3 ] (at least 3, max 3)' do
100
+
101
+ let(:parser) {
102
+ Class.new(Neg::Parser) do
103
+ text == `x` * [ 3, 3 ]
104
+ end
105
+ }
106
+
107
+ it 'parses' do
108
+
109
+ parser.parse('xxx').should ==
110
+ [ :text, true, [ 0, 1, 1 ], [
111
+ [ nil, true, [ 0, 1, 1 ], 'x' ],
112
+ [ nil, true, [ 1, 1, 2 ], 'x' ],
113
+ [ nil, true, [ 2, 1, 3 ], 'x' ] ] ]
114
+ end
115
+
116
+ it 'fails gracefully' do
117
+
118
+ parser.parse('xx').should ==
119
+ [ :text, false, [ 0, 1, 1 ], [
120
+ [ nil, true, [ 0, 1, 1 ], 'x' ],
121
+ [ nil, true, [ 1, 1, 2 ], 'x' ],
122
+ [ nil, false, [ 2, 1, 3 ], 'expected "x", got ""' ] ] ]
123
+ end
124
+
125
+ it 'fails gracefully (unconsumed input)' do
126
+
127
+ lambda {
128
+ parser.parse('xxxx')
129
+ }.should raise_error(Neg::UnconsumedInputError, 'remaining: "x"')
130
+ end
131
+
132
+ it 'is rendered correctly via #to_s' do
133
+
134
+ parser.to_s.strip.should == %q{
135
+ :
136
+ text == `x` * [3, 3]
137
+ root: text
138
+ }.strip
139
+ end
140
+ end
141
+ end
142
+