sexp_grammar 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +5 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +25 -0
- data/LICENCE.md +22 -0
- data/Manifest.txt +15 -0
- data/README.md +46 -0
- data/Rakefile +23 -0
- data/lib/sexp_grammar.rb +31 -0
- data/lib/sexp_grammar/alternative.rb +28 -0
- data/lib/sexp_grammar/element.rb +9 -0
- data/lib/sexp_grammar/grammar.rb +57 -0
- data/lib/sexp_grammar/loader.rb +1 -0
- data/lib/sexp_grammar/many.rb +52 -0
- data/lib/sexp_grammar/reference.rb +30 -0
- data/lib/sexp_grammar/rule.rb +29 -0
- data/lib/sexp_grammar/sequence.rb +28 -0
- data/lib/sexp_grammar/terminal.rb +35 -0
- data/lib/sexp_grammar/version.rb +14 -0
- data/sexp_grammar.gemspec +188 -0
- data/sexp_grammar.noespec +25 -0
- data/spec/alternative/test_eat.rb +20 -0
- data/spec/alternative/test_match_q.rb +20 -0
- data/spec/bool_expr.yml +19 -0
- data/spec/grammar.yml +24 -0
- data/spec/grammar/test_compile_rule.rb +25 -0
- data/spec/grammar/test_compile_rule_defn.rb +98 -0
- data/spec/grammar/test_fetch.rb +16 -0
- data/spec/many/test_eat.rb +59 -0
- data/spec/many/test_initialize.rb +36 -0
- data/spec/many/test_match_q.rb +24 -0
- data/spec/reference/test_eat.rb +13 -0
- data/spec/reference/test_match_q.rb +20 -0
- data/spec/rule/test_eat.rb +21 -0
- data/spec/rule/test_match_q.rb +24 -0
- data/spec/sequence/test_eat.rb +20 -0
- data/spec/sequence/test_match_q.rb +25 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/terminal/test_eat.rb +20 -0
- data/spec/terminal/test_match_q.rb +56 -0
- data/spec/terminal/test_terminal_match.rb +89 -0
- data/spec/test_bool_expr.rb +27 -0
- data/spec/test_load.rb +35 -0
- data/spec/test_readme_examples.rb +44 -0
- data/spec/test_sexp_grammar.rb +8 -0
- data/tasks/debug_mail.rake +75 -0
- data/tasks/debug_mail.txt +13 -0
- data/tasks/gem.rake +68 -0
- data/tasks/spec_test.rake +71 -0
- data/tasks/unit_test.rake +76 -0
- data/tasks/yard.rake +51 -0
- metadata +169 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Many, "eat" do
|
4
|
+
|
5
|
+
let(:term){ Terminal.new(/^[a-z]+$/) }
|
6
|
+
let(:rule){ Many.new term, min, max }
|
7
|
+
|
8
|
+
context "when set for *" do
|
9
|
+
let(:min){ 0 }
|
10
|
+
let(:max){ nil }
|
11
|
+
|
12
|
+
it 'returns the subarray when zero match' do
|
13
|
+
rule.eat([nil, "world", "then"]).should eq([nil, "world", "then"])
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns the subarray when one match' do
|
17
|
+
rule.eat(["world", nil, "then"]).should eq([nil, "then"])
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'returns the subarray when multiple matches' do
|
21
|
+
rule.eat(["world", "then"]).should eq([])
|
22
|
+
end
|
23
|
+
|
24
|
+
end # *
|
25
|
+
|
26
|
+
context "when set for +" do
|
27
|
+
let(:min){ 1 }
|
28
|
+
let(:max){ nil }
|
29
|
+
|
30
|
+
it 'returns nil zero match' do
|
31
|
+
rule.eat([nil, "world", "then"]).should be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'returns the subarray when one match' do
|
35
|
+
rule.eat(["world", nil, "then"]).should eq([nil, "then"])
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns the subarray when multiple matches' do
|
39
|
+
rule.eat(["world", "then"]).should eq([])
|
40
|
+
end
|
41
|
+
|
42
|
+
end # +
|
43
|
+
|
44
|
+
context "when set for ?" do
|
45
|
+
let(:min){ 0 }
|
46
|
+
let(:max){ 1 }
|
47
|
+
|
48
|
+
it 'returns the subarray when zero match' do
|
49
|
+
rule.eat([nil, "world", "then"]).should eq([nil, "world", "then"])
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns the subarray when one match' do
|
53
|
+
rule.eat(["world", "then"]).should eq(["then"])
|
54
|
+
end
|
55
|
+
|
56
|
+
end # ?
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Many, "initialize" do
|
4
|
+
|
5
|
+
it 'understands a single min' do
|
6
|
+
many = Many.new(nil, 2)
|
7
|
+
many.min.should eq(2)
|
8
|
+
many.max.should be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'understands a min and a max' do
|
12
|
+
many = Many.new(nil, 2, 10)
|
13
|
+
many.min.should eq(2)
|
14
|
+
many.max.should eq(10)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'understands ?' do
|
18
|
+
many = Many.new(nil, '?')
|
19
|
+
many.min.should eq(0)
|
20
|
+
many.max.should eq(1)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'understands *' do
|
24
|
+
many = Many.new(nil, '*')
|
25
|
+
many.min.should eq(0)
|
26
|
+
many.max.should be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'understands +' do
|
30
|
+
many = Many.new(nil, '+')
|
31
|
+
many.min.should eq(1)
|
32
|
+
many.max.should be_nil
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Many do
|
4
|
+
|
5
|
+
let(:term){ Terminal.new(/^[a-z]+$/) }
|
6
|
+
let(:rule){ Many.new term, '+' }
|
7
|
+
|
8
|
+
it 'returns true on match' do
|
9
|
+
rule.should be_match(["hello"])
|
10
|
+
rule.should be_match(["hello", "world"])
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns false on partial match' do
|
14
|
+
rule.should_not be_match(["hello", "world", "12"])
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns false on no match' do
|
18
|
+
rule.should_not be_match(["12"])
|
19
|
+
rule.should_not be_match([])
|
20
|
+
rule.should_not be_match(nil)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Reference, "eat" do
|
4
|
+
|
5
|
+
let(:grammar){ {:hello => Terminal.new(/^[a-z]+$/)} }
|
6
|
+
let(:rule) { Reference.new :hello, grammar }
|
7
|
+
|
8
|
+
it 'delegates the call' do
|
9
|
+
rule.eat(["hello", "world"]).should eq(["world"])
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Reference, "match?" do
|
4
|
+
|
5
|
+
let(:grammar){ {:hello => Terminal.new(/^[a-z]+$/)} }
|
6
|
+
let(:rule) { Reference.new :hello, grammar }
|
7
|
+
|
8
|
+
it 'returns true on match' do
|
9
|
+
rule.should be_match("hello")
|
10
|
+
(rule === "hello").should be_true
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns false on no match' do
|
14
|
+
rule.should_not be_match("12")
|
15
|
+
rule.should_not be_match(nil)
|
16
|
+
rule.should_not be_match([])
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Rule, "eat" do
|
4
|
+
|
5
|
+
let(:defn){ Sequence.new [Terminal.new(/^[a-z]+$/)] }
|
6
|
+
let(:rule){ Rule.new :hello, defn }
|
7
|
+
|
8
|
+
it 'returns the trailing array when match' do
|
9
|
+
rule.eat([[:hello, "world"], "!"]).should eq(["!"])
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'returns nil when not match' do
|
13
|
+
rule.eat([:hello, "world"]).should be_nil
|
14
|
+
rule.eat([:hello]).should be_nil
|
15
|
+
rule.eat([]).should be_nil
|
16
|
+
rule.eat([[]]).should be_nil
|
17
|
+
rule.eat([nil]).should be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Rule, 'match?' do
|
4
|
+
|
5
|
+
let(:defn){ Sequence.new [Terminal.new(/^[a-z]+$/)] }
|
6
|
+
let(:rule){ Rule.new :hello, defn }
|
7
|
+
|
8
|
+
it 'returns true on match' do
|
9
|
+
rule.should be_match([:hello, "hello"])
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'returns false on partial match' do
|
13
|
+
rule.should_not be_match([:hello, "hello", "world"])
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns false on no match' do
|
17
|
+
rule.should_not be_match(["hello"])
|
18
|
+
rule.should_not be_match([:hello, 12])
|
19
|
+
rule.should_not be_match([])
|
20
|
+
rule.should_not be_match(nil)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Sequence, "eat" do
|
4
|
+
|
5
|
+
let(:alt1){ Terminal.new(nil) }
|
6
|
+
let(:alt2){ Terminal.new(/^[a-z]+$/) }
|
7
|
+
let(:rule){ Sequence.new [alt1, alt2] }
|
8
|
+
|
9
|
+
it 'returns the subarray when match' do
|
10
|
+
rule.eat([nil, "world", "then"]).should eq(["then"])
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns nil when no match' do
|
14
|
+
rule.eat([]).should be_nil
|
15
|
+
rule.eat(["12"]).should be_nil
|
16
|
+
rule.eat([nil]).should be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Sequence, 'match?' do
|
4
|
+
|
5
|
+
let(:alt1){ Terminal.new(nil) }
|
6
|
+
let(:alt2){ Terminal.new(/^[a-z]+$/) }
|
7
|
+
let(:rule){ Sequence.new [alt1, alt2] }
|
8
|
+
|
9
|
+
it 'returns true on match' do
|
10
|
+
rule.should be_match([nil, "hello"])
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns false on partial match' do
|
14
|
+
rule.should_not be_match([nil, "hello", "world"])
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns false on no match' do
|
18
|
+
rule.should_not be_match([:hello, 12])
|
19
|
+
rule.should_not be_match([])
|
20
|
+
rule.should_not be_match(nil)
|
21
|
+
rule.should_not be_match([nil])
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Terminal, "eat" do
|
4
|
+
|
5
|
+
let(:rule){ Terminal.new(/^[a-z]+$/) }
|
6
|
+
|
7
|
+
context "with a regexp" do
|
8
|
+
|
9
|
+
it 'returns subarray when match' do
|
10
|
+
rule.eat(["hello", "world"]).should eq(["world"])
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns nil when no match' do
|
14
|
+
rule.eat([]).should be_nil
|
15
|
+
rule.eat(["12"]).should be_nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Terminal, "match?" do
|
4
|
+
|
5
|
+
let(:rule){ Terminal.new(arg) }
|
6
|
+
|
7
|
+
describe "with true" do
|
8
|
+
let(:arg){ true }
|
9
|
+
|
10
|
+
it 'returns true on match' do
|
11
|
+
rule.should be_match(true)
|
12
|
+
(rule === true).should be_true
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'returns false on no match' do
|
16
|
+
rule.should_not be_match([])
|
17
|
+
rule.should_not be_match(nil)
|
18
|
+
rule.should_not be_match(false)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "with false" do
|
24
|
+
let(:arg){ false }
|
25
|
+
|
26
|
+
it 'returns true on match' do
|
27
|
+
rule.should be_match(false)
|
28
|
+
(rule === false).should be_true
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns false on no match' do
|
32
|
+
rule.should_not be_match([])
|
33
|
+
rule.should_not be_match(nil)
|
34
|
+
rule.should_not be_match(true)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "with nil" do
|
40
|
+
let(:arg){ nil }
|
41
|
+
|
42
|
+
it 'returns true on match' do
|
43
|
+
rule.should be_match(nil)
|
44
|
+
(rule === nil).should be_true
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns false on no match' do
|
48
|
+
rule.should_not be_match([])
|
49
|
+
rule.should_not be_match(false)
|
50
|
+
rule.should_not be_match(true)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe Terminal, "terminal_match?" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
class Terminal; public :terminal_match?; end
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:terminal){ Terminal.new(arg) }
|
10
|
+
|
11
|
+
context "with a Regexp" do
|
12
|
+
let(:arg){ /^[a-z]+$/ }
|
13
|
+
|
14
|
+
it 'matches a matching string' do
|
15
|
+
terminal.terminal_match?("hello").should be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'matches a non matching string' do
|
19
|
+
terminal.terminal_match?("12").should be_false
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'does not match a sexp' do
|
23
|
+
terminal.terminal_match?([:sexp, "Hello World"]).should be_false
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'does not match nil' do
|
27
|
+
terminal.terminal_match?(nil).should be_false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "with true" do
|
32
|
+
let(:arg){ true }
|
33
|
+
|
34
|
+
it 'matches true' do
|
35
|
+
terminal.terminal_match?(true).should be_true
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'does not match false/nil' do
|
39
|
+
terminal.terminal_match?(false).should be_false
|
40
|
+
terminal.terminal_match?(nil).should be_false
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'does not match anything else' do
|
44
|
+
terminal.terminal_match?([]).should be_false
|
45
|
+
terminal.terminal_match?([:sexp]).should be_false
|
46
|
+
terminal.terminal_match?("true").should be_false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with false" do
|
51
|
+
let(:arg){ false }
|
52
|
+
|
53
|
+
it 'matches false' do
|
54
|
+
terminal.terminal_match?(false).should be_true
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'does not match true/nil' do
|
58
|
+
terminal.terminal_match?(true).should be_false
|
59
|
+
terminal.terminal_match?(nil).should be_false
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'does not match anything else' do
|
63
|
+
terminal.terminal_match?([]).should be_false
|
64
|
+
terminal.terminal_match?([:sexp]).should be_false
|
65
|
+
terminal.terminal_match?("false").should be_false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "with nil" do
|
70
|
+
let(:arg){ nil }
|
71
|
+
|
72
|
+
it 'matches nil' do
|
73
|
+
terminal.terminal_match?(nil).should be_true
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'does not match true/false' do
|
77
|
+
terminal.terminal_match?(true).should be_false
|
78
|
+
terminal.terminal_match?(false).should be_false
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'does not match anything else' do
|
82
|
+
terminal.terminal_match?([]).should be_false
|
83
|
+
terminal.terminal_match?([:sexp]).should be_false
|
84
|
+
terminal.terminal_match?("nil").should be_false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SexpGrammar
|
3
|
+
describe "the bool_expr grammar" do
|
4
|
+
|
5
|
+
let(:g){ SexpGrammar.load(Path.dir/"bool_expr.yml") }
|
6
|
+
|
7
|
+
it "allows checking validy of specific nodes" do
|
8
|
+
(g[:bool_lit] === true).should be_true
|
9
|
+
(g[:var_ref] === [:var_ref, "x"]).should be_true
|
10
|
+
(g[:bool_and] === [:bool_and, true, false]).should be_true
|
11
|
+
(g[:bool_and] === [:bool_or, true, false]).should be_false
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'allows checking the validity against the root rule' do
|
15
|
+
(g === true).should be_true
|
16
|
+
(g === false).should be_true
|
17
|
+
(g === [:bool_not, true]).should be_true
|
18
|
+
(g === [:var_ref, "x"]).should be_true
|
19
|
+
(g === [:bool_not, [:var_ref, "x"]]).should be_true
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'detects wrong matches' do
|
23
|
+
(g === [:bool_not, [:something_else, "x"]]).should be_false
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|