predicate 2.3.0 → 2.5.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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -0
  3. data/LICENSE.md +17 -19
  4. data/README.md +433 -0
  5. data/bin/g +2 -0
  6. data/lib/predicate.rb +26 -2
  7. data/lib/predicate/dsl.rb +138 -0
  8. data/lib/predicate/factory.rb +130 -33
  9. data/lib/predicate/grammar.rb +11 -2
  10. data/lib/predicate/grammar.sexp.yml +29 -0
  11. data/lib/predicate/nodes/${op_name}.rb.jeny +12 -0
  12. data/lib/predicate/nodes/and.rb +9 -0
  13. data/lib/predicate/nodes/binary_func.rb +20 -0
  14. data/lib/predicate/nodes/contradiction.rb +2 -7
  15. data/lib/predicate/nodes/dyadic_comp.rb +3 -5
  16. data/lib/predicate/nodes/empty.rb +14 -0
  17. data/lib/predicate/nodes/eq.rb +13 -6
  18. data/lib/predicate/nodes/expr.rb +9 -3
  19. data/lib/predicate/nodes/has_size.rb +14 -0
  20. data/lib/predicate/nodes/identifier.rb +1 -3
  21. data/lib/predicate/nodes/in.rb +9 -8
  22. data/lib/predicate/nodes/intersect.rb +3 -23
  23. data/lib/predicate/nodes/literal.rb +1 -3
  24. data/lib/predicate/nodes/match.rb +1 -21
  25. data/lib/predicate/nodes/nadic_bool.rb +1 -3
  26. data/lib/predicate/nodes/native.rb +1 -3
  27. data/lib/predicate/nodes/not.rb +1 -3
  28. data/lib/predicate/nodes/opaque.rb +1 -3
  29. data/lib/predicate/nodes/qualified_identifier.rb +2 -4
  30. data/lib/predicate/nodes/set_op.rb +26 -0
  31. data/lib/predicate/nodes/subset.rb +11 -0
  32. data/lib/predicate/nodes/superset.rb +11 -0
  33. data/lib/predicate/nodes/tautology.rb +6 -7
  34. data/lib/predicate/nodes/unary_func.rb +16 -0
  35. data/lib/predicate/nodes/var.rb +46 -0
  36. data/lib/predicate/processors.rb +1 -0
  37. data/lib/predicate/processors/qualifier.rb +4 -0
  38. data/lib/predicate/processors/renamer.rb +4 -0
  39. data/lib/predicate/processors/to_s.rb +28 -0
  40. data/lib/predicate/processors/unqualifier.rb +21 -0
  41. data/lib/predicate/sequel/to_sequel.rb +4 -1
  42. data/lib/predicate/sugar.rb +47 -0
  43. data/lib/predicate/version.rb +1 -1
  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_empty.rb +11 -0
  51. data/spec/factory/test_has_size.rb +11 -0
  52. data/spec/factory/test_match.rb +1 -0
  53. data/spec/factory/test_set_ops.rb +18 -0
  54. data/spec/factory/test_var.rb +22 -0
  55. data/spec/factory/test_vars.rb +27 -0
  56. data/spec/nodes/${op_name}.jeny/test_evaluate.rb.jeny +19 -0
  57. data/spec/nodes/empty/test_evaluate.rb +42 -0
  58. data/spec/nodes/eq/test_and.rb +6 -0
  59. data/spec/nodes/has_size/test_evaluate.rb +44 -0
  60. data/spec/nodes/qualified_identifier/test_and_split.rb +1 -1
  61. data/spec/nodes/qualified_identifier/test_free_variables.rb +1 -1
  62. data/spec/predicate/test_and_split.rb +18 -0
  63. data/spec/predicate/test_attr_split.rb +18 -0
  64. data/spec/predicate/test_bool_and.rb +11 -0
  65. data/spec/predicate/test_constant_variables.rb +24 -2
  66. data/spec/predicate/test_constants.rb +24 -0
  67. data/spec/predicate/test_evaluate.rb +205 -3
  68. data/spec/predicate/test_free_variables.rb +1 -1
  69. data/spec/predicate/test_to_hash.rb +40 -0
  70. data/spec/predicate/test_to_s.rb +37 -0
  71. data/spec/predicate/test_unqualify.rb +18 -0
  72. data/spec/sequel/test_to_sequel.rb +16 -0
  73. data/spec/shared/a_predicate.rb +30 -0
  74. data/spec/spec_helper.rb +1 -0
  75. data/spec/test_predicate.rb +68 -33
  76. data/spec/test_readme.rb +80 -0
  77. data/spec/test_sugar.rb +48 -0
  78. data/tasks/test.rake +3 -3
  79. metadata +43 -13
  80. data/spec/factory/test_between.rb +0 -12
  81. data/spec/factory/test_intersect.rb +0 -12
@@ -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])
@@ -22,8 +20,8 @@ class Predicate
22
20
  @free_variables ||= left.free_variables | right.free_variables
23
21
  end
24
22
 
25
- def var_against_literal?
26
- left.identifier? && right.literal?
23
+ def var_against_literal_value?
24
+ left.identifier? && right.literal? && !right.has_placeholder?
27
25
  end
28
26
 
29
27
  end
@@ -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
@@ -13,9 +13,8 @@ class Predicate
13
13
  return self if constants == other.constants
14
14
  return contradiction
15
15
  when In
16
- return super unless var_against_literal? && other.var_against_literal?
17
- mine = self.right.value
18
- hers = other.right.value
16
+ return super unless var_against_literal_value? && other.var_against_literal_value?
17
+ mine, hers = self.right.value, other.right.value
19
18
  return self if hers.include?(mine)
20
19
  contradiction
21
20
  else
@@ -41,13 +40,21 @@ class Predicate
41
40
  end
42
41
  end
43
42
 
44
- def dyadic_priority
45
- 900
46
- end
43
+ def dyadic_priority; 900; end
47
44
 
48
45
  def evaluate(tuple)
49
46
  left.evaluate(tuple) == right.evaluate(tuple)
50
47
  end
51
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
+
52
59
  end
53
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)
@@ -71,8 +67,13 @@ class Predicate
71
67
  values.include?(identifier.evaluate(tuple))
72
68
  end
73
69
 
74
- def var_against_literal?
75
- left.identifier? && right.literal?
70
+ def var_against_literal_value?
71
+ left.identifier? && right.literal? && !right.has_placeholder?
72
+ end
73
+
74
+ def to_hash
75
+ return super unless var_against_literal_value?
76
+ { identifier.name => right.value }
76
77
  end
77
78
 
78
79
  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 ||= []
@@ -2,9 +2,7 @@ class Predicate
2
2
  module QualifiedIdentifier
3
3
  include Expr
4
4
 
5
- def priority
6
- 100
7
- end
5
+ def priority; 100; end
8
6
 
9
7
  def qualifier
10
8
  self[1]
@@ -15,7 +13,7 @@ class Predicate
15
13
  end
16
14
 
17
15
  def free_variables
18
- @free_variables ||= [ name ]
16
+ @free_variables ||= [ :"#{qualifier}.#{name}" ]
19
17
  end
20
18
 
21
19
  def evaluate(tuple)