predicate 2.3.2 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -0
  3. data/LICENSE.md +17 -19
  4. data/README.md +435 -0
  5. data/bin/g +2 -0
  6. data/lib/predicate/dsl.rb +138 -0
  7. data/lib/predicate/factory.rb +142 -37
  8. data/lib/predicate/grammar.rb +11 -2
  9. data/lib/predicate/grammar.sexp.yml +29 -0
  10. data/lib/predicate/nodes/${op_name}.rb.jeny +12 -0
  11. data/lib/predicate/nodes/and.rb +9 -0
  12. data/lib/predicate/nodes/binary_func.rb +20 -0
  13. data/lib/predicate/nodes/contradiction.rb +2 -7
  14. data/lib/predicate/nodes/dyadic_comp.rb +1 -3
  15. data/lib/predicate/nodes/empty.rb +14 -0
  16. data/lib/predicate/nodes/eq.rb +11 -3
  17. data/lib/predicate/nodes/expr.rb +9 -3
  18. data/lib/predicate/nodes/has_size.rb +14 -0
  19. data/lib/predicate/nodes/identifier.rb +1 -3
  20. data/lib/predicate/nodes/in.rb +7 -6
  21. data/lib/predicate/nodes/intersect.rb +3 -23
  22. data/lib/predicate/nodes/literal.rb +1 -3
  23. data/lib/predicate/nodes/match.rb +1 -21
  24. data/lib/predicate/nodes/nadic_bool.rb +1 -3
  25. data/lib/predicate/nodes/native.rb +1 -3
  26. data/lib/predicate/nodes/not.rb +1 -3
  27. data/lib/predicate/nodes/opaque.rb +1 -3
  28. data/lib/predicate/nodes/qualified_identifier.rb +1 -3
  29. data/lib/predicate/nodes/set_op.rb +26 -0
  30. data/lib/predicate/nodes/subset.rb +11 -0
  31. data/lib/predicate/nodes/superset.rb +11 -0
  32. data/lib/predicate/nodes/tautology.rb +6 -7
  33. data/lib/predicate/nodes/unary_func.rb +16 -0
  34. data/lib/predicate/nodes/var.rb +46 -0
  35. data/lib/predicate/processors/qualifier.rb +4 -0
  36. data/lib/predicate/processors/renamer.rb +4 -0
  37. data/lib/predicate/processors/to_s.rb +28 -0
  38. data/lib/predicate/processors/unqualifier.rb +21 -0
  39. data/lib/predicate/processors.rb +1 -0
  40. data/lib/predicate/sequel/to_sequel.rb +4 -1
  41. data/lib/predicate/sugar.rb +47 -0
  42. data/lib/predicate/version.rb +2 -2
  43. data/lib/predicate.rb +26 -2
  44. data/spec/dsl/test_dsl.rb +204 -0
  45. data/spec/dsl/test_evaluate.rb +65 -0
  46. data/spec/dsl/test_respond_to_missing.rb +35 -0
  47. data/spec/dsl/test_to_skake_case.rb +38 -0
  48. data/spec/factory/shared/a_comparison_factory_method.rb +1 -0
  49. data/spec/factory/test_${op_name}.rb.jeny +12 -0
  50. data/spec/factory/test_comp.rb +28 -5
  51. data/spec/factory/test_empty.rb +11 -0
  52. data/spec/factory/test_has_size.rb +11 -0
  53. data/spec/factory/test_match.rb +1 -0
  54. data/spec/factory/test_set_ops.rb +18 -0
  55. data/spec/factory/test_var.rb +22 -0
  56. data/spec/factory/test_vars.rb +27 -0
  57. data/spec/nodes/${op_name}.jeny/test_evaluate.rb.jeny +19 -0
  58. data/spec/nodes/empty/test_evaluate.rb +42 -0
  59. data/spec/nodes/has_size/test_evaluate.rb +44 -0
  60. data/spec/predicate/test_and_split.rb +18 -0
  61. data/spec/predicate/test_attr_split.rb +18 -0
  62. data/spec/predicate/test_constant_variables.rb +24 -2
  63. data/spec/predicate/test_constants.rb +24 -0
  64. data/spec/predicate/test_evaluate.rb +205 -3
  65. data/spec/predicate/test_free_variables.rb +1 -1
  66. data/spec/predicate/test_to_hash.rb +40 -0
  67. data/spec/predicate/test_to_s.rb +37 -0
  68. data/spec/predicate/test_unqualify.rb +18 -0
  69. data/spec/sequel/test_to_sequel.rb +25 -0
  70. data/spec/shared/a_predicate.rb +30 -0
  71. data/spec/spec_helper.rb +1 -0
  72. data/spec/test_predicate.rb +78 -33
  73. data/spec/test_readme.rb +80 -0
  74. data/spec/test_sugar.rb +48 -0
  75. data/tasks/test.rake +3 -3
  76. metadata +45 -14
  77. data/spec/factory/test_between.rb +0 -12
  78. data/spec/factory/test_intersect.rb +0 -12
