mini_kraken 0.1.01 → 0.1.02

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/README.md +0 -3
  4. data/lib/mini_kraken/core/any_value.rb +29 -0
  5. data/lib/mini_kraken/core/association.rb +21 -0
  6. data/lib/mini_kraken/core/association_walker.rb +179 -0
  7. data/lib/mini_kraken/core/atomic_term.rb +64 -0
  8. data/lib/mini_kraken/core/binary_relation.rb +61 -0
  9. data/lib/mini_kraken/core/composite_term.rb +54 -0
  10. data/lib/mini_kraken/core/cons_cell.rb +44 -0
  11. data/lib/mini_kraken/core/duck_fiber.rb +44 -0
  12. data/lib/mini_kraken/core/environment.rb +59 -0
  13. data/lib/mini_kraken/core/equals.rb +216 -0
  14. data/lib/mini_kraken/core/fail.rb +8 -5
  15. data/lib/mini_kraken/core/freshness.rb +42 -0
  16. data/lib/mini_kraken/core/goal.rb +31 -6
  17. data/lib/mini_kraken/core/k_integer.rb +15 -0
  18. data/lib/mini_kraken/core/k_symbol.rb +15 -0
  19. data/lib/mini_kraken/core/nullary_relation.rb +11 -3
  20. data/lib/mini_kraken/core/outcome.rb +35 -0
  21. data/lib/mini_kraken/core/relation.rb +31 -4
  22. data/lib/mini_kraken/core/succeed.rb +13 -1
  23. data/lib/mini_kraken/core/term.rb +7 -0
  24. data/lib/mini_kraken/core/variable.rb +45 -3
  25. data/lib/mini_kraken/core/variable_ref.rb +76 -0
  26. data/lib/mini_kraken/core/vocabulary.rb +161 -0
  27. data/lib/mini_kraken/glue/fresh_env.rb +31 -0
  28. data/lib/mini_kraken/glue/run_star_expression.rb +43 -0
  29. data/lib/mini_kraken/version.rb +1 -1
  30. data/spec/core/association_spec.rb +38 -0
  31. data/spec/core/association_walker_spec.rb +191 -0
  32. data/spec/core/cons_cell_spec.rb +63 -0
  33. data/spec/core/duck_fiber_spec.rb +62 -0
  34. data/spec/core/environment_spec.rb +154 -0
  35. data/spec/core/equals_spec.rb +289 -0
  36. data/spec/core/fail_spec.rb +16 -0
  37. data/spec/core/goal_spec.rb +36 -10
  38. data/spec/core/k_symbol_spec.rb +72 -0
  39. data/spec/core/succeed_spec.rb +43 -0
  40. data/spec/core/variable_ref_spec.rb +31 -0
  41. data/spec/core/variable_spec.rb +11 -3
  42. data/spec/core/vocabulary_spec.rb +188 -0
  43. data/spec/glue/fresh_env_spec.rb +36 -0
  44. data/spec/glue/run_star_expression_spec.rb +247 -0
  45. data/spec/support/factory_methods.rb +54 -0
  46. metadata +46 -13
  47. data/lib/mini_kraken/core/facade.rb +0 -45
  48. data/lib/mini_kraken/core/formal_arg.rb +0 -6
  49. data/lib/mini_kraken/core/publisher.rb +0 -27
  50. data/lib/mini_kraken/core/run_star_expression.rb +0 -34
  51. data/lib/mini_kraken/dsl/kraken_dsl.rb +0 -12
  52. data/spec/core/facade_spec.rb +0 -38
  53. data/spec/core/run_star_expression_spec.rb +0 -43
  54. data/spec/dsl/kraken_dsl_spec.rb +0 -31
@@ -0,0 +1,15 @@
1
+ require_relative 'atomic_term'
2
+
3
+ module MiniKraken
4
+ module Core
5
+ # A specialized atomic term that represents an integer value.
6
+ # in MiniKraken
7
+ class KInteger < AtomicTerm
8
+
9
+ # @param aValue [Integer] Ruby representation of integer value
10
+ def initialize(aValue)
11
+ super(aValue)
12
+ end
13
+ end # class
14
+ end # module
15
+ end # module
@@ -0,0 +1,15 @@
1
+ require_relative 'atomic_term'
2
+
3
+ module MiniKraken
4
+ module Core
5
+ # A specialized atomic term that represents a symbolic value.
6
+ # in MiniKraken
7
+ class KSymbol < AtomicTerm
8
+
9
+ # @param aValue [Symbol] Ruby representation of symbol value
10
+ def initialize(aValue)
11
+ super(aValue)
12
+ end
13
+ end # class
14
+ end # module
15
+ end # module
@@ -3,9 +3,17 @@ require_relative 'relation'
3
3
  module MiniKraken
