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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/LICENSE.md +22 -0
  4. data/Rakefile +11 -0
  5. data/lib/predicate/factory.rb +107 -0
  6. data/lib/predicate/grammar.rb +33 -0
  7. data/lib/predicate/grammar.sexp.yml +58 -0
  8. data/lib/predicate/nodes/and.rb +23 -0
  9. data/lib/predicate/nodes/contradiction.rb +34 -0
  10. data/lib/predicate/nodes/dyadic_comp.rb +26 -0
  11. data/lib/predicate/nodes/eq.rb +15 -0
  12. data/lib/predicate/nodes/expr.rb +68 -0
  13. data/lib/predicate/nodes/gt.rb +10 -0
  14. data/lib/predicate/nodes/gte.rb +10 -0
  15. data/lib/predicate/nodes/identifier.rb +18 -0
  16. data/lib/predicate/nodes/in.rb +26 -0
  17. data/lib/predicate/nodes/literal.rb +18 -0
  18. data/lib/predicate/nodes/lt.rb +10 -0
  19. data/lib/predicate/nodes/lte.rb +10 -0
  20. data/lib/predicate/nodes/nadic_bool.rb +16 -0
  21. data/lib/predicate/nodes/native.rb +30 -0
  22. data/lib/predicate/nodes/neq.rb +10 -0
  23. data/lib/predicate/nodes/not.rb +22 -0
  24. data/lib/predicate/nodes/or.rb +10 -0
  25. data/lib/predicate/nodes/qualified_identifier.rb +22 -0
  26. data/lib/predicate/nodes/tautology.rb +30 -0
  27. data/lib/predicate/processors/qualifier.rb +23 -0
  28. data/lib/predicate/processors/renamer.rb +25 -0
  29. data/lib/predicate/processors/to_ruby_code.rb +71 -0
  30. data/lib/predicate/processors.rb +3 -0
  31. data/lib/predicate/version.rb +8 -0
  32. data/lib/predicate.rb +113 -0
  33. data/spec/expr/test_to_proc.rb +16 -0
  34. data/spec/expr/test_to_ruby_code.rb +152 -0
  35. data/spec/factory/shared/a_comparison_factory_method.rb +35 -0
  36. data/spec/factory/shared/a_predicate_ast_node.rb +20 -0
  37. data/spec/factory/test_and.rb +13 -0
  38. data/spec/factory/test_between.rb +12 -0
  39. data/spec/factory/test_comp.rb +35 -0
  40. data/spec/factory/test_contradiction.rb +11 -0
  41. data/spec/factory/test_eq.rb +9 -0
  42. data/spec/factory/test_factor_predicate.rb +50 -0
  43. data/spec/factory/test_gt.rb +9 -0
  44. data/spec/factory/test_gte.rb +9 -0
  45. data/spec/factory/test_identifier.rb +13 -0
  46. data/spec/factory/test_in.rb +12 -0
  47. data/spec/factory/test_literal.rb +13 -0
  48. data/spec/factory/test_lt.rb +9 -0
  49. data/spec/factory/test_lte.rb +9 -0
  50. data/spec/factory/test_native.rb +19 -0
  51. data/spec/factory/test_neq.rb +9 -0
  52. data/spec/factory/test_not.rb +13 -0
  53. data/spec/factory/test_or.rb +13 -0
  54. data/spec/factory/test_qualified_identifier.rb +15 -0
  55. data/spec/factory/test_tautology.rb +12 -0
  56. data/spec/grammar/test_match.rb +93 -0
  57. data/spec/grammar/test_sexpr.rb +103 -0
  58. data/spec/nodes/and/test_and_split.rb +66 -0
  59. data/spec/nodes/dyadic_comp/test_and_split.rb +45 -0
  60. data/spec/nodes/identifier/test_and_split.rb +23 -0
  61. data/spec/nodes/identifier/test_free_variables.rb +12 -0
  62. data/spec/nodes/identifier/test_name.rb +12 -0
  63. data/spec/nodes/nadic_bool/test_free_variables.rb +14 -0
  64. data/spec/nodes/qualified_identifier/test_and_split.rb +23 -0
  65. data/spec/nodes/qualified_identifier/test_free_variables.rb +12 -0
  66. data/spec/nodes/qualified_identifier/test_name.rb +12 -0
  67. data/spec/nodes/qualified_identifier/test_qualifier.rb +12 -0
  68. data/spec/predicate/test_and_split.rb +57 -0
  69. data/spec/predicate/test_bool_and.rb +34 -0
  70. data/spec/predicate/test_bool_not.rb +68 -0
  71. data/spec/predicate/test_bool_or.rb +34 -0
  72. data/spec/predicate/test_coerce.rb +75 -0
  73. data/spec/predicate/test_constant_variables.rb +52 -0
  74. data/spec/predicate/test_contradiction.rb +24 -0
  75. data/spec/predicate/test_evaluate.rb +66 -0
  76. data/spec/predicate/test_factory_methods.rb +77 -0
  77. data/spec/predicate/test_free_variables.rb +14 -0
  78. data/spec/predicate/test_hash_and_equal.rb +26 -0
  79. data/spec/predicate/test_qualify.rb +34 -0
  80. data/spec/predicate/test_rename.rb +76 -0
  81. data/spec/predicate/test_tautology.rb +24 -0
  82. data/spec/predicate/test_to_proc.rb +14 -0
  83. data/spec/predicate/test_to_ruby_code.rb +20 -0
  84. data/spec/spec_helper.rb +12 -0
  85. data/spec/test_predicate.rb +127 -0
  86. data/tasks/test.rake +11 -0
  87. metadata +186 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: '07778e150b98d73bf35ce337d9874395f1f58e1c'