@@ -1,78 +1,199 @@
1
1
  class Predicate
2
2
  module Factory
3
3
 
4
+ public # Boolean
5
+
6
+ # Factors a Predicate that captures True
4
7
  def tautology
5
8
  _factor_predicate([:tautology, true])
6
9
  end
7
10
 
11
+ # Factors a Predicate that captures False
8
12
  def contradiction
9
13
  _factor_predicate([:contradiction, false])
10
14
  end
11
15
 
16
+ public # Literals
17
+
18
+ # Factors a Literal node for some ruby value.
19
+ def literal(literal)
20
+ _factor_predicate([:literal, literal])
21
+ end
22
+
23
+ public # Vars & identifiers
24
+
25
+ # Factors a var node, using a given extractor semantics
26
+ def var(formaldef, semantics = :dig)
27
+ _factor_predicate([:var, formaldef, semantics])
28
+ end
29
+
30
+ # Factors a couple of variables at once. The semantics can
31
+ # be passed as a Symbol as last argument and defaults to :dig
32
+ def vars(*args)
33
+ args << :dig unless args.last.is_a?(Symbol)
34
+ args[0...-1].map{|v| var(v, args.last) }
35
+ end
36
+
37
+ # Factors a Predicate for a free variable whose
38
+ # name is provided. If the variable is a Boolean
39
+ # variable, this is a valid Predicate, otherwise
40
+ # it must be used in a higher-level expression.
12
41
  def identifier(name)
13
42
  _factor_predicate([:identifier, name])
14
43
  end
15
44
 
45
+ # Factors a Predicate for a qualified free variable.
46
+ # Same remark as in `identifier`.
16
47
  def qualified_identifier(qualifier, name)
17
48
  _factor_predicate([:qualified_identifier, qualifier, name])
18
49
  end
19
50
 
51
+ # Builds and returns a placeholder that can be used
52
+ # everywhere a literal can be used. Placeholders can
53
+ # be bound later, using `Predicate#bind`.
20
54
  def placeholder
21
55
  Placeholder.new
22
56
  end
23
57
 
58
+ public # Boolean logic
59
+
60
+ # Builds a AND predicate using two sub predicates.
61
+ #
62
+ # Please favor `Predicate#&` instead.
24
63
  def and(left, right = nil)
25
64
  _factor_predicate([:and, sexpr(left), sexpr(right)])
26
65
  end
27
66
 
67
+ # Builds a OR predicate using two sub predicates.
68
+ #
69
+ # Please favor `Predicate#|` instead.
28
70
  def or(left, right = nil)
29
71
  _factor_predicate([:or, sexpr(left), sexpr(right)])
30
72
  end
31
73
 
74
+ # Negates an existing predicate.
75
+ #
76
+ # Please favor `Predicate#!` instead.
32
77
  def not(operand)
33
78
  _factor_predicate([:not, sexpr(operand)])
34
79
  end
35
80
 
81
+ public # Comparison operators
82
+
83
+ # :nodoc:
84
+ def comp(op, h)
85
+ from_hash(h, op)
86
+ end
87
+
88
+ # Factors =, !=, <, <=, >, >= predicates between
89
+ # a variable and either a literal or another variable.
90
+ [ :eq, :neq, :lt, :lte, :gt, :gte ].each do |m|
91
+ define_method(m) do |left, right|
92
+ _factor_predicate([m, sexpr(left), sexpr(right)])
93
+ end
94
+ end
95
+
96
+ # Set operators
97
+
98
+ # Factors a IN predicate between a variable and
99
+ # either a list of values of another variable.
36
100
  def in(left, right)
37
- left, right = sexpr(left), sexpr(right)
38
- if right.literal? && right.empty_value?
39
- contradiction
101
+ case right
102
+ when Range
103
+ return contradiction if right.size == 0
104
+ rl = gte(left, right.begin)
105
+ rr = right.exclude_end? ? lt(left, right.end) : lte(left, right.end)
106
+ self.and(rl, rr)
40
107
  else