4
4
  module Core
5
5
  class NullaryRelation < Relation
6
- def initialize(aName)
7
- super(aName)
8
- @tuple.freeze
6
+ # @param aName [String] Name of the relation.
7
+ # @param alternateName [String, NilClass] Alternative name (optional).
8
+ def initialize(aName, alternateName = nil)
9
+ super(aName, alternateName)
10
+ freeze
11
+ end
12
+
13
+ # Number of arguments for the relation.
14
+ # @return [Integer]
15
+ def arity
16
+ 0
9
17
  end
10
18
  end # class
11
19
  end # module
@@ -0,0 +1,35 @@
1
+ require_relative 'vocabulary'
2
+
3
+ module MiniKraken
4
+ module Core
5
+ class Outcome
6
+ include Vocabulary # Use mix-in module
7
+
8
+ # @return [Symbol] One of: :"#s" (success), :"#u" (failure)
9
+ attr_reader :resultant
10
+
11
+ def initialize(aResult, aParent = nil)
12
+ init_vocabulary(aParent)
13
+ @resultant = aResult
14
+ end
15
+
16
+ def successful?
17
+ self.resultant == :"#s"
18
+ end
19
+
20
+ def ==(other)
21
+ are_equal = false
22
+
23
+ if resultant == other.resultant && parent == other.parent &&
24
+ associations == other.associations
25
+ are_equal = true
26
+ end
27
+
28
+ are_equal
29
+ end
30
+ end # class
31
+
32
+ Failure = Outcome.new(:"#u")
33
+ BasicSuccess = Outcome.new(:"#s")
34
+ end # module
35
+ end # module
@@ -1,16 +1,43 @@
1
1
  module MiniKraken
2
2
  module Core
3
3
  class Relation
4
+ # @return [String] Name of the relation.
4
5
  attr_reader :name
5
- attr_reader :tuple
6
6
 
7
- def initialize(aName)
7
+ # @return [String, NilClass] Optional alternative name of the relation.
8
+ attr_reader :alt_name
9
+
10
+ # @param aName [String] Name of the relation.
11
+ # @param alternateName [String, NilClass] Alternative name (optional).
12
+ def initialize(aName, alternateName = nil)
8
13
  @name = aName
9
- @tuple = []
14
+ @alt_name = alternateName
10
15
  end
11
16
 
17
+ # Number of arguments for the relation.
18
+ # @return [Integer]
12
19
  def arity
13
- tuple.size
20
+ raise NotImplementedError
21
+ end
22
+
23
+ # Attempt to achieve the goal for a given context (environment)
24
+ # @param anEnv [Environment] The context in which the goal take place.
25
+ # @return [Fiber<Outcome>] A Fiber object that will generate the results.
26
+ # def solve(args, anEnv)
27
+ # Fiber instance responds to resume(*args) message
28
+ # If too much resume calls => FiberError: dead fiber called message.
29
+
30
+ # Fiber.new do |first_yield_arg| do
31
+ # begin
32
+ # result = relation.solve(actuals, anEnv)
33
+ # Fiber.yield result
34
+ # while result.success?
35
+
36
+ nil
37
+ # end
38
+
39
+ def inspect
40
+ alt_name ? alt_name : name
14
41
  end
15
42
  end # class
16
43
  end # module
@@ -1,8 +1,20 @@
1
+ require 'singleton'
2
+ require_relative 'duck_fiber'
1
3
  require_relative 'nullary_relation'
2
4
 
3
5
  module MiniKraken
4
6
  module Core
7
+ # A nullary relation that unconditionally always fails.
5
8
  class Succeed < NullaryRelation
9
+ include Singleton
10
+
11
+ def initialize
12
+ super('succeed', '#s')
13
+ end
14
+
15
+ def solver_for(_actuals, _env)
16
+ DuckFiber.new(:success)
17
+ end
6
18
  end # class
7
19
  end # module
8
- end # module
20
+ end # module
@@ -0,0 +1,7 @@
1
+ module MiniKraken
2
+ module Core
3
+ # The generalization of data value in MiniKraken
4
+ class Term
5
+ end # class
6
+ end # module
7
+ end # module
@@ -1,11 +1,53 @@
1
+ require_relative 'any_value'
2
+ require_relative 'vocabulary'
3
+
1
4
  module MiniKraken
2
5
  module Core
6
+ # Representation of a MiniKraken variable.
7
+ # It is a named slot that can be associated with one value.
3
8
  class Variable
9
+ # @return [String] Name of the variable
4
10
  attr_reader :name
