mini_kraken 0.1.03 → 0.1.08

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -1
  3. data/CHANGELOG.md +54 -3
  4. data/Gemfile +3 -1
  5. data/README.md +22 -1
  6. data/Rakefile +5 -3
  7. data/lib/mini_kraken.rb +3 -1
  8. data/lib/mini_kraken/core/any_value.rb +9 -7
  9. data/lib/mini_kraken/core/association.rb +20 -7
  10. data/lib/mini_kraken/core/association_walker.rb +5 -1
  11. data/lib/mini_kraken/core/atomic_term.rb +5 -3
  12. data/lib/mini_kraken/core/binary_relation.rb +8 -6
  13. data/lib/mini_kraken/core/composite_goal.rb +46 -0
  14. data/lib/mini_kraken/core/composite_term.rb +7 -20
  15. data/lib/mini_kraken/core/conj2.rb +77 -0
  16. data/lib/mini_kraken/core/cons_cell.rb +51 -41
  17. data/lib/mini_kraken/core/designation.rb +55 -0
  18. data/lib/mini_kraken/core/disj2.rb +71 -0
  19. data/lib/mini_kraken/core/duck_fiber.rb +4 -2
  20. data/lib/mini_kraken/core/environment.rb +25 -11
  21. data/lib/mini_kraken/core/equals.rb +128 -189
  22. data/lib/mini_kraken/core/fail.rb +20 -14
  23. data/lib/mini_kraken/core/freshness.rb +11 -8
  24. data/lib/mini_kraken/core/goal.rb +8 -4
  25. data/lib/mini_kraken/core/goal_arg.rb +10 -0
  26. data/lib/mini_kraken/core/goal_relation.rb +28 -0
  27. data/lib/mini_kraken/core/k_integer.rb +4 -3
  28. data/lib/mini_kraken/core/k_symbol.rb +4 -3
  29. data/lib/mini_kraken/core/nullary_relation.rb +3 -1
  30. data/lib/mini_kraken/core/outcome.rb +29 -25
  31. data/lib/mini_kraken/core/relation.rb +4 -18
  32. data/lib/mini_kraken/core/succeed.rb +20 -14
  33. data/lib/mini_kraken/core/term.rb +7 -2
  34. data/lib/mini_kraken/core/variable.rb +11 -25
  35. data/lib/mini_kraken/core/variable_ref.rb +12 -59
  36. data/lib/mini_kraken/core/vocabulary.rb +267 -48
  37. data/lib/mini_kraken/glue/fresh_env.rb +5 -3
  38. data/lib/mini_kraken/glue/run_star_expression.rb +18 -8
  39. data/lib/mini_kraken/version.rb +3 -1
  40. data/mini_kraken.gemspec +15 -13
  41. data/spec/core/association_spec.rb +4 -4
  42. data/spec/core/association_walker_spec.rb +25 -24
  43. data/spec/core/conj2_spec.rb +114 -0
  44. data/spec/core/cons_cell_spec.rb +12 -3
  45. data/spec/core/disj2_spec.rb +99 -0
  46. data/spec/core/duck_fiber_spec.rb +22 -12
  47. data/spec/core/environment_spec.rb +16 -28
  48. data/spec/core/equals_spec.rb +7 -7
  49. data/spec/core/fail_spec.rb +7 -7
  50. data/spec/core/goal_spec.rb +10 -10
  51. data/spec/core/k_symbol_spec.rb +5 -6
  52. data/spec/core/succeed_spec.rb +4 -4
  53. data/spec/core/variable_ref_spec.rb +0 -4
  54. data/spec/core/vocabulary_spec.rb +33 -27
  55. data/spec/glue/fresh_env_spec.rb +1 -1
  56. data/spec/glue/run_star_expression_spec.rb +213 -60
  57. data/spec/mini_kraken_spec.rb +4 -0
  58. data/spec/spec_helper.rb +3 -2
  59. data/spec/support/factory_methods.rb +20 -2
  60. metadata +12 -2
@@ -1,44 +1,54 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'composite_term'
2
4
 