41
- _factor_predicate([:in, left, right])
108
+ left, right = sexpr(left), sexpr(right)
109
+ if right.literal? && right.empty_value?
110
+ contradiction
111
+ else
112
+ _factor_predicate([:in, left, right])
113
+ end
42
114
  end
43
115
  end
44
116
  alias :among :in
45
117
 
46
- def intersect(identifier, values)
47
- identifier = sexpr(identifier) if identifier.is_a?(Symbol)
48
- _factor_predicate([:intersect, identifier, values])
118
+ # Factors an INTERSECT predicate between a
119
+ # variable and a list of values.
120
+ [:intersect, :subset, :superset].each do |name|
121
+ define_method(name) do |left, right|
122
+ identifier = sexpr(identifier) if identifier.is_a?(Symbol)
123
+ _factor_predicate([name, sexpr(left), sexpr(right)])
124
+ end
49
125
  end
50
126
 
51
- def comp(op, h)
52
- from_hash(h, op)
127
+ public # Other operators
128
+
129
+ # Factors a MATCH predicate between a variable
130
+ # and a literal or another variable.
131
+ #
132
+ # Matching options can be passes and are specific
133
+ # to the actual usage of the library.
134
+ def match(left, right, options)
135
+ s = [:match, sexpr(left), sexpr(right)]
136
+ s << options unless options.nil?
137
+ _factor_predicate(s)
53
138
  end
54
139
 
55
- [ :eq, :neq, :lt, :lte, :gt, :gte ].each do |m|
56
- define_method(m) do |left, right=nil|
57
- return comp(m, left) if TupleLike===left && right.nil?
58
- _factor_predicate([m, sexpr(left), sexpr(right)])
59
- end
140
+ # Factors an EMPTY predicate that responds true
141
+ # when its operand is something empty.
142
+ #
143
+ # Default evaluation uses ruby `empty?` method.
144
+ def empty(operand)
145
+ _factor_predicate([:empty, sexpr(operand)])
146
+ end
147
+
148
+ # Factors a SIZE predicate that responds true when
149
+ # its operand has a size meeting the right constraint
150
+ # (typically a Range literal)
151
+ def has_size(left, right)
152
+ _factor_predicate([:has_size, sexpr(left), sexpr(right)])
153
+ end
154
+
155
+ #jeny(predicate) # TODO
156
+ #jeny(predicate) def ${op_name}(*args)
157
+ #jeny(predicate) args = args.map{|arg| sexpr(arg) }
158
+ #jeny(predicate) _factor_predicate([:${op_name}] + args)
159
+ #jeny(predicate) end
160
+
161
+ public # Low-level
162
+
163
+ # Factors a predicate from a mapping between variables
164
+ # and values. This typically generates a AND(EQ)
165
+ # predicate, but a value can be an Array (IN) or a
166
+ # Regexp (MATCH).
167
+ def h(h)
168
+ from_hash(h, :eq)
60
169
  end
61
170
 
62
- def between(middle, lower_bound, upper_bound)
63
- _factor_predicate [:and, [:gte, sexpr(middle), sexpr(lower_bound)],
64
- [:lte, sexpr(middle), sexpr(upper_bound)]]
171
+ # Factors a predicate for a ruby Proc that returns
172
+ # truth-value for a single argument.
173
+ def native(arg)
174
+ _factor_predicate([:native, arg])
65
175
  end
66
176
 
177
+ # Converts `arg` to an opaque predicate, whose semantics
178
+ # depends on the actual usage of the library.
179
+ def opaque(arg)
180
+ _factor_predicate([:opaque, arg])
181
+ end
182
+
183
+ public # Semi protected
184
+
185
+ # Builds a AND predicate between all key/value pairs
186
+ # of the provided Hash, using the comparison operator
187
+ # specified.
67
188
  def from_hash(h, op = :eq)
68
189
  if h.empty?
69
190
  tautology
70
191
  else
71
192
  terms = h.to_a.map{|(k,v)|
72
- if v.is_a?(Array)
73
- [:in, sexpr(k), sexpr(v)]
74
- else
75
- [op, sexpr(k), sexpr(v)]
193
+ case v
194
+ when Array then [:in, sexpr(k), sexpr(v)]
195
+ when Regexp then [:match, sexpr(k), sexpr(v) ]
196
+ else [op, sexpr(k), sexpr(v)]
76
197
  end
77
198
  }
78
199
  terms = terms.size == 1 ? terms.first : terms.unshift(:and)