5
-
11
+
12
+ # @param aName [String] The name of the variable
6
13
  def initialize(aName)
7
- @name = aName
14
+ @name = valid_name(aName)
15
+ end
16
+
17
+ def fresh?(anEnvironment)
18
+ anEnvironment.fresh?(self)
19
+ end
20
+
21
+ # @param env [Environment]
22
+ # @return [Freshness]
23
+ def freshness(env)
24
+ freshness = env.freshness_ref(self)
25
+ end
26
+
27
+ def ground?(anEnvironment)
28
+ !fresh?(anEnvironment)
29
+ end
30
+
31
+ def quote(anEnvironment)
32
+ # raise StandardError, "class #{anEnvironment}" unless anEnvironment.kind_of?(Vocabulary)
33
+ # freshness = anEnvironment.freshness_ref(self)
34
+ # raise StandardError, "class #{freshness}" unless freshness.kind_of?(Freshness)
35
+ # raise StandardError, "class #{freshness.associated}" if freshness.associated.kind_of?(Freshness)
36
+ # freshness.fresh? ? AnyValue.new(0) : freshness.associated.quote(anEnvironment)
37
+
38
+ val = anEnvironment.quote_ref(self)
39
+ val.nil? ? AnyValue.new(0) : val
40
+ end
41
+
42
+ private
43
+
44
+ def valid_name(aName)
45
+ if aName.empty?
46
+ raise StandardError, "Variable name may not be empty."
47
+ end
48
+
49
+ aName
8
50
  end
9
51
  end # class
10
52
  end # module