3
- module MiniKraken
4
- module Core
5
- class ConsCell < CompositeTerm
6
- attr_reader :car
7
- attr_reader :cdr
8
-
9
- def initialize(obj1, obj2 = nil)
10
- @car = obj1
11
- @cdr = obj2
12
- end
13
-
14
- def children
15
- [car, cdr]
16
- end
17
-
18
- # Return true if it is an empty list, otherwise false.
19
- # A list is empty, when both car and cdr fields are nil.
20
- def null?
21
- car.nil? && cdr.nil?
22
- end
23
-
24
- def ==(other)
25
- return false unless other.respond_to?(:car)
26
- (car == other.car) && (cdr == other.cdr)
27
- end
28
-
29
- def eql?(other)
30
- (self.class == other.class) && car.eql?(other.car) && cdr.eql?(other.cdr)
31
- end
32
-
33
- def quote(anEnv)
34
- return self if null?
35
- new_car = car.nil? ? nil : car.quote(anEnv)
36
- new_cdr = cdr.nil? ? nil : cdr.quote(anEnv)
37
- ConsCell.new(new_car, new_cdr)
38
- end
39
- end # class
40
-
41
- # Constant representing the null (empty) list.
42
- NullList = ConsCell.new(nil, nil).freeze
5
+ unless MiniKraken::Core.constants(false).include? :ConsCell
6
+ module MiniKraken
7
+ module Core
8
+ class ConsCell < CompositeTerm
9
+ attr_reader :car
10
+ attr_reader :cdr
11
+
12
+ def initialize(obj1, obj2 = nil)
13
+ @car = obj1
14
+ @cdr = obj2
15
+ end
16
+
17
+ def children
18
+ [car, cdr]
19
+ end
20
+
21
+ # Return true if it is an empty list, otherwise false.
22
+ # A list is empty, when both car and cdr fields are nil.
23
+ def null?
24
+ car.nil? && cdr.nil?
25
+ end
26
+
27
+ def ==(other)
28
+ return false unless other.respond_to?(:car)
29
+
30
+ (car == other.car) && (cdr == other.cdr)
31
+ end
32
+
33
+ def eql?(other)
34
+ (self.class == other.class) && car.eql?(other.car) && cdr.eql?(other.cdr)
35
+ end
36
+
37
+ def quote(anEnv)
38
+ return self if null?
39
+
40
+ new_car = car.nil? ? nil : car.quote(anEnv)
41
+ new_cdr = cdr.nil? ? nil : cdr.quote(anEnv)
42
+ ConsCell.new(new_car, new_cdr)
43
+ end
44
+
45
+ def append(another)
46
+ @cdr = another
47
+ end
48
+ end # class
49
+
50
+ # Constant representing the null (empty) list.
51
+ NullList = ConsCell.new(nil, nil).freeze
52
+ end # module
43
53
  end # module