@@ -80,22 +201,6 @@ class Predicate
80
201
  end
81
202
  end
82
203
 
83
- def literal(literal)
84
- _factor_predicate([:literal, literal])
85
- end
86
-
87
- def opaque(arg)
88
- _factor_predicate([:opaque, arg])
89
- end
90
-
91
- def match(left, right, options = nil)
92
- _factor_predicate([:match, sexpr(left), sexpr(right)] + (options.nil? ? [] : [options]))
93
- end
94
-
95
- def native(arg)
96
- _factor_predicate([:native, arg])
97
- end
98
-
99
204
  protected
100
205
 
101
206
  def sexpr(expr)
@@ -10,13 +10,14 @@ class Predicate
10
10
  Expr
11
11
  end
12
12
 
13
- end
13
+ end # module Grammar
14
14
  end # class Predicate
15
15
  require_relative 'nodes/expr'
16
16
  require_relative 'nodes/dyadic_comp'
17
17
  require_relative 'nodes/nadic_bool'
18
18
  require_relative 'nodes/tautology'
19
19
  require_relative 'nodes/contradiction'
20
+ require_relative 'nodes/var'
20
21
  require_relative 'nodes/identifier'
21
22
  require_relative 'nodes/qualified_identifier'
22
23
  require_relative 'nodes/and'
@@ -29,8 +30,16 @@ require_relative 'nodes/gte'
29
30
  require_relative 'nodes/lt'
30
31
  require_relative 'nodes/lte'
31
32
  require_relative 'nodes/in'
33
+ require_relative 'nodes/set_op'
32
34
  require_relative 'nodes/intersect'
35
+ require_relative 'nodes/subset'
36
+ require_relative 'nodes/superset'
33
37
  require_relative 'nodes/literal'
34
- require_relative 'nodes/match'
35
38
  require_relative 'nodes/native'
36
39
  require_relative 'nodes/opaque'
40
+ require_relative 'nodes/unary_func'
41
+ require_relative 'nodes/binary_func'
42
+ require_relative 'nodes/match'
43
+ require_relative 'nodes/empty'
44
+ require_relative 'nodes/has_size'
45
+ #jeny(predicate) require_relative 'nodes/${op_name}'
@@ -3,6 +3,8 @@ rules:
3
3
  - tautology
4
4
  - contradiction
5
5
  - identifier
6
+ - qualified_identifier
7
+ - var
6
8
  - not
7
9
  - and
8
10
  - or
@@ -14,8 +16,13 @@ rules:
14
16
  - gte
15
17
  - in
16
18
  - intersect
19
+ - subset
20
+ - superset
17
21
  - match
18
22
  - native
23
+ - empty
24
+ - has_size
25
+ #jeny(predicate) - ${op_name}
19
26
  tautology:
20
27
  - [ true ]
21
28
  contradiction:
@@ -24,6 +31,8 @@ rules:
24
31
  - [ name ]
25
32
  qualified_identifier:
26
33
  - [ name, name ]
34
+ var:
35
+ - [ formaldef, semantics ]
27
36
  not:
28
37
  - [ predicate ]
29
38
  and:
@@ -46,8 +55,18 @@ rules:
46
55
  - [ varref, term ]
47
56
  intersect:
48
57
  - [ term, term ]
58
+ subset:
59
+ - [ term, term ]
60
+ superset:
61
+ - [ term, term ]
49
62
  match:
50
63
  - [ term, term, options ]
64
+ empty:
65
+ - [ term ]
66
+ has_size:
67
+ - [ term, term ]
68
+ #jeny(predicate) ${op_name}:
69
+ #jeny(predicate) - [ TODO ]
51
70
  term:
52
71
  - varref
53
72
  - literal
@@ -56,6 +75,7 @@ rules:
56
75
  varref:
57
76
  - qualified_identifier
58
77
  - identifier
78
+ - var
59
79
  native:
60
80
  - [ "::Proc" ]
61
81
  literal:
@@ -68,3 +88,12 @@ rules:
68
88
  - "::Hash"
69
89
  name:
70
90
  !ruby/regexp /^[a-zA-Z0-9_]+[?!]?$/