11
- end # module
53
+ end # module
@@ -0,0 +1,76 @@
1
+ require_relative 'term'
2
+ require_relative 'any_value'
3
+ require_relative 'association'
4
+
5
+ module MiniKraken
6
+ module Core
7
+ # A variable reference represents the occurrence of a variable (name) in a
8
+ # MiniKraken term.
9
+ class VariableRef < Term
10
+ # @return [String] Name of the variable
11
+ attr_reader :var_name
12
+
13
+ # @param aName [String] The name of the variable
14
+ def initialize(aName)
15
+ @var_name = valid_name(aName)
16
+ end
17
+
18
+ # @param env [Environment]
19
+ # @return [Boolean]
20
+ def fresh?(env)
21
+ env.fresh?(self)
22
+ end
23
+
24
+ # @param env [Environment]
25
+ # @return [Boolean]
26
+ def ground?(env)
27
+ !fresh?(env)
28
+ end
29
+
30
+ # @param aValue [Term]
31
+ # @param env [Environment]
32
+ def associate(aValue, env)
33
+ assoc = Association.new(var_name, aValue)
34
+ env.add_assoc(assoc)
35
+ end
36
+
37
+ # @param env [Environment]
38
+ # @return [Array<Term>]
39
+ def values(env)
40
+ env[var_name].map(&:value)
41
+ end
42
+
43
+ # @param env [Environment]
44
+ # @return [Term, NilClass]
45
+ def value(env)
46
+ freshness = env.freshness_ref(self)
47
+ freshness.associated
48
+ end
49
+
50
+ # @param env [Environment]
51
+ # @return [Freshness]
52
+ def freshness(env)
53
+ freshness = env.freshness_ref(self)
54
+ end
55
+
56
+
57
+ # @param env [Environment]
58
+ def quote(env)
59
+ val = env.quote_ref(self)
60
+ val.nil? ? AnyValue.new(0) : val
61
+ end
62
+
63
+ private
64
+
65
+ def valid_name(aName)
66
+ if aName.empty?
67
+ raise StandardError, "Variable name may not be empty."
68
+ end
69
+
70
+ aName
71
+ end
72
+
73
+
74
+ end # class
75
+ end # module
76
+ end # module
@@ -0,0 +1,161 @@
1
+ require_relative 'association_walker'
2
+
3
+ module MiniKraken
4
+ module Core
5
+ module Vocabulary
6
+ # @return [Environment] Parent environment to this one.
7
+ attr_accessor :parent
8
+
9
+ # @return [Hash] Pairs of the kind {String => Array[Association]}
10
+ attr_reader :associations
11
+
12
+ # @param aParent [Environment, NilClass] Parent environment to this one.
13
+ def init_vocabulary(aParent = nil)
14
+ @parent = validated_parent(aParent)
15
+ @associations = {}
16
+ end
17
+
18
+ # Return a Fiber object that can iterate over this vocabulary and
19
+ # all its direct and indirect parent(s).
20
+ # @return [Fiber<Vocabulary, NilClass>]
21
+ def ancestor_walker
22
+ Fiber.new do
23
+ relative = self
24
+ while relative do
25
+ Fiber.yield relative
26
+ relative = relative.parent
27
+ end
28
+
29
+ Fiber.yield nil # nil marks end of iteration...
30
+ end
31
+ end
32
+
33
+ # Record an association between a variable with given name and a term.
34
+ # @param anAssociation [Association]
35
+ def add_assoc(anAssociation)
36
+ name = anAssociation.var_name
37
+ unless include?(name)
38
+ err_msg = "Unknown variable '#{name}'."
39
+ raise StandardError, err_msg
40
+ end
41
+ found_assocs = associations[name]
42
+ if found_assocs
43
+ found_assocs << anAssociation
44
+ else
45
+ associations[name] = [anAssociation]
46
+ end
47
+ end
48
+
49
+ # Handler for the event: an outcome has been produced.
50
+ # Can be overridden in other to propagate associations from child
51
+ # @param _descendent [Outcome]
52
+ def propagate(_descendent)
53
+ #Do nothing...
54
+ end
55
+
56
+ # Remove all the associations.
57
+ def clear
58
+ associations.clear
59
+ end
60
+
61
+ # Merge the associations from another vocabulary-like object.
62
+ # @param another [Vocabulary]
63
+ def merge(another)
64
+ another.associations.each_pair do |_name, assocs|
65
+ assocs.each { |a| add_assoc(a) }
66
+ end
67
+ end
68
+
69
+ # @param var [Variable, VariableRef] the variable to check.
70
+ # @return [Boolean]
71
+ def fresh?(var)
72
+ ground_term = ground_value(var)
73
+ ground_term.nil? ? true : false
74
+ end
75
+
76
+ # @param var [Variable, VariableRef] variable for which the value to retrieve
77
+ # @return [Term, NilClase]
78
+ def ground_value(var)
79
+ name = var.respond_to?(:var_name) ? var.var_name : var.name
80
+
81
+ walker = AssociationWalker.new
82
+ walker.find_ground(name, self)
83
+ end
84
+
85
+ # @param var [CompositeTerm] the composite term to check.
86
+ # @return [Boolean]
87
+ def fresh_value?(val)
88
+ walker = AssociationWalker.new
89
+ ground_term = walker.walk_value(val, self)
90
+ ground_term.nil? ? true : false
91
+ end
92
+
93
+ # A composite term is fresh when all its members are nil or all non-nil members
94
+ # are all fresh
95
+ # A composite term is bound when it is not fresh and not ground
96
+ # A composite term is a ground term when all its non-nil members are ground.
97
+ # @param aComposite [CompositeTerm]
98
+ # @return [Freshness]
99
+ def freshness_composite(aComposite)
100
+ walker = AssociationWalker.new
101
+ walker.freshness_composite(aComposite)
102
+ end
103
+
104
+ # Determine whether the reference points to a fresh, bound or ground term.
105
+ # @param aVariableRef [VariableRef]
106
+ # @return [Freshness]
107
+ def freshness_ref(aVariableRef)
108
+ walker = AssociationWalker.new
109
+ walker.determine_freshness(aVariableRef, self)
110
+ end
111
+
112
+ # @param aVariableRef [VariableRef]
113
+ # @return [Term, NilClass]
114
+ def quote_ref(aVariableRef)
115
+ walker = AssociationWalker.new
116
+ walker.quote_term(aVariableRef, self)
117
+ end
118
+
119
+ # @param aName [String]
120
+ # @return [Array<Association>]
121
+ def [](aName)
122
+ assoc_arr = associations[aName]
123
+ assoc_arr = [] if assoc_arr.nil?
124
+
125
+ assoc_arr.concat(parent[aName]) if parent
126
+ assoc_arr
127
+ end
128
+
129
+ # Check that a variable with given name is defined in this vocabulary
130
+ # of one of its ancestor.
131
+ # @return [Boolean]
132
+ def include?(aVarName)
133
+ var_found = false
134
+ walker = ancestor_walker
135
+ loop do
136
+ voc = walker.resume
137
+ if voc
138
+ next unless voc.respond_to?(:vars) && voc.vars.include?(aVarName)
139
+ var_found = true
140
+ end
141
+
142
+ break
143
+ end
144
+
145
+ var_found
146
+ end
147
+
148
+ protected
149
+
150
+ def validated_parent(aParent)
151
+ if aParent
152
+ unless aParent.kind_of?(Vocabulary)
153
+ raise StandardError, "Invalid parent type #{aParent.class}"
154
+ end
155
+ end
156
+
157
+ aParent
158
+ end
159
+ end # class
160
+ end # module
161
+ end # module