predicate 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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