91
+ semantics:
92
+ !ruby/regexp /^(dig)$/
93
+ formaldef:
94
+ - string_formaldef
95
+ - array_formaldef
96
+ string_formaldef:
97
+ - "::String"
98
+ array_formaldef:
99
+ - "::Array"
@@ -0,0 +1,12 @@
1
+ #jeny(predicate)
2
+ class Predicate
3
+ module ${OpName}
4
+ include ${Arity}Func
5
+
6
+ def evaluate(tuple)
7
+ # TODO: implement this
8
+ raise NotImplementedError
9
+ end
10
+
11
+ end
12
+ end
@@ -53,5 +53,14 @@ class Predicate
53
53
  sexpr_body.all?{|operand| operand.evaluate(tuple) }
54
54
  end
55
55
 
56
+ def to_hash
57
+ sexpr_body.inject({}) do |p,term|
58
+ p.merge(term.to_hash){|k,v1,v2|
59
+ super unless v1 == v2
60
+ v1
61
+ }
62
+ end
63
+ end
64
+
56
65
  end
57
66
  end
@@ -0,0 +1,20 @@
1
+ class Predicate
2
+ module BinaryFunc
3
+ include Expr
4
+
5
+ def priority; 80; end
6
+
7
+ def left
8
+ self[1]
9
+ end
10
+
11
+ def right
12
+ self[2]
13
+ end
14
+
15
+ def free_variables
16
+ @free_variables ||= left.free_variables | right.free_variables
17
+ end
18
+
19
+ end
20
+ end
@@ -18,13 +18,8 @@ class Predicate
18
18
  other
19
19
  end
20
20
 
21
- def dyadic_priority
22
- 1000
23
- end
24
-
25
- def priority
26
- 100
27
- end
21
+ def dyadic_priority; 1000; end
22
+ def priority; 100; end
28
23
 
29
24
  def free_variables
30
25
  @free_variables ||= []
@@ -2,9 +2,7 @@ class Predicate
2
2
  module DyadicComp
3
3
  include Expr
4
4
 
5
- def priority
6
- 50
7
- end
5
+ def priority; 50; end
8
6
 
9
7
  def !
10
8
  Factory.send(OP_NEGATIONS[first], self[1], self[2])
@@ -0,0 +1,14 @@
1
+ class Predicate
2
+ module Empty
3
+ include UnaryFunc
4
+
5
+ def evaluate(tuple)
6
+ value = operand.evaluate(tuple)
7
+ unless value.respond_to?(:empty?)
8
+ raise TypeError, "Expected #{value} to respond to empty?"
9
+ end
10
+ value.empty?
11
+ end
12
+
13
+ end
14
+ end
@@ -40,13 +40,21 @@ class Predicate
40
40
  end
41
41
  end
42
42
 
43
- def dyadic_priority
44
- 900
45
- end
43
+ def dyadic_priority; 900; end
46
44
 
47
45
  def evaluate(tuple)
48
46
  left.evaluate(tuple) == right.evaluate(tuple)
49
47
  end
50
48
 
49
+ def to_hash
50
+ if left.identifier? && right.literal? && !right.has_placeholder?
51
+ { left.name => right.value }
52
+ elsif right.identifier? && left.literal? && !left.has_placeholder?
53
+ { right.name => left.value }
54
+ else
55
+ super
56
+ end
57
+ end
58
+
51
59
  end
52
60
  end
@@ -35,9 +35,7 @@ class Predicate
35
35
  sexpr([:not, self])
36
36
  end
37
37
 
38
- def dyadic_priority
39
- 0
40
- end
38
+ def dyadic_priority; 0; end
41
39
 
42
40
  def &(other)
43
41
  return other if other.contradiction?
@@ -77,6 +75,10 @@ class Predicate
77
75
  Qualifier.new(qualifier).call(self)
78
76
  end
79
77
 
78
+ def unqualify
79
+ Unqualifier.new.call(self)
80
+ end
81
+
80
82
  def bind(binding)
81
83
  Binder.new(binding).call(self)
82
84
  end
@@ -93,6 +95,10 @@ class Predicate
93
95
  ToS.call(self, scope: scope)
94
96
  end
95
97
 
98
+ def to_hash
99
+ raise ArgumentError, "Unable to represent #{self} to a Hash"
100
+ end
101
+
96
102
  def sexpr(arg)
97
103
  Factory.sexpr(arg)
98
104
  end
@@ -0,0 +1,14 @@
1
+ class Predicate
2
+ module HasSize
3
+ include BinaryFunc
4
+
5
+ def evaluate(tuple)
6
+ l, r = left.evaluate(tuple), right.evaluate(tuple)
7
+ r = r..r if r.is_a?(Integer)
8
+ raise Error, "Expected Range, got #{r}" unless r.is_a?(Range)
9
+ raise Error, "Expected #{l} to respond to :size" unless l.respond_to?(:size)
10
+ r === l.size
11
+ end
12
+
13
+ end
14
+ end
@@ -2,9 +2,7 @@ class Predicate
2
2
  module Identifier