4
+ data.tar.gz: 9d0e523c2f41b8b5c530e67a14a69f3dbdf31914
5
+ SHA512:
6
+ metadata.gz: 84111b7fe5dc17da15f9cb3afb1cee966cedb9a29ed2e420348071c348d6f7c0cd94265c116afc7cae987ca7a96f6f29e52035c9540db85b817ee219da958c59
7
+ data.tar.gz: cb5d7863db1db730e22b7fd6fe1b7023b89a8a2ef442333d8762ff1fe0b585805d10a2dd832ab07fc98453d7186daeac793c8963e4e38da2ea94fdb94c73db3a
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ # The MIT Licence
2
+
3
+ Copyright (c) 2017 - Enspirit SPRL (Bernard Lambeau)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'path'
2
+
3
+ #
4
+ # Install all tasks found in tasks folder
5
+ #
6
+ # See .rake files there for complete documentation.
7
+ #
8
+ Dir["tasks/*.rake"].each do |taskfile|
9
+ load taskfile
10
+ end
11
+ task :default => :test
@@ -0,0 +1,107 @@
1
+ class Predicate
2
+ module Factory
3
+
4
+ def tautology
5
+ _factor_predicate([:tautology, true])
6
+ end
7
+
8
+ def contradiction
9
+ _factor_predicate([:contradiction, false])
10
+ end
11
+
12
+ def identifier(name)
13
+ _factor_predicate([:identifier, name])
14
+ end
15
+
16
+ def qualified_identifier(qualifier, name)
17
+ _factor_predicate([:qualified_identifier, qualifier, name])
18
+ end
19
+
20
+ def and(left, right = nil)
21
+ _factor_predicate([:and, sexpr(left), sexpr(right)])
22
+ end
23
+
24
+ def or(left, right = nil)
25
+ _factor_predicate([:or, sexpr(left), sexpr(right)])
26
+ end
27
+
28
+ def not(operand)
29
+ _factor_predicate([:not, sexpr(operand)])
30
+ end
31
+
32
+ def relation(r)
33
+ tuples = r.to_a
34
+ case tuples.size
35
+ when 0 then contradiction
36
+ when 1 then eq(tuples.first)
37
+ else
38
+ if tuples.first.size==1
39
+ k = tuples.first.keys.first
40
+ self.in(k, tuples.map{|t| t[k]})
41
+ else
42
+ tuples.inject(contradiction){|p,t| p | eq(t) }
43
+ end
44
+ end
45
+ end
46
+
47
+ def in(identifier, values)
48
+ identifier = sexpr(identifier) if identifier.is_a?(Symbol)
49
+ _factor_predicate([:in, identifier, values])
50
+ end
51
+ alias :among :in
52
+
53
+ def comp(op, h)
54
+ h = h.to_hash
55
+ if h.empty?
56
+ return tautology
57
+ elsif h.size==1
58
+ _factor_predicate [op, sexpr(h.keys.first), sexpr(h.values.last)]
59
+ else
60
+ terms = h.to_a.inject([:and]) do |anded,pair|
61
+ anded << ([op] << sexpr(pair.first) << sexpr(pair.last))
62
+ end
63
+ _factor_predicate terms
64
+ end
65
+ end
66
+
67
+ [ :eq, :neq, :lt, :lte, :gt, :gte ].each do |m|
68
+ define_method(m) do |left, right=nil|
69
+ return comp(m, left) if TupleLike===left && right.nil?
70
+ _factor_predicate([m, sexpr(left), sexpr(right)])
71
+ end
72
+ end
73
+
74
+ def between(middle, lower_bound, upper_bound)
75
+ _factor_predicate [:and, [:gte, sexpr(middle), sexpr(lower_bound)],
76
+ [:lte, sexpr(middle), sexpr(upper_bound)]]
77
+ end
78
+
79
+ def literal(literal)
80
+ _factor_predicate([:literal, literal])
81
+ end
82
+
83
+ def native(arg)
84
+ _factor_predicate([:native, arg])
85
+ end
86
+
87
+ def sexpr(expr)
88
+ case expr
89
+ when Expr then expr
90
+ when Predicate then expr.expr
91
+ when TrueClass then Grammar.sexpr([:tautology, true])
92
+ when FalseClass then Grammar.sexpr([:contradiction, false])
93
+ when Symbol then Grammar.sexpr([:identifier, expr])
94
+ when Proc then Grammar.sexpr([:native, expr])
95
+ when Array then Grammar.sexpr(expr)
96
+ else
97
+ Grammar.sexpr([:literal, expr])
98
+ end
99
+ end
100
+
101
+ def _factor_predicate(arg)
102
+ sexpr(arg)
103
+ end
104
+
105
+ extend(self)
106
+ end # module Factory
107
+ end # class Predicate
@@ -0,0 +1,33 @@
1
+ class Predicate
2
+ Grammar = Sexpr.load File.expand_path('../grammar.sexp.yml', __FILE__)
3
+ module Grammar
4
+
5
+ def tagging_reference
6
+ Predicate
7
+ end
8
+
9
+ def default_tagging_module
10
+ Expr
11
+ end
12
+
13
+ end
14
+ end # class Predicate
15
+ require_relative 'nodes/expr'
16
+ require_relative 'nodes/dyadic_comp'
17
+ require_relative 'nodes/nadic_bool'
18
+ require_relative 'nodes/tautology'
19
+ require_relative 'nodes/contradiction'
20
+ require_relative 'nodes/identifier'
21
+ require_relative 'nodes/qualified_identifier'
22
+ require_relative 'nodes/and'
23
+ require_relative 'nodes/or'
24
+ require_relative 'nodes/not'
25
+ require_relative 'nodes/eq'
26
+ require_relative 'nodes/neq'
27
+ require_relative 'nodes/gt'
28
+ require_relative 'nodes/gte'
29
+ require_relative 'nodes/lt'
30
+ require_relative 'nodes/lte'
31
+ require_relative 'nodes/in'
32
+ require_relative 'nodes/literal'
33
+ require_relative 'nodes/native'
@@ -0,0 +1,58 @@
1
+ rules:
2
+ predicate:
3
+ - tautology
4
+ - contradiction
5
+ - identifier
6
+ - not
7
+ - and
8
+ - or
9
+ - eq
10
+ - neq
11
+ - lt
12
+ - lte
13
+ - gt
14
+ - gte
15
+ - in
16
+ - native
17
+ tautology:
18
+ - [ true ]
19
+ contradiction:
20
+ - [ false ]
21
+ identifier:
22
+ - [ name ]
23
+ qualified_identifier:
24
+ - [ name, name ]
25
+ not:
26
+ - [ predicate ]
27
+ and:
28
+ - [ predicate+ ]
29
+ or:
30
+ - [ predicate+ ]
31
+ eq:
32
+ - [ term, term ]
33
+ neq:
34
+ - [ term, term ]
35
+ lt:
36
+ - [ term, term ]
37
+ lte:
38
+ - [ term, term ]
39
+ gt:
40
+ - [ term, term ]
41
+ gte:
42
+ - [ term, term ]
43
+ in:
44
+ - [ varref, values ]
45
+ term:
46
+ - varref
47
+ - literal
48
+ varref:
49
+ - qualified_identifier
50
+ - identifier
51
+ native:
52
+ - [ "::Proc" ]
53
+ literal:
54
+ - "::Object"
55
+ values:
56
+ - "::Object"
57
+ name:
58
+ !ruby/regexp /^[a-zA-Z0-9_]+[?!]?$/
@@ -0,0 +1,23 @@
1
+ class Predicate
2
+ module And
3
+ include NadicBool
4
+
5
+ def operator_symbol
6
+ :'&&'
7
+ end
8
+
9
+ def and_split(attr_list)
10
+ sexpr_body.inject([tautology, tautology]) do |(top,down),term|
11
+ pair = term.and_split(attr_list)
12
+ [top & pair.first, down & pair.last]
13
+ end
14
+ end
15
+
16
+ def constant_variables
17
+ sexpr_body.inject([]) do |cvars,expr|
18
+ cvars | expr.constant_variables
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ class Predicate
2
+ module Contradiction
3
+ include Expr
4
+
5
+ def contradiction?
6
+ true
7
+ end
8
+
9
+ def !
10
+ tautology
11
+ end
12
+
13
+ def &(other)
14
+ self
15
+ end
16
+
17
+ def |(other)
18
+ other
19
+ end
20
+
21
+ def priority
22
+ 100
23
+ end
24
+
25
+ def free_variables
26
+ @free_variables ||= []
27
+ end
28
+
29
+ def and_split(*args)
30
+ [tautology, self]
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ class Predicate
2
+ module DyadicComp
3
+ include Expr
4
+
5
+ def priority
6
+ 50
7
+ end
8
+
9
+ def !
10
+ Factory.send(OP_NEGATIONS[first], self[1], self[2])
11
+ end
12
+
13
+ def left
14
+ self[1]
15
+ end
16
+
17
+ def right
18
+ self[2]
19
+ end
20
+
21
+ def free_variables
22
+ @free_variables ||= left.free_variables | right.free_variables
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ class Predicate
2
+ module Eq
3
+ include DyadicComp
4
+
5
+ def operator_symbol
6
+ :==
7
+ end
8
+
9
+ def constant_variables
10
+ fv = free_variables
11
+ fv.size == 1 ? fv : []
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,68 @@
1
+ class Predicate
2
+ module Expr
3
+ include Factory
4
+
5
+ OP_NEGATIONS = {
6
+ :eq => :neq,
7
+ :neq => :eq,
8
+ :lt => :gte,
9
+ :lte => :gt,
10
+ :gt => :lte,
11
+ :gte => :lt
12
+ }
13
+
14
+ def tautology?
15
+ false
16
+ end
17
+
18
+ def contradiction?
19
+ false
20
+ end
21
+
22
+ def !
23
+ sexpr([:not, self])
24
+ end
25
+
26
+ def &(other)
27
+ return other if other.contradiction?
28
+ return self if other.tautology?
29
+ sexpr([:and, self, other])
30
+ end
31
+
32
+ def |(other)
33
+ return other if other.tautology?
34
+ return self if other.contradiction?
35
+ sexpr([:or, self, other])
36
+ end
37
+
38
+ def and_split(attr_list)
39
+ (free_variables & attr_list).empty? ? [ tautology, self ] : [ self, tautology ]
40
+ end
41
+
42
+ def rename(renaming)
43
+ Renamer.call(self, :renaming => renaming)
44
+ end
45
+
46
+ def qualify(qualifier)
47
+ Qualifier.new(qualifier).call(self)
48
+ end
49
+
50
+ def constant_variables
51
+ []
52
+ end
53
+
54
+ def to_ruby_code(scope = 't')
55
+ code = ToRubyCode.call(self, scope: scope)
56
+ "->(t){ #{code} }"
57
+ end
58
+
59
+ def to_proc(scope = 't')
60
+ Kernel.eval(to_ruby_code(scope))
61
+ end
62
+
63
+ def sexpr(arg)
64
+ Factory.sexpr(arg)
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,10 @@
1
+ class Predicate
2
+ module Gt
3
+ include DyadicComp
4
+
5
+ def operator_symbol
6
+ :>
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ class Predicate
2
+ module Gte
3
+ include DyadicComp
4
+
5
+ def operator_symbol
6
+ :>=
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ class Predicate
2
+ module Identifier
3
+ include Expr
4
+
5
+ def priority
6
+ 100
7
+ end
8
+
9
+ def name
10
+ self[1]
11
+ end
12
+
13
+ def free_variables
14
+ @free_variables ||= [ name ]
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ class Predicate
2
+ module In
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
+ values.size == 1 ? free_variables : []
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ class Predicate
2
+ module Literal
3
+ include Expr
4
+
5
+ def priority
6
+ 100
7
+ end
8
+
9
+ def free_variables
10
+ @free_variables ||= []
11
+ end
12
+
13
+ def value
14
+ last
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ class Predicate
2
+ module Lt
3
+ include DyadicComp
4
+
5
+ def operator_symbol
6
+ :<
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ class Predicate
2
+ module Lte
3
+ include DyadicComp
4
+
5
+ def operator_symbol
6
+ :<=
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ class Predicate
2
+ module NadicBool
3
+ include Expr
4
+
5
+ def priority
6
+ 60
7
+ end
8
+
9
+ def free_variables
10
+ @free_variables ||= sexpr_body.inject([]){|list,term|
11
+ list | term.free_variables
12
+ }
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ class Predicate
2
+ module Native
3
+ include Expr
4
+
5
+ def priority
6
+ 90
7
+ end
8
+
9
+ def proc
10
+ self[1]
11
+ end
12
+
13
+ def to_ruby_code(scope = 't')
14
+ if proc.respond_to?(:source_code)
15
+ code = proc.source_code
16
+ return code if code
17
+ end
18
+ raise NotSupportedError
19
+ end
20
+
21
+ def to_proc(scope = 't')
22
+ proc
23
+ end
24
+
25
+ def free_variables
26
+ raise NotSupportedError
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ class Predicate
2
+ module Neq
3
+ include DyadicComp
4
+
5
+ def operator_symbol
6
+ :'!='
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ class Predicate
2
+ module Not
3
+ include Expr
4
+
5
+ def operator_symbol
6
+ :'!'
7
+ end
8
+
9
+ def priority
10
+ 90
11
+ end
12
+
13
+ def !
14
+ last
15
+ end
16
+
17
+ def free_variables
18
+ @free_variables ||= last.free_variables
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ class Predicate
2
+ module Or
3
+ include NadicBool
4
+
5
+ def operator_symbol
6
+ :'||'
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ class Predicate
2
+ module QualifiedIdentifier
3
+ include Expr
4
+
5
+ def priority
6
+ 100
7
+ end
8
+
9
+ def qualifier
10
+ self[1]
11
+ end
12
+
13
+ def name
14
+ self[2]
15
+ end
16
+
17
+ def free_variables
18
+ @free_variables ||= [ name ]
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ class Predicate
2
+ module Tautology
3
+ include Expr
4
+
5
+ def tautology?
6
+ true
7
+ end
8
+
9
+ def !
10
+ contradiction
11
+ end
12
+
13
+ def &(other)
14
+ other
15
+ end
16
+
17
+ def |(other)
18
+ self
19
+ end
20
+
21
+ def priority
22
+ 100
23
+ end
24
+
25
+ def free_variables
26
+ @free_variables ||= []
27
+ end
28
+
29
+ end
30
+ end