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,23 @@
|
|
1
|
+
class Predicate
|
2
|
+
class Qualifier < Sexpr::Rewriter
|
3
|
+
|
4
|
+
grammar Grammar
|
5
|
+
|
6
|
+
def initialize(qualifier)
|
7
|
+
@qualifier = qualifier
|
8
|
+
end
|
9
|
+
attr_reader :qualifier
|
10
|
+
|
11
|
+
def on_identifier(sexpr)
|
12
|
+
return sexpr unless q = qualifier[sexpr.name]
|
13
|
+
[:qualified_identifier, q, sexpr.name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_native(sexpr)
|
17
|
+
raise NotSupportedError
|
18
|
+
end
|
19
|
+
|
20
|
+
alias :on_missing :copy_and_apply
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Predicate
|
2
|
+
class Renamer < Sexpr::Rewriter
|
3
|
+
|
4
|
+
grammar Grammar
|
5
|
+
|
6
|
+
def on_identifier(sexpr)
|
7
|
+
return sexpr unless new_name = options[:renaming][sexpr.name]
|
8
|
+
return new_name if Sexpr===new_name
|
9
|
+
[:identifier, new_name]
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_qualified_identifier(sexpr)
|
13
|
+
return sexpr unless new_name = options[:renaming][sexpr.name]
|
14
|
+
return new_name if Sexpr===new_name
|
15
|
+
[:qualified_identifier, sexpr.qualifier, new_name]
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_native(sexpr)
|
19
|
+
raise NotSupportedError
|
20
|
+
end
|
21
|
+
|
22
|
+
alias :on_missing :copy_and_apply
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class Predicate
|
2
|
+
class ToRubyCode < Sexpr::Processor
|
3
|
+
|
4
|
+
def on_tautology(sexpr)
|
5
|
+
"true"
|
6
|
+
end
|
7
|
+
|
8
|
+
def on_contradiction(sexpr)
|
9
|
+
"false"
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_qualified_identifier(sexpr)
|
13
|
+
"#{sexpr.qualifier}[:#{sexpr.name}]"
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_identifier(sexpr)
|
17
|
+
if s = options[:scope]
|
18
|
+
"#{s}[:#{sexpr.last.to_s}]"
|
19
|
+
else
|
20
|
+
sexpr.last.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_not(sexpr)
|
25
|
+
"#{sexpr.operator_symbol}" << apply(sexpr.last, sexpr)
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_nadic_bool(sexpr)
|
29
|
+
sexpr.sexpr_body.map{|term|
|
30
|
+
apply(term, sexpr)
|
31
|
+
}.join(" #{sexpr.operator_symbol} ")
|
32
|
+
end
|
33
|
+
alias :on_and :on_nadic_bool
|
34
|
+
alias :on_or :on_nadic_bool
|
35
|
+
|
36
|
+
def on_dyadic(sexpr)
|
37
|
+
sexpr.sexpr_body.map{|term|
|
38
|
+
apply(term, sexpr)
|
39
|
+
}.join(" #{sexpr.operator_symbol} ")
|
40
|
+
end
|
41
|
+
alias :on_eq :on_dyadic
|
42
|
+
alias :on_neq :on_dyadic
|
43
|
+
alias :on_lt :on_dyadic
|
44
|
+
alias :on_lte :on_dyadic
|
45
|
+
alias :on_gt :on_dyadic
|
46
|
+
alias :on_gte :on_dyadic
|
47
|
+
|
48
|
+
def on_in(sexpr)
|
49
|
+
"#{to_ruby_literal(sexpr.values)}.include?(#{apply(sexpr.identifier)})"
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_literal(sexpr)
|
53
|
+
to_ruby_literal(sexpr.last)
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def to_ruby_literal(x)
|
59
|
+
x.inspect
|
60
|
+
end
|
61
|
+
|
62
|
+
def apply(sexpr, parent = nil)
|
63
|
+
code = super(sexpr)
|
64
|
+
if parent && (parent.priority >= sexpr.priority)
|
65
|
+
code = "(" << code << ")"
|
66
|
+
end
|
67
|
+
code
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
data/lib/predicate.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'sexpr'
|
2
|
+
require_relative 'predicate/version'
|
3
|
+
require_relative 'predicate/factory'
|
4
|
+
require_relative 'predicate/grammar'
|
5
|
+
require_relative 'predicate/processors'
|
6
|
+
class Predicate
|
7
|
+
|
8
|
+
class NotSupportedError < StandardError; end
|
9
|
+
|
10
|
+
TupleLike = ->(t){ t.is_a?(Hash) }
|
11
|
+
|
12
|
+
def initialize(sexpr)
|
13
|
+
@sexpr = sexpr
|
14
|
+
end
|
15
|
+
attr_reader :sexpr
|
16
|
+
alias :expr :sexpr
|
17
|
+
|
18
|
+
class << self
|
19
|
+
include Factory
|
20
|
+
|
21
|
+
def coerce(arg)
|
22
|
+
case arg
|
23
|
+
when Predicate then arg
|
24
|
+
when TrueClass then tautology
|
25
|
+
when FalseClass then contradiction
|
26
|
+
when Symbol then identifier(arg)
|
27
|
+
when Proc then native(arg)
|
28
|
+
when Hash then eq(arg)
|
29
|
+
else
|
30
|
+
raise ArgumentError, "Unable to coerce `#{arg}` to a predicate"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias :parse :coerce
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def _factor_predicate(arg)
|
38
|
+
Predicate.new Grammar.sexpr(arg)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
def native?
|
44
|
+
Native===expr
|
45
|
+
end
|
46
|
+
|
47
|
+
def tautology?
|
48
|
+
expr.tautology?
|
49
|
+
end
|
50
|
+
|
51
|
+
def contradiction?
|
52
|
+
expr.contradiction?
|
53
|
+
end
|
54
|
+
|
55
|
+
def free_variables
|
56
|
+
expr.free_variables
|
57
|
+
end
|
58
|
+
|
59
|
+
def constant_variables
|
60
|
+
expr.constant_variables
|
61
|
+
end
|
62
|
+
|
63
|
+
def &(other)
|
64
|
+
return self if other.tautology? or other==self
|
65
|
+
return other if tautology?
|
66
|
+
Predicate.new(expr & other.expr)
|
67
|
+
end
|
68
|
+
|
69
|
+
def |(other)
|
70
|
+
return self if other.contradiction? or other==self
|
71
|
+
return other if contradiction?
|
72
|
+
Predicate.new(expr | other.expr)
|
73
|
+
end
|
74
|
+
|
75
|
+
def !
|
76
|
+
Predicate.new(!expr)
|
77
|
+
end
|
78
|
+
|
79
|
+
def qualify(qualifier)
|
80
|
+
Predicate.new(expr.qualify(qualifier))
|
81
|
+
end
|
82
|
+
|
83
|
+
def rename(renaming)
|
84
|
+
Predicate.new(expr.rename(renaming))
|
85
|
+
end
|
86
|
+
|
87
|
+
def evaluate(tuple)
|
88
|
+
to_proc.call(tuple)
|
89
|
+
end
|
90
|
+
|
91
|
+
def and_split(attr_list)
|
92
|
+
expr.and_split(attr_list).map{|e| Predicate.new(e)}
|
93
|
+
end
|
94
|
+
|
95
|
+
def ==(other)
|
96
|
+
other.is_a?(Predicate) && (other.expr==expr)
|
97
|
+
end
|
98
|
+
alias :eql? :==
|
99
|
+
|
100
|
+
def hash
|
101
|
+
expr.hash
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_ruby_code(scope = "t")
|
105
|
+
expr.to_ruby_code(scope)
|
106
|
+
end
|
107
|
+
alias :to_s :to_ruby_code
|
108
|
+
|
109
|
+
def to_proc
|
110
|
+
@proc ||= expr.to_proc("t")
|
111
|
+
end
|
112
|
+
|
113
|
+
end # class Predicate
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Expr, "to_ruby_code" do
|
4
|
+
|
5
|
+
let(:f){ Factory }
|
6
|
+
|
7
|
+
subject{ expr.to_ruby_code }
|
8
|
+
|
9
|
+
after do
|
10
|
+
unless expr.is_a?(Native)
|
11
|
+
lambda{
|
12
|
+
eval(subject)
|
13
|
+
}.should_not raise_error
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'tautology' do
|
18
|
+
let(:expr){ f.tautology }
|
19
|
+
|
20
|
+
it{ should eq("->(t){ true }") }
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'contradiction' do
|
24
|
+
let(:expr){ f.contradiction }
|
25
|
+
|
26
|
+
it{ should eq("->(t){ false }") }
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "identifier" do
|
30
|
+
let(:expr){ f.identifier(:name) }
|
31
|
+
|
32
|
+
it{ should eq("->(t){ t[:name] }") }
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "and" do
|
36
|
+
let(:expr){ f.and(f.contradiction, f.contradiction) }
|
37
|
+
|
38
|
+
it{ should eq("->(t){ false && false }") }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "or" do
|
42
|
+
let(:expr){ f.or(f.contradiction, f.contradiction) }
|
43
|
+
|
44
|
+
it{ should eq("->(t){ false || false }") }
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "not" do
|
48
|
+
let(:expr){ f.not(f.contradiction) }
|
49
|
+
|
50
|
+
it{ should eq("->(t){ !false }") }
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "comp" do
|
54
|
+
let(:expr){ f.comp(:lt, {:x => 2}) }
|
55
|
+
|
56
|
+
it{ should eq("->(t){ t[:x] < 2 }") }
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "eq" do
|
60
|
+
let(:expr){ f.eq(:x, 2) }
|
61
|
+
|
62
|
+
it{ should eq("->(t){ t[:x] == 2 }") }
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "eq (parentheses required)" do
|
66
|
+
let(:expr){ f.eq(f.eq(:y, 2), true) }
|
67
|
+
|
68
|
+
it{ should eq("->(t){ (t[:y] == 2) == true }") }
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "comp (multiple)" do
|
72
|
+
let(:expr){ f.comp(:eq, :x => 2, :y => 3) }
|
73
|
+
|
74
|
+
it{ should eq("->(t){ (t[:x] == 2) && (t[:y] == 3) }") }
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "in" do
|
78
|
+
let(:expr){ f.in(:x, [2, 3]) }
|
79
|
+
|
80
|
+
it{ should eq("->(t){ [2, 3].include?(t[:x]) }") }
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "neq" do
|
84
|
+
let(:expr){ f.neq(:x, 2) }
|
85
|
+
|
86
|
+
it{ should eq("->(t){ t[:x] != 2 }") }
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "gt" do
|
90
|
+
let(:expr){ f.gt(:x, 2) }
|
91
|
+
|
92
|
+
it{ should eq("->(t){ t[:x] > 2 }") }
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "gte" do
|
96
|
+
let(:expr){ f.gte(:x, 2) }
|
97
|
+
|
98
|
+
it{ should eq("->(t){ t[:x] >= 2 }") }
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "lt" do
|
102
|
+
let(:expr){ f.lt(:x, 2) }
|
103
|
+
|
104
|
+
it{ should eq("->(t){ t[:x] < 2 }") }
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "lte" do
|
108
|
+
let(:expr){ f.lte(:x, 2) }
|
109
|
+
|
110
|
+
it{ should eq("->(t){ t[:x] <= 2 }") }
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "literal" do
|
114
|
+
let(:expr){ f.literal(12) }
|
115
|
+
|
116
|
+
it{ should eq("->(t){ 12 }") }
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "native" do
|
120
|
+
let(:expr){ f.native(lambda{}) }
|
121
|
+
|
122
|
+
specify{
|
123
|
+
lambda{ subject }.should raise_error(NotSupportedError)
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "conjunction of two eqs" do
|
128
|
+
let(:expr){
|
129
|
+
f.and(f.eq(:x, 2), f.eq(:y, 3))
|
130
|
+
}
|
131
|
+
|
132
|
+
it{ should eq("->(t){ (t[:x] == 2) && (t[:y] == 3) }") }
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "conjunction of two comps" do
|
136
|
+
let(:expr){
|
137
|
+
f.and(f.comp(:eq, :x => 2), f.comp(:eq, :y => 3))
|
138
|
+
}
|
139
|
+
|
140
|
+
it{ should eq("->(t){ (t[:x] == 2) && (t[:y] == 3) }") }
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "or and and" do
|
144
|
+
let(:expr){
|
145
|
+
f.and(f.eq(:x, 2), f.or(true, false))
|
146
|
+
}
|
147
|
+
|
148
|
+
it{ should eq("->(t){ (t[:x] == 2) && (true || false) }") }
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'a_predicate_ast_node'
|
2
|
+
shared_examples_for "a comparison factory method" do
|
3
|
+
include Predicate::Factory
|
4
|
+
|
5
|
+
context 'with two operands' do
|
6
|
+
subject{ self.send(method, true, true) }
|
7
|
+
|
8
|
+
it_should_behave_like "a predicate AST node"
|
9
|
+
it{ should be_a(node_class) }
|
10
|
+
it{ should eql([method, tautology, tautology]) }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'with a Hash operand (singleton)' do
|
14
|
+
subject{ self.send(method, :x => :y) }
|
15
|
+
let(:expected){
|
16
|
+
[method, [:identifier, :x], [:identifier, :y]]
|
17
|
+
}
|
18
|
+
|
19
|
+
it_should_behave_like "a predicate AST node"
|
20
|
+
it{ should eql(expected) }
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'with a Hash operand' do
|
24
|
+
subject{ self.send(method, :x => :y, :v => 2) }
|
25
|
+
let(:expected){
|
26
|
+
[:and,
|
27
|
+
[method, [:identifier, :x], [:identifier, :y]],
|
28
|
+
[method, [:identifier, :v], [:literal, 2]]]
|
29
|
+
}
|
30
|
+
|
31
|
+
it_should_behave_like "a predicate AST node"
|
32
|
+
it{ should eql(expected) }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
shared_examples_for "a predicate AST node" do
|
3
|
+
|
4
|
+
it{ should be_a(Sexpr) }
|
5
|
+
|
6
|
+
it{ should be_a(Predicate::Expr) }
|
7
|
+
|
8
|
+
specify{
|
9
|
+
(subject.tautology? == subject.is_a?(Predicate::Tautology)).should be(true)
|
10
|
+
}
|
11
|
+
|
12
|
+
specify{
|
13
|
+
(subject.contradiction? == subject.is_a?(Predicate::Contradiction)).should be(true)
|
14
|
+
}
|
15
|
+
|
16
|
+
specify{
|
17
|
+
subject.free_variables.should be_a(Array) unless subject.is_a?(Predicate::Native)
|
18
|
+
}
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'shared/a_predicate_ast_node'
|
2
|
+
class Predicate
|
3
|
+
describe Factory, 'and' do
|
4
|
+
include Factory
|
5
|
+
|
6
|
+
subject{ self.and(true, true) }
|
7
|
+
|
8
|
+
it_should_behave_like "a predicate AST node"
|
9
|
+
it{ should be_a(And) }
|
10
|
+
it{ should eql([:and, tautology, tautology]) }
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require_relative "shared/a_comparison_factory_method"
|
2
|
+
class Predicate
|
3
|
+
describe Factory, 'between' do
|
4
|
+
|
5
|
+
subject{ Factory.between(:x, 2, 3) }
|
6
|
+
|
7
|
+
it{ should be_a(And) }
|
8
|
+
|
9
|
+
it{ should eq([:and, [:gte, [:identifier, :x], [:literal, 2]], [:lte, [:identifier, :x], [:literal, 3]]]) }
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'shared/a_predicate_ast_node'
|
2
|
+
class Predicate
|
3
|
+
describe Factory, 'comp' do
|
4
|
+
|
5
|
+
subject{ Factory.comp(:eq, h) }
|
6
|
+
|
7
|
+
context "when the hash is empty" do
|
8
|
+
let(:h){ {} }
|
9
|
+
|
10
|
+
it{ should eq(Factory.tautology) }
|
11
|
+
end
|
12
|
+
|
13
|
+
context "when the hash is singleton" do
|
14
|
+
let(:h){ {:x => 12, :y => :z} }
|
15
|
+
let(:expected){
|
16
|
+
[:and,
|
17
|
+
[:eq, [:identifier, :x], [:literal, 12]],
|
18
|
+
[:eq, [:identifier, :y], [:identifier, :z]]]
|
19
|
+
}
|
20
|
+
|
21
|
+
it_should_behave_like "a predicate AST node"
|
22
|
+
it{ should be_a(And) }
|
23
|
+
it{ should eq(expected) }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when the hash is not singelton" do
|
27
|
+
let(:h){ {:x => 12} }
|
28
|
+
|
29
|
+
it_should_behave_like "a predicate AST node"
|
30
|
+
it{ should be_a(Eq) }
|
31
|
+
it{ should eq([:eq, [:identifier, :x], [:literal, 12]]) }
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class Predicate
|
3
|
+
describe Factory, "_factor_predicate" do
|
4
|
+
|
5
|
+
subject{ Factory._factor_predicate(arg) }
|
6
|
+
|
7
|
+
context "on Expr" do
|
8
|
+
let(:arg){ Grammar.sexpr([:literal, 12]) }
|
9
|
+
|
10
|
+
it{ should be(arg) }
|
11
|
+
end
|
12
|
+
|
13
|
+
context "on true" do
|
14
|
+
let(:arg){ true }
|
15
|
+
|
16
|
+
it{ should be_a(Tautology) }
|
17
|
+
end
|
18
|
+
|
19
|
+
context "on false" do
|
20
|
+
let(:arg){ false }
|
21
|
+
|
22
|
+
it{ should be_a(Contradiction) }
|
23
|
+
end
|
24
|
+
|
25
|
+
context "on Symbol" do
|
26
|
+
let(:arg){ :name }
|
27
|
+
|
28
|
+
it{ should be_a(Identifier) }
|
29
|
+
end
|
30
|
+
|
31
|
+
context "on Proc" do
|
32
|
+
let(:arg){ lambda{} }
|
33
|
+
|
34
|
+
it{ should be_a(Native) }
|
35
|
+
end
|
36
|
+
|
37
|
+
context "on Array" do
|
38
|
+
let(:arg){ [:identifier, :name] }
|
39
|
+
|
40
|
+
it{ should be_a(Identifier) }
|
41
|
+
end
|
42
|
+
|
43
|
+
context "on 12" do
|
44
|
+
let(:arg){ 12 }
|
45
|
+
|
46
|
+
it{ should be_a(Literal) }
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'shared/a_predicate_ast_node'
|
2
|
+
class Predicate
|
3
|
+
describe Factory, 'identifier' do
|
4
|
+
include Factory
|
5
|
+
|
6
|
+
subject{ identifier(:name) }
|
7
|
+
|
8
|
+
it_should_behave_like "a predicate AST node"
|
9
|
+
it{ should be_a(Identifier) }
|
10
|
+
it{ should eql([:identifier, :name]) }
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'shared/a_predicate_ast_node'
|
2
|
+
class Predicate
|
3
|
+
describe Factory, 'literal' do
|
4
|
+
include Factory
|
5
|
+
|
6
|
+
subject{ literal(12) }
|
7
|
+
|
8
|
+
it_should_behave_like "a predicate AST node"
|
9
|
+
it{ should be_a(Literal) }
|
10
|
+
it{ should eql([:literal, 12]) }
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|