sexp_grammar 0.1.0
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.
- 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
|