44
- end # module
54
+ end # defined
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniKraken
4
+ module Core
5
+ # Wordnet definition: Identifying word or words by which someone or
6
+ # something is called and classified or distinguished from others.
7
+ # Mix-in module that contains factored code for managing named entries
8
+ # in a vocabulary such as variables and variable references.
9
+ module Designation
10
+ # @return [String] User-defined name of the variable
11
+ attr_reader :name
12
+
13
+ def init_designation(aName)
14
+ @name = valid_name(aName)
15
+ end
16
+
17
+ # @param voc [Vocabulary]
18
+ # @return [Freshness]
19
+ def freshness(voc)
20
+ voc.freshness_ref(self)
21
+ end
22
+
23
+ # @param voc [Vocabulary]
24
+ # @return [Boolean]
25
+ def fresh?(voc)
26
+ frsh = freshness(voc)
27
+ frsh.degree == :fresh || frsh.degree == :bound
28
+ end
29
+
30
+ # @param voc [Vocabulary]
31
+ # @return [Boolean]
32
+ def bound?(voc)
33
+ frsh = freshness(voc)
34
+ frsh.degree == :bound
35
+ end
36
+
37
+ # @param voc [Vocabulary]
38
+ # @return [Boolean]
39
+ def ground?(voc)
40
+ frsh = freshness(voc)
41
+ frsh.degree == :bound
42
+ end
43
+
44
+ private
45
+
46
+ def valid_name(aName)
47
+ if aName.empty?
48
+ raise StandardError, 'Variable name may not be empty.'
49
+ end
50
+
51
+ aName
52
+ end
53
+ end # class
54
+ end # module
55
+ end # module
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative 'duck_fiber'
5
+ require_relative 'goal'
6
+ require_relative 'goal_relation'
7
+ require_relative 'outcome'
8
+
9
+ unless MiniKraken::Core.constants(false).include? :Disj2
10
+ module MiniKraken
11
+ module Core
12
+ # The disjunction is a relation that accepts only goal(s) as its two
13
+ # arguments. It succeeds if at least one of its goal arguments succeeds.
14
+ class Disj2 < GoalRelation
15
+ include Singleton
16
+
17
+ def initialize
18
+ super('disj2', nil)
19
+ end
20
+
21
+ # @param actuals [Array<Term>] A two-elements array
22
+ # @param anEnv [Vocabulary] A vocabulary object
23
+ # @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
24
+ def solver_for(actuals, anEnv)
25
+ g1, g2 = *validated_args(actuals)
26
+ Fiber.new { disjunction(g1, g2, anEnv) }
27
+ end
28
+
29
+ # Yields [Outcome, NilClass] result of the disjunction
30
+ # @param g1 [Goal] First goal argument
31
+ # @param g2 [Goal] Second goal argument
32
+ # @param voc [Vocabulary] A vocabulary object
33
+ def disjunction(g1, g2, voc)
34
+ # require 'debug'
35
+ outcome1 = nil
36
+ outcome2 = nil
37
+ if g1.relation.kind_of?(Fail) && g2.relation.kind_of?(Fail)
38
+ Fiber.yield Outcome.new(:"#u", voc)
39
+ else
40
+ f1 = g1.attain(voc)
41
+ loop do
42
+ outcome1 = f1.resume
43
+ break unless outcome1
44
+
45
+ outcome1.parent = voc unless outcome1.parent
46
+ if outcome1.successful?
47
+ Fiber.yield outcome1
48
+ outcome1.clear
49
+ end
50
+ end
51
+ f2 = g2.attain(voc)
52
+ loop do
53
+ outcome2 = f2.resume
54
+ break unless outcome2
55
+
56
+ outcome2.parent = voc unless outcome2.parent
57
+ if outcome2.successful?
58
+ Fiber.yield outcome2
59
+ outcome2.clear
60
+ end
61
+ end
62
+ end
63
+
64
+ Fiber.yield nil
65
+ end
66
+ end # class
67
+
68
+ Disj2.instance.freeze
69
+ end # module
70
+ end # module
71
+ end # unless
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'outcome'
2
4
 
3
5
  module MiniKraken
@@ -28,13 +30,13 @@ module MiniKraken
28
30
  return nil
29
31
  end
30
32
  end
31
-
33
+
32
34
  def valid_outcome(outcomeKind)
33
35
  case outcomeKind
34
36
  when :failure
35
37
  Failure
36
38
  when :success
37
- BasicSuccess
39
+ Outcome.new(:"#s")
38
40
  else
39
41
  raise StandardError, "Unknonw outcome kind #{outcomeKind}"
40
42
  end
@@ -1,17 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
1
4
  require_relative 'vocabulary'
2
5
 
3
6
  module MiniKraken
4
7
  module Core
5
8
  class Environment
6
- include Vocabulary # Use mix-in module
9
+ include Vocabulary # Use mix-in module
7
10
 
11
+ # Mapping from user-defined name to Variable instance
8
12
  # @return [Hash] Pairs of the kind {String => Variable}
9
13
  attr_reader :vars
10
14
 
15
+ # Mapping from internal name to user-defined name(s)
16
+ # @return [Hash] Pairs of the kind {String => Set<String>}
17
+ attr_reader :ivars
18
+
11
19
  # @param aParent [Environment, NilClass] Parent environment to this one.
12
20
  def initialize(aParent = nil)
13
21
  init_vocabulary(aParent)
