sexpr 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +13 -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/sexpr.rb +31 -0
- data/lib/sexpr/alternative.rb +28 -0
- data/lib/sexpr/element.rb +9 -0
- data/lib/sexpr/grammar.rb +82 -0
- data/lib/sexpr/loader.rb +1 -0
- data/lib/sexpr/many.rb +52 -0
- data/lib/sexpr/reference.rb +30 -0
- data/lib/sexpr/rule.rb +29 -0
- data/lib/sexpr/sequence.rb +28 -0
- data/lib/sexpr/terminal.rb +35 -0
- data/lib/sexpr/version.rb +14 -0
- data/sexpr.gemspec +188 -0
- data/sexpr.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 +18 -0
- data/spec/grammar/test_parse.rb +32 -0
- data/spec/grammar/test_root.rb +20 -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_sexpr.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 +173 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module Sexpr
|
3
|
+
describe Grammar, "fetch" do
|
4
|
+
|
5
|
+
let(:grammar){
|
6
|
+
Sexpr.load(:terminal => /[a-z]+/)
|
7
|
+
}
|
8
|
+
|
9
|
+
it 'returns the rule when it exists' do
|
10
|
+
grammar[:terminal].should be_a(Terminal)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns nil otherwise' do
|
14
|
+
grammar[:nosuchone].should be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end # module Sexpr
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module Sexpr
|
3
|
+
describe Grammar, "parse" do
|
4
|
+
|
5
|
+
def parser
|
6
|
+
Object.new.tap{|x|
|
7
|
+
def x.parse(s)
|
8
|
+
Struct.new(:value).new([:parsed, s])
|
9
|
+
end
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def grammar(options = {})
|
14
|
+
Sexpr.load({}, {:parser => parser})
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'it accepts a string' do
|
18
|
+
grammar.parse("Hello world").should eq([:parsed, "Hello world"])
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'it accepts a path' do
|
22
|
+
grammar.parse(Path.here).should eq([:parsed, File.read(__FILE__)])
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'it accepts an IO' do
|
26
|
+
File.open(__FILE__, 'r') do |io|
|
27
|
+
grammar.parse(io).should eq([:parsed, File.read(__FILE__)])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module Sexpr
|
3
|
+
describe Grammar, "root" do
|
4
|
+
|
5
|
+
let(:rules){ {:t => /[a-z]+/, :nt => true} }
|
6
|
+
|
7
|
+
def grammar(options = {})
|
8
|
+
Sexpr.load(rules, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'is the first key by default' do
|
12
|
+
grammar.root.value.should eq(/[a-z]+/)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'is the specified rule when specified' do
|
16
|
+
grammar(:root => :nt).root.value.should eq(true)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module Sexpr
|
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 Sexpr
|
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 Sexpr
|
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 Sexpr
|
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 Sexpr
|
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 Sexpr
|
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 Sexpr
|
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 Sexpr
|
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 Sexpr
|
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 Sexpr
|
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 Sexpr
|
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 Sexpr
|
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
|