predicate 1.0.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.
- 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
|