14
22
  @vars = {}
23
+ @ivars = {}
15
24
  end
16
25
 
17
26
  # @param aVariable [Variable]
@@ -22,6 +31,13 @@ module MiniKraken
22
31
  raise StandardError, err_msg
23
32
  end
24
33
  vars[name] = aVariable
34
+ i_name = aVariable.i_name
35
+ if ivars.include?(i_name)
36
+ set = ivars[i_name]
37
+ set.add(name)
38
+ else
39
+ ivars[i_name] = Set.new([i_name])
40
+ end
25
41
  end
26
42
 
27
43
  # Handler for the event: an outcome has been produced.
@@ -31,23 +47,21 @@ module MiniKraken
31
47
  # Rollout associations from hierarchy
32
48
  walker = descendent.ancestor_walker
33
49
  begin
34
- env = walker.resume
50
+ env = walker.next
35
51
  break if env.nil?
36
- env.do_propagate(descendent) if env.kind_of?(Environment)
52
+
53
+ env.do_propagate(descendent) if env.kind_of?(Environment)
37
54
  end until env.equal?(self)
38
55
  end
39
56
 
40
- # Move associations from descendent outcome object
57
+ # Roll up associations from descendent outcome object
58
+ # @param descendent [Outcome]
41
59
  def do_propagate(descendent)
42
- return unless descendent.successful?
60
+ return unless descendent.successful?
43
61
 
44
62
  vars.each_key do |var_name|
45
63
  assocs = descendent[var_name]
46
- assocs.each do |assoc|
47
- own = self[var_name]
48
- add_assoc(assoc) unless assoc.equal?(own)
49
- end
50
- descendent.associations.delete(var_name) unless assocs.empty?
64
+ move_assocs(var_name, descendent)
51
65
  end
52
66
  end
53
67
 
@@ -56,4 +70,4 @@ module MiniKraken
56
70
  end
57
71
  end # class
58
72
  end # module
59
- end # module
73
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'singleton'
2
4
  require_relative 'binary_relation'
3
5
  # require_relative 'any_value'
@@ -5,212 +7,149 @@ require_relative 'duck_fiber'
5
7
  require_relative 'variable'
6
8
  require_relative 'variable_ref'
7
9
 
8
- module MiniKraken
9
- module Core
10
- # equals tries to unify two terms
11
- class Equals < BinaryRelation
12
- include Singleton
10
+ unless MiniKraken::Core.constants(false).include? :Equals
11
+ module MiniKraken
12
+ module Core
13
+ # equals tries to unify two terms
14
+ class Equals < BinaryRelation
15
+ include Singleton
13
16
 
14
- def initialize
15
- super('equals', '==')
16
- end
17
+ def initialize
18
+ super('equals', '==')
19
+ end
17
20
 
18
- =begin
19
- double data flow:
20
- A goal has actual arguments
21
- When its corresponding relation is invoked
22
- this one will return two things: (a success, the bindings/constraints
23
- resulting from the relation)
24
- the bindings/constraints can be undone if enclosing fails, otherwise
25
- the bindings/constraints are rolled up.
26
- =end
27
- =begin
28
- def unify(aGoal, vars)
29
- arg1, arg2 = aGoal.actuals
30
- arg_kinds = [arg1.kind_of?(VariableRef), arg2.kind_of?(VariableRef)]
31
- case arg_kinds
32
- when [false, false]
33
- if arg1.eql?(arg2)
34
- result = Outcome.new(:"#s", [])
35
- else
36
- result = Failure
37
- end
38
- when [false, true]
39
- if arg2.fresh?
40
- arg2.bind_to(arg1)
41
- result = Outcome.new(:"#s", [arg1])
42
- else
43
- if arg2.value.eql?(arg1)
44
- result = Outcome.new(:"#s", [arg1])
45
- else
46
- result = Failure
47
- end
48
- end
49
- when [true, false]
50
- if arg1.fresh?
51
- arg1.bind_to(arg2)
52
- result = Outcome.new(:"#s", [arg2])
53
- else
54
- if arg1.value.eql?(arg2)
55
- result = Outcome.new(:"#s", [arg2])
56
- else
57
- result = Failure
58
- end
59
- end
60
- when [true, true]
61
- case [arg1.fresh?, arg2.fresh?]
62
- when [false, false]
63
- if arg1.value == arg2.value
64
- result = Outcome.new(:"#s", [arg1.value])
21
+ # @param actuals [Array<Term>] A two-elements array
22
+ # @param anEnv [Vocabulary] A vocabulary object
23
+ # @return [Fiber<Outcome>] A Fiber(-like) instance that yields Outcomes
24
+ def solver_for(actuals, anEnv)
25
+ arg1, arg2 = *actuals
26
+ DuckFiber.new(:custom) { unification(arg1, arg2, anEnv) }
27
+ end
28
+
29
+ def unification(arg1, arg2, anEnv)
30
+ arg1_nil = arg1.nil?
31
+ arg2_nil = arg2.nil?
32
+ if arg1_nil || arg2_nil
33
+ if arg1_nil && arg2_nil
34
+ result = Outcome.new(:"#s", anEnv)
65
35
  else