3
3
  include Expr
4
4
 
5
- def priority
6
- 100
7
- end
5
+ def priority; 100; end
8
6
 
9
7
  def name
10
8
  self[1]
@@ -2,9 +2,7 @@ class Predicate
2
2
  module In
3
3
  include Expr
4
4
 
5
- def priority
6
- 80
7
- end
5
+ def priority; 80; end
8
6
 
9
7
  def left
10
8
  self[1]
@@ -61,9 +59,7 @@ class Predicate
61
59
  end
62
60
  end
63
61
 
64
- def dyadic_priority
65
- 800
66
- end
62
+ def dyadic_priority; 800; end
67
63
 
68
64
  def evaluate(tuple)
69
65
  values = right.evaluate(tuple)
@@ -75,5 +71,10 @@ class Predicate
75
71
  left.identifier? && right.literal? && !right.has_placeholder?
76
72
  end
77
73
 
74
+ def to_hash
75
+ return super unless var_against_literal_value?
76
+ { identifier.name => right.value }
77
+ end
78
+
78
79
  end
79
80
  end
@@ -1,30 +1,10 @@
1
1
  class Predicate
2
2
  module Intersect
3
- include Expr
4
-
5
- def priority
6
- 80
7
- end
8
-
9
- def identifier
10
- self[1]
11
- end
12
-
13
- def values
14
- self[2]
15
- end
16
-
17
- def free_variables
18
- @free_variables ||= identifier.free_variables
19
- end
20
-
21
- def constant_variables
22
- []
23
- end
3
+ include SetOp
24
4
 
25
5
  def evaluate(tuple)
26
- t_x = identifier.evaluate(tuple)
27
- t_x && !(t_x & values).empty?
6
+ x, y = left.evaluate(tuple), right.evaluate(tuple)
7
+ x && y && !(x & y).empty?
28
8
  end
29
9
 
30
10
  end
@@ -2,9 +2,7 @@ class Predicate
2
2
  module Literal
3
3
  include Expr
4
4
 
5
- def priority
6
- 100
7
- end
5
+ def priority; 100; end
8
6
 
9
7
  def free_variables
10
8
  @free_variables ||= []
@@ -1,23 +1,11 @@
1
1
  class Predicate
2
2
  module Match
3
- include Expr
3
+ include BinaryFunc
4
4
 
5
5
  DEFAULT_OPTIONS = {
6
6
  case_sensitive: true
7
7
  }
8
8
 
9
- def priority
10
- 80
11
- end
12
-
13
- def left
14
- self[1]
15
- end
16
-
17
- def right
18
- self[2]
19
- end
20
-
21
9
  def options
22
10
  @options ||= DEFAULT_OPTIONS.merge(self[3] || {})
23
11
  end
@@ -26,14 +14,6 @@ class Predicate
26
14
  options[:case_sensitive]
27
15
  end
28
16
 
29
- def free_variables
30
- @free_variables ||= left.free_variables | right.free_variables
31
- end
32
-
33
- def dyadic_priority
34
- 800
35
- end
36
-
37
17
  def evaluate(tuple)
38
18
  l = left.evaluate(tuple)
39
19
  r = right.evaluate(tuple)
@@ -2,9 +2,7 @@ class Predicate
2
2
  module NadicBool
3
3
  include Expr
4
4
 
5
- def priority
6
- 60
7
- end
5
+ def priority; 60; end
8
6
 
9
7
  def free_variables
10
8
  @free_variables ||= sexpr_body.inject([]){|list,term|
@@ -2,9 +2,7 @@ class Predicate
2
2
  module Native
3
3
  include Expr
4
4
 
5
- def priority
6
- 90
7
- end
5
+ def priority; 90; end
8
6
 
9
7
  def proc
10
8
  self[1]
@@ -6,9 +6,7 @@ class Predicate
6
6
  :'!'
7
7
  end
8
8
 
9
- def priority
10
- 90
11
- end
9
+ def priority; 90; end
12
10
 
13
11
  def !
14
12
  last
@@ -2,9 +2,7 @@ class Predicate
2
2
  module Opaque
3
3
  include Expr
4
4
 
5
- def priority
6
- 100
7
- end
5
+ def priority; 100; end
8
6
 
9
7
  def free_variables
10
8
  @free_variables ||= []