predicate 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/LICENSE.md +22 -0
- data/Rakefile +11 -0
- data/lib/predicate/factory.rb +107 -0
- data/lib/predicate/grammar.rb +33 -0
- data/lib/predicate/grammar.sexp.yml +58 -0
- data/lib/predicate/nodes/and.rb +23 -0
- data/lib/predicate/nodes/contradiction.rb +34 -0
- data/lib/predicate/nodes/dyadic_comp.rb +26 -0
- data/lib/predicate/nodes/eq.rb +15 -0
- data/lib/predicate/nodes/expr.rb +68 -0
- data/lib/predicate/nodes/gt.rb +10 -0
- data/lib/predicate/nodes/gte.rb +10 -0
- data/lib/predicate/nodes/identifier.rb +18 -0
- data/lib/predicate/nodes/in.rb +26 -0
- data/lib/predicate/nodes/literal.rb +18 -0
- data/lib/predicate/nodes/lt.rb +10 -0
- data/lib/predicate/nodes/lte.rb +10 -0
- data/lib/predicate/nodes/nadic_bool.rb +16 -0
- data/lib/predicate/nodes/native.rb +30 -0
- data/lib/predicate/nodes/neq.rb +10 -0
- data/lib/predicate/nodes/not.rb +22 -0
- data/lib/predicate/nodes/or.rb +10 -0
- data/lib/predicate/nodes/qualified_identifier.rb +22 -0
- data/lib/predicate/nodes/tautology.rb +30 -0
- data/lib/predicate/processors/qualifier.rb +23 -0
- data/lib/predicate/processors/renamer.rb +25 -0
- data/lib/predicate/processors/to_ruby_code.rb +71 -0
- data/lib/predicate/processors.rb +3 -0
- data/lib/predicate/version.rb +8 -0
- data/lib/predicate.rb +113 -0
- data/spec/expr/test_to_proc.rb +16 -0
- data/spec/expr/test_to_ruby_code.rb +152 -0
- data/spec/factory/shared/a_comparison_factory_method.rb +35 -0
- data/spec/factory/shared/a_predicate_ast_node.rb +20 -0
- data/spec/factory/test_and.rb +13 -0
- data/spec/factory/test_between.rb +12 -0
- data/spec/factory/test_comp.rb +35 -0
- data/spec/factory/test_contradiction.rb +11 -0
- data/spec/factory/test_eq.rb +9 -0
- data/spec/factory/test_factor_predicate.rb +50 -0
- data/spec/factory/test_gt.rb +9 -0
- data/spec/factory/test_gte.rb +9 -0
- data/spec/factory/test_identifier.rb +13 -0
- data/spec/factory/test_in.rb +12 -0
- data/spec/factory/test_literal.rb +13 -0
- data/spec/factory/test_lt.rb +9 -0
- data/spec/factory/test_lte.rb +9 -0
- data/spec/factory/test_native.rb +19 -0
- data/spec/factory/test_neq.rb +9 -0
- data/spec/factory/test_not.rb +13 -0
- data/spec/factory/test_or.rb +13 -0
- data/spec/factory/test_qualified_identifier.rb +15 -0
- data/spec/factory/test_tautology.rb +12 -0
- data/spec/grammar/test_match.rb +93 -0
- data/spec/grammar/test_sexpr.rb +103 -0
- data/spec/nodes/and/test_and_split.rb +66 -0
- data/spec/nodes/dyadic_comp/test_and_split.rb +45 -0
- data/spec/nodes/identifier/test_and_split.rb +23 -0
- data/spec/nodes/identifier/test_free_variables.rb +12 -0
- data/spec/nodes/identifier/test_name.rb +12 -0
- data/spec/nodes/nadic_bool/test_free_variables.rb +14 -0
- data/spec/nodes/qualified_identifier/test_and_split.rb +23 -0
- data/spec/nodes/qualified_identifier/test_free_variables.rb +12 -0
- data/spec/nodes/qualified_identifier/test_name.rb +12 -0
- data/spec/nodes/qualified_identifier/test_qualifier.rb +12 -0
- data/spec/predicate/test_and_split.rb +57 -0
- data/spec/predicate/test_bool_and.rb +34 -0
- data/spec/predicate/test_bool_not.rb +68 -0
- data/spec/predicate/test_bool_or.rb +34 -0
- data/spec/predicate/test_coerce.rb +75 -0
- data/spec/predicate/test_constant_variables.rb +52 -0
- data/spec/predicate/test_contradiction.rb +24 -0
- data/spec/predicate/test_evaluate.rb +66 -0
- data/spec/predicate/test_factory_methods.rb +77 -0
- data/spec/predicate/test_free_variables.rb +14 -0
- data/spec/predicate/test_hash_and_equal.rb +26 -0
- data/spec/predicate/test_qualify.rb +34 -0
- data/spec/predicate/test_rename.rb +76 -0
- data/spec/predicate/test_tautology.rb +24 -0
- data/spec/predicate/test_to_proc.rb +14 -0
- data/spec/predicate/test_to_ruby_code.rb +20 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/test_predicate.rb +127 -0
- data/tasks/test.rake +11 -0
- metadata +186 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Predicate, "|" do
|
4
|
+
|
5
|
+
let(:left) { Predicate.coerce(x: 2) }
|
6
|
+
|
7
|
+
subject{ left | right }
|
8
|
+
|
9
|
+
before do
|
10
|
+
subject.should be_a(Predicate)
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'with itself' do
|
14
|
+
let(:right){ left }
|
15
|
+
|
16
|
+
it{ should be(left) }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with the same expression' do
|
20
|
+
let(:right){ Predicate.coerce(x: 2) }
|
21
|
+
|
22
|
+
it{ should be(left) }
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with another predicate' do
|
26
|
+
let(:right){ Predicate.coerce(y: 3) }
|
27
|
+
|
28
|
+
it 'should be a expected' do
|
29
|
+
subject.to_ruby_code.should eq("->(t){ (t[:x] == 2) || (t[:y] == 3) }")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Predicate, ".coerce" do
|
4
|
+
|
5
|
+
subject{ Predicate.coerce(arg) }
|
6
|
+
|
7
|
+
describe "from Predicate" do
|
8
|
+
let(:arg){ Predicate.new(Factory.tautology) }
|
9
|
+
|
10
|
+
it{ should be(arg) }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "from true" do
|
14
|
+
let(:arg){ true }
|
15
|
+
|
16
|
+
specify{
|
17
|
+
subject.expr.should be_a(Tautology)
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "from false" do
|
22
|
+
let(:arg){ false }
|
23
|
+
|
24
|
+
specify{
|
25
|
+
subject.expr.should be_a(Contradiction)
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "from Symbol" do
|
30
|
+
let(:arg){ :status }
|
31
|
+
|
32
|
+
specify{
|
33
|
+
subject.expr.should be_a(Identifier)
|
34
|
+
subject.expr.name.should eq(arg)
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "from Proc" do
|
39
|
+
let(:arg){ lambda{ status == 10 } }
|
40
|
+
|
41
|
+
specify{
|
42
|
+
subject.expr.should be_a(Native)
|
43
|
+
subject.to_proc.should be(arg)
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "from String" do
|
48
|
+
let(:arg){ "status == 10" }
|
49
|
+
|
50
|
+
it 'raises an error' do
|
51
|
+
lambda{
|
52
|
+
subject
|
53
|
+
}.should raise_error(ArgumentError)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "from Hash (single)" do
|
58
|
+
let(:arg){ {status: 10} }
|
59
|
+
|
60
|
+
specify{
|
61
|
+
subject.expr.should be_a(Eq)
|
62
|
+
subject.expr.should eq([:eq, [:identifier, :status], [:literal, 10]])
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "from Hash (multiple)" do
|
67
|
+
let(:arg){ {status: 10, name: "Jones"} }
|
68
|
+
|
69
|
+
specify{
|
70
|
+
subject.should eq(Predicate.eq(status: 10) & Predicate.eq(name: "Jones"))
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Predicate, "constant_variables" do
|
4
|
+
|
5
|
+
subject{ p.constant_variables }
|
6
|
+
|
7
|
+
describe "on a comp(:eq)" do
|
8
|
+
let(:p){ Predicate.coerce(x: 2, y: 3) }
|
9
|
+
|
10
|
+
it{ expect(subject).to eql([:x, :y]) }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "on a in with one value" do
|
14
|
+
let(:p){ Predicate.in(:x, [2]) }
|
15
|
+
|
16
|
+
it{ expect(subject).to eql([:x]) }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "on a in with mutiple values" do
|
20
|
+
let(:p){ Predicate.in(:x, [2, 3]) }
|
21
|
+
|
22
|
+
it{ expect(subject).to eql([]) }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "on a NOT" do
|
26
|
+
let(:p){ !Predicate.coerce(x: 2) }
|
27
|
+
|
28
|
+
it{ expect(subject).to eql([]) }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "on a AND" do
|
32
|
+
let(:p){ Predicate.coerce(x: 2) & Predicate.coerce(y: 3) }
|
33
|
+
|
34
|
+
it{ expect(subject).to eql([:x, :y]) }
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "on a OR" do
|
38
|
+
let(:p){ Predicate.coerce(x: 2) | Predicate.coerce(y: 3) }
|
39
|
+
|
40
|
+
it{ expect(subject).to eql([]) }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "on a negated OR" do
|
44
|
+
let(:p){ !(Predicate.coerce(x: 2) | Predicate.coerce(y: 3)) }
|
45
|
+
|
46
|
+
pending("NNF would make constant_variables smarter"){
|
47
|
+
expect(subject).to eql([:x, :y])
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Predicate, "contradiction?" do
|
4
|
+
|
5
|
+
context "tautology" do
|
6
|
+
subject{ Predicate.tautology }
|
7
|
+
|
8
|
+
it{ expect(subject.contradiction?).to be(false) }
|
9
|
+
end
|
10
|
+
|
11
|
+
context "contradiction" do
|
12
|
+
subject{ Predicate.contradiction }
|
13
|
+
|
14
|
+
it{ expect(subject.contradiction?).to be(true) }
|
15
|
+
end
|
16
|
+
|
17
|
+
context "identifier" do
|
18
|
+
subject{ Predicate.identifier(:x) }
|
19
|
+
|
20
|
+
it{ expect(subject.contradiction?).to be(false) }
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Predicate, "evaluate" do
|
4
|
+
|
5
|
+
context 'on a native predicate of arity 1, used through tuple#[]' do
|
6
|
+
let(:predicate){
|
7
|
+
Predicate.native(->(t){ t[:name] =~ /foo/ })
|
8
|
+
}
|
9
|
+
|
10
|
+
context 'on a matching tuple' do
|
11
|
+
let(:scope){ { :name => "foo" } }
|
12
|
+
|
13
|
+
it{ expect(predicate.evaluate(scope)).to be_truthy }
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'on a non-matching tuple' do
|
17
|
+
let(:scope){ { :name => "bar" } }
|
18
|
+
|
19
|
+
it{ expect(predicate.evaluate(scope)).to be_falsy }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'on a native predicate of arity 1, used through tuple[:xxx]' do
|
24
|
+
let(:predicate){
|
25
|
+
Predicate.native(->(t){ t[:name] =~ /foo/ })
|
26
|
+
}
|
27
|
+
|
28
|
+
context 'on a matching tuple' do
|
29
|
+
let(:scope){ { :name => "foo" } }
|
30
|
+
|
31
|
+
it{ expect(predicate.evaluate(scope)).to be_truthy }
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'on a non-matching tuple' do
|
35
|
+
let(:scope){ { :name => "bar" } }
|
36
|
+
|
37
|
+
it{ expect(predicate.evaluate(scope)).to be_falsy }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'on a factored predicate' do
|
42
|
+
let(:predicate){
|
43
|
+
Predicate.new(Factory.lte(:x => 2))
|
44
|
+
}
|
45
|
+
|
46
|
+
describe "on x == 2" do
|
47
|
+
let(:scope){ { :x => 2 } }
|
48
|
+
|
49
|
+
it{ expect(predicate.evaluate(scope)).to be_truthy }
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "on x == 1" do
|
53
|
+
let(:scope){ { :x => 1 } }
|
54
|
+
|
55
|
+
it{ expect(predicate.evaluate(scope)).to be_truthy }
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "on x == 3" do
|
59
|
+
let(:scope){ { :x => 3 } }
|
60
|
+
|
61
|
+
it{ expect(predicate.evaluate(scope)).to be_falsy }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Predicate, "factory methods" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
subject.should be_a(Predicate)
|
7
|
+
subject.expr.should be_a(Expr)
|
8
|
+
end
|
9
|
+
|
10
|
+
context "tautology" do
|
11
|
+
subject{ Predicate.tautology }
|
12
|
+
|
13
|
+
specify{ subject.to_ruby_code.should eq("->(t){ true }") }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "contradiction" do
|
17
|
+
subject{ Predicate.contradiction }
|
18
|
+
|
19
|
+
specify{ subject.to_ruby_code.should eq("->(t){ false }") }
|
20
|
+
end
|
21
|
+
|
22
|
+
context "identifier" do
|
23
|
+
subject{ Predicate.identifier(:x) }
|
24
|
+
|
25
|
+
specify{ subject.to_ruby_code.should eq("->(t){ t[:x] }") }
|
26
|
+
end
|
27
|
+
|
28
|
+
context "eq" do
|
29
|
+
subject{ Predicate.eq(:x, 2) }
|
30
|
+
|
31
|
+
specify{ subject.to_ruby_code.should eq("->(t){ t[:x] == 2 }") }
|
32
|
+
end
|
33
|
+
|
34
|
+
context "neq" do
|
35
|
+
subject{ Predicate.neq(:x, 2) }
|
36
|
+
|
37
|
+
specify{ subject.to_ruby_code.should eq("->(t){ t[:x] != 2 }") }
|
38
|
+
end
|
39
|
+
|
40
|
+
context "lt" do
|
41
|
+
subject{ Predicate.lt(:x, 2) }
|
42
|
+
|
43
|
+
specify{ subject.to_ruby_code.should eq("->(t){ t[:x] < 2 }") }
|
44
|
+
end
|
45
|
+
|
46
|
+
context "lte" do
|
47
|
+
subject{ Predicate.lte(:x, 2) }
|
48
|
+
|
49
|
+
specify{ subject.to_ruby_code.should eq("->(t){ t[:x] <= 2 }") }
|
50
|
+
end
|
51
|
+
|
52
|
+
context "gt" do
|
53
|
+
subject{ Predicate.gt(:x, 2) }
|
54
|
+
|
55
|
+
specify{ subject.to_ruby_code.should eq("->(t){ t[:x] > 2 }") }
|
56
|
+
end
|
57
|
+
|
58
|
+
context "gte" do
|
59
|
+
subject{ Predicate.gte(:x, 2) }
|
60
|
+
|
61
|
+
specify{ subject.to_ruby_code.should eq("->(t){ t[:x] >= 2 }") }
|
62
|
+
end
|
63
|
+
|
64
|
+
context "literal" do
|
65
|
+
subject{ Predicate.literal(2) }
|
66
|
+
|
67
|
+
specify{ subject.to_ruby_code.should eq("->(t){ 2 }") }
|
68
|
+
end
|
69
|
+
|
70
|
+
context "native" do
|
71
|
+
subject{ Predicate.native(lambda{}) }
|
72
|
+
|
73
|
+
specify{ subject.expr.should be_a(Native) }
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Predicate, "hash and ==" do
|
4
|
+
|
5
|
+
subject{ left == right }
|
6
|
+
|
7
|
+
after do
|
8
|
+
left.hash.should eq(right.hash) if subject
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "on equal predicates" do
|
12
|
+
let(:left) { Predicate.coerce(:x => 2) }
|
13
|
+
let(:right){ Predicate.coerce(:x => 2) }
|
14
|
+
|
15
|
+
it{ should be(true) }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "on non equal predicates" do
|
19
|
+
let(:left) { Predicate.coerce(:x => 2) }
|
20
|
+
let(:right){ Predicate.coerce(:x => 3) }
|
21
|
+
|
22
|
+
it{ should be(false) }
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Predicate, "qualify" do
|
4
|
+
|
5
|
+
let(:p){ Predicate }
|
6
|
+
|
7
|
+
subject{ predicate.qualify(qualifier) }
|
8
|
+
|
9
|
+
let(:qualifier) { {:x => :t} }
|
10
|
+
|
11
|
+
context 'on a full AST predicate' do
|
12
|
+
let(:predicate){ p.in(:x, [2]) & p.eq(:y, 3) }
|
13
|
+
|
14
|
+
it{ should eq(p.in(Factory.qualified_identifier(:t, :x), [2]) & p.eq(:y, 3)) }
|
15
|
+
|
16
|
+
specify "it should tag expressions correctly" do
|
17
|
+
subject.expr.should be_a(Sexpr)
|
18
|
+
subject.expr.should be_a(Expr)
|
19
|
+
subject.expr.should be_a(And)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'on a predicate that contains natives' do
|
24
|
+
let(:predicate){ p.in(:x, [2]) & p.native(lambda{}) }
|
25
|
+
|
26
|
+
it 'raises an error' do
|
27
|
+
lambda{
|
28
|
+
subject
|
29
|
+
}.should raise_error(NotSupportedError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Predicate, "rename" do
|
4
|
+
|
5
|
+
let(:p){ Predicate }
|
6
|
+
|
7
|
+
subject{ predicate.rename(renaming) }
|
8
|
+
|
9
|
+
context 'on a pure renaming hash' do
|
10
|
+
let(:renaming) { {:x => :z} }
|
11
|
+
|
12
|
+
context 'on a full AST predicate' do
|
13
|
+
let(:predicate){ p.in(:x, [2]) & p.eq(:y, 3) }
|
14
|
+
|
15
|
+
it{ should eq(p.in(:z, [2]) & p.eq(:y, 3)) }
|
16
|
+
|
17
|
+
specify "it should tag expressions correctly" do
|
18
|
+
subject.expr.should be_a(Sexpr)
|
19
|
+
subject.expr.should be_a(Expr)
|
20
|
+
subject.expr.should be_a(And)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'on a predicate that contains natives' do
|
25
|
+
let(:predicate){ p.in(:x, [2]) & p.native(lambda{}) }
|
26
|
+
|
27
|
+
it 'raises an error' do
|
28
|
+
lambda{
|
29
|
+
subject
|
30
|
+
}.should raise_error(NotSupportedError)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'on a predicate that contains qualified identifiers' do
|
35
|
+
let(:predicate){ p.eq(Factory.qualified_identifier(:t, :x), 3) }
|
36
|
+
|
37
|
+
it 'renames correctly' do
|
38
|
+
subject.should eq(p.eq(Factory.qualified_identifier(:t, :z), 3))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when the renamer is a Proc returning identifiers' do
|
44
|
+
let(:renaming){ ->(a){ Grammar.sexpr([:identifier, :y]) } }
|
45
|
+
|
46
|
+
context "on an identifier" do
|
47
|
+
let(:predicate){ p.eq(:x, 2) }
|
48
|
+
|
49
|
+
it{ should eq(p.eq(:y, 2)) }
|
50
|
+
end
|
51
|
+
|
52
|
+
context "on a qualifier identifier" do
|
53
|
+
let(:predicate){ p.eq(Factory.qualified_identifier(:t, :x), 2) }
|
54
|
+
|
55
|
+
it{ should eq(p.eq(:y, 2)) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when the renamer is a Proc returning qualified identifiers' do
|
60
|
+
let(:renaming){ ->(a){ Grammar.sexpr([:qualified_identifier, :t, :y]) } }
|
61
|
+
|
62
|
+
context 'on an identifier' do
|
63
|
+
let(:predicate){ p.eq(:x, 2) }
|
64
|
+
|
65
|
+
it{ should eq(p.eq(Factory.qualified_identifier(:t, :y), 2)) }
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'on a qualified' do
|
69
|
+
let(:predicate){ p.eq(Factory.qualified_identifier(:t, :x), 2) }
|
70
|
+
|
71
|
+
it{ should eq(p.eq(Factory.qualified_identifier(:t, :y), 2)) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Predicate, "tautology?" do
|
4
|
+
|
5
|
+
context "tautology" do
|
6
|
+
subject{ Predicate.tautology }
|
7
|
+
|
8
|
+
it{ expect(subject.tautology?).to be(true) }
|
9
|
+
end
|
10
|
+
|
11
|
+
context "contradiction" do
|
12
|
+
subject{ Predicate.contradiction }
|
13
|
+
|
14
|
+
it{ expect(subject.tautology?).to be(false) }
|
15
|
+
end
|
16
|
+
|
17
|
+
context "identifier" do
|
18
|
+
subject{ Predicate.identifier(:x) }
|
19
|
+
|
20
|
+
it{ expect(subject.tautology?).to be(false) }
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Predicate, "to_ruby_code" do
|
4
|
+
|
5
|
+
subject{ p.to_ruby_code }
|
6
|
+
|
7
|
+
describe "on a comp(:eq)" do
|
8
|
+
let(:p){ Predicate.coerce(:x => 2) }
|
9
|
+
|
10
|
+
it{ should eq("->(t){ t[:x] == 2 }") }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "with qualified identifiers" do
|
14
|
+
let(:p){ Predicate.eq(Factory.qualified_identifier(:t, :y), 2) }
|
15
|
+
|
16
|
+
it{ should eq("->(t){ t[:y] == 2 }") }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for "a predicate" do
|
4
|
+
|
5
|
+
let(:x){ 12 }
|
6
|
+
let(:y){ 13 }
|
7
|
+
|
8
|
+
it 'provides a proc for easy evaluation' do
|
9
|
+
got = subject.to_proc.call(x: 12, y: 13)
|
10
|
+
[ TrueClass, FalseClass ].should include(got.class)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'can be negated easily' do
|
14
|
+
(!subject).should be_a(Predicate)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'detects stupid AND' do
|
18
|
+
(subject & Predicate.tautology).should be(subject)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'detects stupid OR' do
|
22
|
+
(subject | Predicate.contradiction).should be(subject)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'has free variables' do
|
26
|
+
(fv = subject.free_variables).should be_a(Array)
|
27
|
+
(fv - [ :x, :y ]).should be_empty
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'always splits around and trivially when no free variables are touched' do
|
31
|
+
top, down = subject.and_split([:z])
|
32
|
+
top.should be_tautology
|
33
|
+
down.should eq(subject)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'Predicate.tautology' do
|
39
|
+
subject{ Predicate.tautology }
|
40
|
+
|
41
|
+
it_should_behave_like "a predicate"
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'Predicate.contradiction' do
|
45
|
+
subject{ Predicate.contradiction }
|
46
|
+
|
47
|
+
it_should_behave_like "a predicate"
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "Predicate.comp" do
|
51
|
+
subject{ Predicate.comp(:lt, {:x => 2}) }
|
52
|
+
|
53
|
+
it_should_behave_like "a predicate"
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "Predicate.in" do
|
57
|
+
subject{ Predicate.in(:x, [2, 3]) }
|
58
|
+
|
59
|
+
it_should_behave_like "a predicate"
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "Predicate.among" do
|
63
|
+
subject{ Predicate.among(:x, [2, 3]) }
|
64
|
+
|
65
|
+
it_should_behave_like "a predicate"
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "Predicate.eq" do
|
69
|
+
subject{ Predicate.eq(:x, 2) }
|
70
|
+
|
71
|
+
it_should_behave_like "a predicate"
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "Predicate.neq" do
|
75
|
+
subject{ Predicate.neq(:x, 2) }
|
76
|
+
|
77
|
+
it_should_behave_like "a predicate"
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "Predicate.gt" do
|
81
|
+
subject{ Predicate.gt(:x, 2) }
|
82
|
+
|
83
|
+
it_should_behave_like "a predicate"
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "Predicate.gte" do
|
87
|
+
subject{ Predicate.gte(:x, 2) }
|
88
|
+
|
89
|
+
it_should_behave_like "a predicate"
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "Predicate.lt" do
|
93
|
+
subject{ Predicate.lt(:x, 2) }
|
94
|
+
|
95
|
+
it_should_behave_like "a predicate"
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "Predicate.lte" do
|
99
|
+
subject{ Predicate.lte(:x, 2) }
|
100
|
+
|
101
|
+
it_should_behave_like "a predicate"
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "Predicate.between" do
|
105
|
+
subject{ Predicate.between(:x, 2, 3) }
|
106
|
+
|
107
|
+
it_should_behave_like "a predicate"
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "Predicate.and" do
|
111
|
+
subject{ Predicate.and(Predicate.eq(:x, 12), Predicate.eq(:y, 12)) }
|
112
|
+
|
113
|
+
it_should_behave_like "a predicate"
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "Predicate.or" do
|
117
|
+
subject{ Predicate.or(Predicate.eq(:x, 12), Predicate.eq(:y, 12)) }
|
118
|
+
|
119
|
+
it_should_behave_like "a predicate"
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "Predicate.not" do
|
123
|
+
subject{ Predicate.not(Predicate.in(:x, [12])) }
|
124
|
+
|
125
|
+
it_should_behave_like "a predicate"
|
126
|
+
end
|
127
|
+
|
data/tasks/test.rake
ADDED