66
36
  result = Failure
67
37
  end
68
- when [false, true]
69
- arg2.bind_to(arg1)
70
- result = Outcome.new(:"#s", arg1.value)
71
- when [true, false]
72
- arg1.bind_to(arg2)
73
- result = Outcome.new(:"#s", arg2.value)
74
- when [true, true]
75
- if arg1.variable.name == arg2.variable.name
76
- result = Outcome.new(:"#s", [])
77
- else
78
- # TODO: add constraints
79
- result = Outcome.new(:"#s", [])
80
- end
38
+ return result
81
39
  end
82
- end
40
+ new_arg1, new_arg2 = commute_cond(arg1, arg2, anEnv)
41
+ result = do_unification(new_arg1, new_arg2, anEnv)
42
+ # anEnv.merge(result) if result.successful? && !result.association.empty?
83
43
 
84
- result
85
- end
86
- =end
87
-
88
- def solver_for(actuals, anEnv)
89
- arg1, arg2 = *actuals
90
- DuckFiber.new(:custom) { unification(arg1, arg2, anEnv) }
91
- end
92
-
93
- def unification(arg1, arg2, anEnv)
94
- arg1_nil = arg1.nil?
95
- arg2_nil = arg2.nil?
96
- if arg1_nil || arg2_nil
97
- if arg1_nil && arg2_nil
98
- result = Outcome.new(:"#s", anEnv)
99
- else
100
- result = Failure
44
+ result
101
45
  end
102
- return result
103
- end
104
- new_arg1, new_arg2 = commute_cond(arg1, arg2, anEnv)
105
- result = do_unification(new_arg1, new_arg2, anEnv)
106
- # anEnv.merge(result) if result.successful? && !result.association.empty?
107
46
 
108
- result
109
- end
47
+ private
110
48
 
111
- private
49
+ # table: Unification
50
+ # | arg1 | arg2 | Criterion || Unification |
51
+ # | isa? Atomic | isa? Atomic | arg1.eq? arg2 is true || { "s", [] } |
52
+ # | isa? Atomic | isa? Atomic | arg1.eq? arg2 is false || { "u", [] } |
53
+ # | isa? CompositeTerm | isa? Atomic | dont_care || { "u", [] } |
54
+ # | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.car, arg2.car) => "s" || { "s", [bindings*] } |
55
+ # | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.cdr, arg2.cdr) => "u" || { "u", [] ) | |
56
+ # | isa? VariableRef | isa? Atomic | arg1.fresh? is true || { "s", [arg2] } |
57
+ # | isa? VariableRef | isa? Atomic | arg1.fresh? is false || |
58
+ # | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
59
+ # | | unification(arg1.value, arg2) => "u" || { "u", [] } |
60
+ # | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is true || { "s", [arg2] } | # What if arg1 occurs in arg2?
61
+ # | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is false || |
62
+ # | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
63
+ # | | unification(arg1.value, arg2) => "u" || { "u", [] } |
64
+ # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, true] || { "s", [arg1 <=> arg2] } |
65
+ # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, false] || |
66
+ # | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
67
+ # | | unification(arg1, arg2.value) => "u" || { "u", [] } |
68
+ # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [false, false]|| |
69
+ # | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
70
+ # | | unification(arg1, arg2.value) => "u" || { "u", [] }
71
+ def do_unification(arg1, arg2, anEnv)
72
+ # require 'debug'
73
+ return Outcome.new(:"#s", anEnv) if arg1.equal?(arg2)
112
74
 
