sexpr 0.2.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 +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
|