113
- # table: Unification
114
- # | arg1 | arg2 | Criterion || Unification |
115
- # | isa? Atomic | isa? Atomic | arg1.eq? arg2 is true || { "s", [] } |
116
- # | isa? Atomic | isa? Atomic | arg1.eq? arg2 is false || { "u", [] } |
117
- # | isa? CompositeTerm | isa? Atomic | dont_care || { "u", [] } |
118
- # | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.car, arg2.car) => "s" || { "s", [bindings*] } |
119
- # | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.cdr, arg2.cdr) => "u" || { "u", [] ) | |
120
- # | isa? VariableRef | isa? Atomic | arg1.fresh? is true || { "s", [arg2] } |
121
- # | isa? VariableRef | isa? Atomic | arg1.fresh? is false || |
122
- # | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
123
- # | | unification(arg1.value, arg2) => "u" || { "u", [] } |
124
- # | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is true || { "s", [arg2] } | # What if arg1 occurs in arg2?
125
- # | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is false || |
126
- # | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
127
- # | | unification(arg1.value, arg2) => "u" || { "u", [] } |
128
- # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, true] || { "s", [arg1 <=> arg2] } |
129
- # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, false] || |
130
- # | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
131
- # | | unification(arg1, arg2.value) => "u" || { "u", [] } |
132
- # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [false, false]|| |
133
- # | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
134
- # | | unification(arg1, arg2.value) => "u" || { "u", [] }
135
- def do_unification(arg1, arg2, anEnv)
136
- # require 'debug'
137
- return Outcome.new(:"#s", anEnv) if arg1.equal?(arg2)
138
- result = Outcome.new(:"#u", anEnv) # default case
75
+ result = Outcome.new(:"#u", anEnv) # default case
139
76
 
140
- if arg1.kind_of?(AtomicTerm)
141
- result = BasicSuccess if arg1.eql?(arg2)
142
- elsif arg1.kind_of?(CompositeTerm)
143
- if arg2.kind_of?(CompositeTerm) # AtomicTerm is default case => fail
144
- result = unify_composite_terms(arg1, arg2, anEnv)
145
- end
146
- elsif arg1.kind_of?(VariableRef)
147
- arg1_freshness = arg1.freshness(anEnv)
148
- if arg2.kind_of?(AtomicTerm)
149
- if arg1_freshness.degree == :fresh
150
- result = Outcome.new(:"#s", anEnv)
151
- arg1.associate(arg2, result)
152
- else
153
- result = Outcome.new(:"#s", anEnv) if arg1.value(anEnv).eql?(arg2)
154
- end
155
- elsif arg2.kind_of?(CompositeTerm)
156
- if arg1_freshness.degree == :fresh
157
- result = Outcome.new(:"#s", anEnv)
158
- arg1.associate(arg2, result)
159
- else
160
- # Ground case...
161
- result = unify_composite_terms(arg1_freshness.associated, arg2, anEnv)
162
- end
163
- elsif arg2.kind_of?(VariableRef)
164
- freshness = [arg1.fresh?(anEnv), arg2.fresh?(anEnv)]
165
- case freshness
166
- when [false, false] # TODO: confirm this...
167
- result = unification(arg1.value(anEnv), arg2.value(anEnv), anEnv)
168
- when [true, true]
169
- result = Outcome.new(:"#s", anEnv)
170
- if arg1.var_name != arg2.var_name
171
- arg1.associate(arg2, result)
172
- arg2.associate(arg1, result)
77
+ if arg1.kind_of?(AtomicTerm)
78
+ result = BasicSuccess if arg1.eql?(arg2)
79
+ elsif arg1.kind_of?(CompositeTerm)
80
+ if arg2.kind_of?(CompositeTerm) # AtomicTerm is default case => fail
81
+ result = unify_composite_terms(arg1, arg2, anEnv)
82
+ end
83
+ elsif arg1.kind_of?(VariableRef)
84
+ arg1_freshness = arg1.freshness(anEnv)
85
+ if arg2.kind_of?(AtomicTerm)
86
+ if arg1_freshness.degree == :fresh
87
+ result = Outcome.new(:"#s", anEnv)
88
+ arg1.associate(arg2, result)
89
+ else
90
+ result = Outcome.new(:"#s", anEnv) if arg1.value(anEnv).eql?(arg2)
91
+ end
92
+ elsif arg2.kind_of?(CompositeTerm)
93
+ if arg1_freshness.degree == :fresh
94
+ result = Outcome.new(:"#s", anEnv)
95
+ arg1.associate(arg2, result)
96
+ else
97
+ # Ground case...
98
+ result = unify_composite_terms(arg1_freshness.associated, arg2, anEnv)
99
+ end
100
+ elsif arg2.kind_of?(VariableRef)
101
+ freshness = [arg1.fresh?(anEnv), arg2.fresh?(anEnv)]
102
+ case freshness
103
+ when [false, false] # TODO: confirm this...
104
+ result = unification(arg1.value(anEnv), arg2.value(anEnv), anEnv)
105
+ when [true, true]
106
+ result = Outcome.new(:"#s", anEnv)
107
+ if arg1.var_name != arg2.var_name
108
+ arg1.associate(arg2, result)
109
+ arg2.associate(arg1, result)
110
+ end
111
+ else
112
+ raise StandardError, "Unsupported freshness combination #{freshness}"
113
+ end
114
+ else
115
+ arg_kinds = [arg1.class, arg2.class]
116
+ raise StandardError, "Unsupported combination #{arg_kinds}"
173
117
  end
174
- else
175
- raise StandardError, "Unsupported freshness combination #{freshness}"
176
118
  end
177
- else
178
- arg_kinds = [arg1.class, arg2.class]
179
- raise StandardError, "Unsupported combination #{arg_kinds}"
180
- end
181
- end
182
119
 
183
- result
184
- end
120
+ result
121
+ end
185
122
 
186
- # @return [Freshness]
187
- def unify_composite_terms(arg1, arg2, anEnv)
188
- # require 'debug'
189
- result = Outcome.new(:"#u", anEnv)
190
- children1 = arg1.children
191
- children2 = arg2.children
123
+ # @return [Freshness]
124
+ def unify_composite_terms(arg1, arg2, anEnv)
125
+ # require 'debug'
126
+ result = Outcome.new(:"#u", anEnv)
127
+ children1 = arg1.children
128
+ children2 = arg2.children
192
129
 
193
- if children1.size == children2.size
194
- i = 0
195
- subresults = children1.map do |child1|
196
- child2 = children2[i]
197
- i += 1
198
- unification(child1, child2, anEnv)
199
- end
200
- total_success = subresults.all?(&:successful?)
201
- if total_success
202
- memo = Outcome.new(:"#s", anEnv)
203
- associations = subresults.reduce(memo) do |sub_total, outcome|
204
- sub_total.merge(outcome)
205
- sub_total
130
+ if children1.size == children2.size
131
+ i = 0
132
+ subresults = children1.map do |child1|
133
+ child2 = children2[i]
134
+ i += 1
135
+ unification(child1, child2, anEnv)
136
+ end
137
+ total_success = subresults.all?(&:successful?)
138
+ if total_success
139
+ memo = Outcome.new(:"#s", anEnv)
140
+ associations = subresults.reduce(memo) do |sub_total, outcome|
141
+ sub_total.merge(outcome)
142
+ sub_total
143
+ end
144
+ result = memo
145
+ end
206
146
  end
207
- result = memo
208
- end
209
- end
210
-
211
- result
212
- end
213
147
 
214
- end # class
148
+ result
149
+ end
150
+ end # class
151
+
152
+ Equals.instance.freeze
153
+ end # module
215
154
  end # module
216
- end # module
155
+ end # unless