mini_kraken 0.2.03 → 0.3.03

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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +378 -333
  3. data/CHANGELOG.md +48 -0
  4. data/README.md +29 -21
  5. data/lib/mini_kraken/atomic/all_atomic.rb +5 -0
  6. data/lib/mini_kraken/atomic/atomic_term.rb +96 -0
  7. data/lib/mini_kraken/atomic/k_boolean.rb +42 -0
  8. data/lib/mini_kraken/{core → atomic}/k_integer.rb +2 -5
  9. data/lib/mini_kraken/atomic/k_string.rb +17 -0
  10. data/lib/mini_kraken/{core → atomic}/k_symbol.rb +4 -8
  11. data/lib/mini_kraken/composite/all_composite.rb +4 -0
  12. data/lib/mini_kraken/composite/composite_term.rb +27 -0
  13. data/lib/mini_kraken/composite/cons_cell.rb +301 -0
  14. data/lib/mini_kraken/composite/cons_cell_visitor.rb +50 -0
  15. data/lib/mini_kraken/composite/list.rb +32 -0
  16. data/lib/mini_kraken/core/all_core.rb +8 -0
  17. data/lib/mini_kraken/core/any_value.rb +31 -7
  18. data/lib/mini_kraken/core/arity.rb +69 -0
  19. data/lib/mini_kraken/core/association.rb +29 -4
  20. data/lib/mini_kraken/core/association_copy.rb +50 -0
  21. data/lib/mini_kraken/core/base_term.rb +13 -0
  22. data/lib/mini_kraken/core/blackboard.rb +315 -0
  23. data/lib/mini_kraken/core/bookmark.rb +46 -0
  24. data/lib/mini_kraken/core/context.rb +492 -0
  25. data/lib/mini_kraken/core/duck_fiber.rb +21 -19
  26. data/lib/mini_kraken/core/entry.rb +40 -0
  27. data/lib/mini_kraken/core/fail.rb +20 -18
  28. data/lib/mini_kraken/core/fusion.rb +29 -0
  29. data/lib/mini_kraken/core/goal.rb +20 -29
  30. data/lib/mini_kraken/core/log_var.rb +22 -0
  31. data/lib/mini_kraken/core/log_var_ref.rb +108 -0
  32. data/lib/mini_kraken/core/nullary_relation.rb +2 -9
  33. data/lib/mini_kraken/core/parametrized_term.rb +68 -0
  34. data/lib/mini_kraken/core/relation.rb +14 -28
  35. data/lib/mini_kraken/core/scope.rb +67 -0
  36. data/lib/mini_kraken/core/solver_adapter.rb +58 -0
  37. data/lib/mini_kraken/core/specification.rb +48 -0
  38. data/lib/mini_kraken/core/succeed.rb +21 -17
  39. data/lib/mini_kraken/core/symbol_table.rb +137 -0
  40. data/lib/mini_kraken/core/term.rb +15 -4
  41. data/lib/mini_kraken/glue/dsl.rb +44 -88
  42. data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
  43. data/lib/mini_kraken/rela/all_rela.rb +8 -0
  44. data/lib/mini_kraken/rela/binary_relation.rb +30 -0
  45. data/lib/mini_kraken/rela/conde.rb +143 -0
  46. data/lib/mini_kraken/rela/conj2.rb +65 -0
  47. data/lib/mini_kraken/rela/def_relation.rb +93 -0
  48. data/lib/mini_kraken/rela/disj2.rb +70 -0
  49. data/lib/mini_kraken/rela/fresh.rb +98 -0
  50. data/lib/mini_kraken/{core → rela}/goal_relation.rb +7 -9
  51. data/lib/mini_kraken/rela/unify.rb +265 -0
  52. data/lib/mini_kraken/version.rb +1 -1
  53. data/mini_kraken.gemspec +2 -2
  54. data/spec/.rubocop.yml +1 -1
  55. data/spec/atomic/atomic_term_spec.rb +98 -0
  56. data/spec/{core → atomic}/k_boolean_spec.rb +19 -34
  57. data/spec/{core → atomic}/k_symbol_spec.rb +3 -16
  58. data/spec/composite/cons_cell_spec.rb +225 -0
  59. data/spec/{core → composite}/cons_cell_visitor_spec.rb +36 -20
  60. data/spec/composite/list_spec.rb +50 -0
  61. data/spec/core/any_value_spec.rb +52 -0
  62. data/spec/core/arity_spec.rb +92 -0
  63. data/spec/core/association_copy_spec.rb +69 -0
  64. data/spec/core/association_spec.rb +31 -4
  65. data/spec/core/blackboard_spec.rb +287 -0
  66. data/spec/core/bookmark_spec.rb +40 -0
  67. data/spec/core/context_spec.rb +245 -0
  68. data/spec/core/core_spec.rb +40 -0
  69. data/spec/core/duck_fiber_spec.rb +16 -46
  70. data/spec/core/fail_spec.rb +5 -6
  71. data/spec/core/goal_spec.rb +22 -12
  72. data/spec/core/log_var_ref_spec.rb +105 -0
  73. data/spec/core/log_var_spec.rb +64 -0
  74. data/spec/core/nullary_relation_spec.rb +33 -0
  75. data/spec/core/parametrized_tem_spec.rb +39 -0
  76. data/spec/core/relation_spec.rb +33 -0
  77. data/spec/core/scope_spec.rb +73 -0
  78. data/spec/core/solver_adapter_spec.rb +70 -0
  79. data/spec/core/specification_spec.rb +43 -0
  80. data/spec/core/succeed_spec.rb +5 -5
  81. data/spec/core/symbol_table_spec.rb +142 -0
  82. data/spec/glue/dsl_chap1_spec.rb +88 -144
  83. data/spec/glue/dsl_chap2_spec.rb +454 -19
  84. data/spec/glue/run_star_expression_spec.rb +81 -906
  85. data/spec/rela/conde_spec.rb +153 -0
  86. data/spec/rela/conj2_spec.rb +123 -0
  87. data/spec/rela/def_relation_spec.rb +119 -0
  88. data/spec/rela/disj2_spec.rb +117 -0
  89. data/spec/rela/fresh_spec.rb +147 -0
  90. data/spec/rela/unify_spec.rb +369 -0
  91. data/spec/support/factory_atomic.rb +29 -0
  92. data/spec/support/factory_composite.rb +21 -0
  93. data/spec/support/factory_methods.rb +11 -26
  94. metadata +98 -70
  95. data/lib/mini_kraken/core/association_walker.rb +0 -183
  96. data/lib/mini_kraken/core/atomic_term.rb +0 -67
  97. data/lib/mini_kraken/core/base_arg.rb +0 -10
  98. data/lib/mini_kraken/core/binary_relation.rb +0 -63
  99. data/lib/mini_kraken/core/composite_goal.rb +0 -46
  100. data/lib/mini_kraken/core/composite_term.rb +0 -41
  101. data/lib/mini_kraken/core/conde.rb +0 -143
  102. data/lib/mini_kraken/core/conj2.rb +0 -79
  103. data/lib/mini_kraken/core/cons_cell.rb +0 -82
  104. data/lib/mini_kraken/core/cons_cell_visitor.rb +0 -102
  105. data/lib/mini_kraken/core/def_relation.rb +0 -53
  106. data/lib/mini_kraken/core/designation.rb +0 -55
  107. data/lib/mini_kraken/core/disj2.rb +0 -72
  108. data/lib/mini_kraken/core/environment.rb +0 -73
  109. data/lib/mini_kraken/core/equals.rb +0 -193
  110. data/lib/mini_kraken/core/formal_arg.rb +0 -22
  111. data/lib/mini_kraken/core/formal_ref.rb +0 -25
  112. data/lib/mini_kraken/core/freshness.rb +0 -45
  113. data/lib/mini_kraken/core/goal_arg.rb +0 -12
  114. data/lib/mini_kraken/core/goal_template.rb +0 -102
  115. data/lib/mini_kraken/core/k_boolean.rb +0 -35
  116. data/lib/mini_kraken/core/outcome.rb +0 -63
  117. data/lib/mini_kraken/core/variable.rb +0 -41
  118. data/lib/mini_kraken/core/variable_ref.rb +0 -84
  119. data/lib/mini_kraken/core/vocabulary.rb +0 -446
  120. data/lib/mini_kraken/glue/fresh_env.rb +0 -103
  121. data/lib/mini_kraken/glue/fresh_env_factory.rb +0 -83
  122. data/spec/core/association_walker_spec.rb +0 -192
  123. data/spec/core/conde_spec.rb +0 -147
  124. data/spec/core/conj2_spec.rb +0 -114
  125. data/spec/core/cons_cell_spec.rb +0 -107
  126. data/spec/core/def_relation_spec.rb +0 -97
  127. data/spec/core/disj2_spec.rb +0 -99
  128. data/spec/core/environment_spec.rb +0 -142
  129. data/spec/core/equals_spec.rb +0 -317
  130. data/spec/core/goal_template_spec.rb +0 -74
  131. data/spec/core/outcome_spec.rb +0 -56
  132. data/spec/core/variable_ref_spec.rb +0 -30
  133. data/spec/core/variable_spec.rb +0 -35
  134. data/spec/core/vocabulary_spec.rb +0 -219
  135. data/spec/glue/fresh_env_factory_spec.rb +0 -97
  136. data/spec/glue/fresh_env_spec.rb +0 -62
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'composite_term'
4
-
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
- super()
14
- @car = obj1
15
- if obj2.kind_of?(ConsCell) && obj2.null?
16
- @cdr = nil
17
- else
18
- @cdr = obj2
19
- end
20
- end
21
-
22
- def children
23
- [car, cdr]
24
- end
25
-
26
- # Return true if it is an empty list, otherwise false.
27
- # A list is empty, when both car and cdr fields are nil.
28
- def null?
29
- car.nil? && cdr.nil?
30
- end
31
-
32
- def ==(other)
33
- return false unless other.respond_to?(:car)
34
-
35
- (car == other.car) && (cdr == other.cdr)
36
- end
37
-
38
- def eql?(other)
39
- (self.class == other.class) && car.eql?(other.car) && cdr.eql?(other.cdr)
40
- end
41
-
42
- def quote(anEnv)
43
- return self if null?
44
-
45
- new_car = car.nil? ? nil : car.quote(anEnv)
46
- new_cdr = cdr.nil? ? nil : cdr.quote(anEnv)
47
- ConsCell.new(new_car, new_cdr)
48
- end
49
-
50
- # Use the list notation from Lisp as a text representation.
51
- def to_s
52
- return '()' if null?
53
-
54
- "(#{pair_to_s})"
55
- end
56
-
57
- def append(another)
58
- @cdr = another
59
- end
60
-
61
- protected
62
-
63
- def pair_to_s
64
- result = +car.to_s
65
- if cdr
66
- result << ' '
67
- if cdr.kind_of?(ConsCell)
68
- result << cdr.pair_to_s
69
- else
70
- result << ". #{cdr}"
71
- end
72
- end
73
-
74
- result
75
- end
76
- end # class
77
-
78
- # Constant representing the null (empty) list.
79
- NullList = ConsCell.new(nil, nil).freeze
80
- end # module
81
- end # module
82
- end # defined
@@ -1,102 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'set'
4
- require_relative 'cons_cell'
5
-
6
- module MiniKraken
7
- module Core
8
- # Factory class.
9
- # Purpose: to create an enumerator specialized in the visit of cons cells.
10
- class ConsCellVisitor
11
- # Build a depth-first in-order expression tree visitor.
12
- # The visitor is implemented as an Enumerator.
13
- # The enumerator returns couples of the form: [:car or :cdr or :nil, visitee]
14
- # [anExpr] the term to visit.
15
- # @param aCell [ConsCell]
16
- # @return [Fiber]
17
- def self.df_visitor(aCell)
18
- first = aCell # The visit will start from the provided cons cell
19
- visitor = Fiber.new do |skipping|
20
- # Initialization part: will run once
21
- visitees = Set.new # Keep track of the conscell already visited
22
- visit_stack = first.nil? ? [] : [[:car, first]] # The LIFO queue of cells to visit
23
-
24
- until visit_stack.empty? # Traversal part (as a loop)
25
- to_swap = false
26
- side, cell = visit_stack.pop
27
- next if visitees.include?(cell)
28
-
29
- visitees << cell
30
-
31
- skip_children = Fiber.yield [side, cell]
32
- # require 'debug' if skip_children
33
- next if skip_children || skipping
34
-
35
- skipping = false
36
- case cell.car
37
- when ConsCell
38
- visit_stack.push([:car, cell.car])
39
- to_swap = true
40
- else
41
- Fiber.yield [:car, cell.car]
42
- end
43
-
44
- case cell.cdr
45
- when ConsCell
46
- if to_swap
47
- visit_stack.insert(-2, [:cdr, cell.cdr])
48
- else
49
- visit_stack.push([:cdr, cell.cdr])
50
- end
51
- else
52
- Fiber.yield [:cdr, cell.cdr]
53
- end
54
- end
55
-
56
- # Send stop mark
57
- Fiber.yield [:stop, nil]
58
- end
59
-
60
- =begin
61
- visitor = Enumerator.new do |requester| # requester argument is a Yielder
62
- # Initialization part: will run once
63
- visitees = Set.new # Keep track of the conscell already visited
64
- visit_stack = first.nil? ? [] : [[ :car, first ]] # The LIFO queue of cells to visit
65
-
66
- until visit_stack.empty? # Traversal part (as a loop)
67
- to_swap = false
68
- side, cell = visit_stack.pop()
69
- next if visitees.include?(cell)
70
-
71
- requester << [side, cell]
72
- case cell.car
73
- when ConsCell
74
- visit_stack.push([:car, cell.car])
75
- to_swap = true
76
- else
77
- requester << [:car, cell.car]
78
- end
79
-
80
- case cell.cdr
81
- when ConsCell
82
- if to_swap
83
- visit_stack.insert(-2, [:cdr, cell.cdr])
84
- else
85
- visit_stack.push([:cdr, cell.cdr])
86
- end
87
- else
88
- requester << [:cdr, cell.cdr]
89
- end
90
-
91
- visitees << cell
92
- end
93
-
94
- # Send stop mark
95
- requester << [:stop, nil]
96
- end
97
- =end
98
- return visitor
99
- end
100
- end # class
101
- end # module
102
- end # module
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'relation'
4
-
5
- module MiniKraken
6
- module Core
7
- # A relation that is parametrized with generic formal arguments
8
- # and a goal template expression.
9
- class DefRelation < Relation
10
- # @return [Array<FormalArg>] formal arguments of this DefRelation
11
- attr_reader :formals
12
-
13
- # @return [GoalTemplate] goal template
14
- attr_reader :goal_template
15
-
16
- # @param aName [String] name of def relation
17
- # @param aGoalTemplate [GoalTemplate]
18
- # @param theFormals [Array<FormalArg>]
19
- def initialize(aName, aGoalTemplate, theFormals, alternateName = nil)
20
- super(aName, alternateName)
21
- @formals = validated_formals(theFormals)
22
- @goal_template = validated_goal_template(aGoalTemplate)
23
- freeze
24
- end
25
-
26
- # Number of arguments for the relation.
27
- # @return [Integer]
28
- def arity
29
- formals.size
30
- end
31
-
32
- # @param actuals [Array<Term>] A two-elements array
33
- # @param anEnv [Vocabulary] A vocabulary object
34
- # @return [Fiber<Outcome>] A Fiber(-like) instance that yields Outcomes
35
- def solver_for(actuals, anEnv)
36
- goal = goal_template.instantiate(formals, actuals)
37
- goal.attain(anEnv)
38
- end
39
-
40
- private
41
-
42
- def validated_formals(theFormals)
43
- theFormals
44
- end
45
-
46
- def validated_goal_template(aGoalTemplate)
47
- raise StandardError unless aGoalTemplate
48
-
49
- aGoalTemplate
50
- end
51
- end # class
52
- end # module
53
- end # module
@@ -1,55 +0,0 @@
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
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'singleton'
4
- require_relative 'duck_fiber'
5
- require_relative 'fail'
6
- require_relative 'goal'
7
- require_relative 'goal_relation'
8
- require_relative 'outcome'
9
-
10
- unless MiniKraken::Core.constants(false).include? :Disj2
11
- module MiniKraken
12
- module Core
13
- # The disjunction is a relation that accepts only goal(s) as its two
14
- # arguments. It succeeds if at least one of its goal arguments succeeds.
15
- class Disj2 < GoalRelation
16
- include Singleton
17
-
18
- def initialize
19
- super('disj2', nil)
20
- end
21
-
22
- # @param actuals [Array<Term>] A two-elements array
23
- # @param anEnv [Vocabulary] A vocabulary object
24
- # @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
25
- def solver_for(actuals, anEnv)
26
- g1, g2 = *validated_args(actuals)
27
- Fiber.new { disjunction(g1, g2, anEnv) }
28
- end
29
-
30
- # Yields [Outcome, NilClass] result of the disjunction
31
- # @param g1 [Goal] First goal argument
32
- # @param g2 [Goal] Second goal argument
33
- # @param voc [Vocabulary] A vocabulary object
34
- def disjunction(g1, g2, voc)
35
- # require 'debug'
36
- outcome1 = nil
37
- outcome2 = nil
38
- if g1.relation.kind_of?(Fail) && g2.relation.kind_of?(Fail)
39
- Fiber.yield Outcome.new(:"#u", voc)
40
- else
41
- f1 = g1.attain(voc)
42
- loop do
43
- outcome1 = f1.resume
44
- break unless outcome1
45
-
46
- outcome1.parent = voc unless outcome1.parent
47
- if outcome1.success?
48
- Fiber.yield outcome1
49
- outcome1.clear
50
- end
51
- end
52
- f2 = g2.attain(voc)
53
- loop do
54
- outcome2 = f2.resume
55
- break unless outcome2
56
-
57
- outcome2.parent = voc unless outcome2.parent
58
- if outcome2.success?
59
- Fiber.yield outcome2
60
- outcome2.clear
61
- end
62
- end
63
- end
64
-
65
- Fiber.yield nil
66
- end
67
- end # class
68
-
69
- Disj2.instance.freeze
70
- end # module
71
- end # module
72
- end # unless
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'set'
4
- require_relative 'vocabulary'
5
-
6
- module MiniKraken
7
- module Core
8
- class Environment
9
- include Vocabulary # Use mix-in module
10
-
11
- # Mapping from user-defined name to Variable instance
12
- # @return [Hash] Pairs of the kind {String => Variable}
13
- attr_reader :vars
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
-
19
- # @param aParent [Environment, NilClass] Parent environment to this one.
20
- def initialize(aParent = nil)
21
- init_vocabulary(aParent)
22
- @vars = {}
23
- @ivars = {}
24
- end
25
-
26
- # @param aVariable [Variable]
27
- def add_var(aVariable)
28
- name = aVariable.name
29
- if vars.include?(name)
30
- err_msg = "Variable with name '#{name}' already exists."
31
- raise StandardError, err_msg
32
- end
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
41
- end
42
-
43
- # Handler for the event: an outcome has been produced.
44
- # Can be overridden in other to propagate associations from child
45
- # @param descendent [Outcome]
46
- def propagate(descendent)
47
- # Rollout associations from hierarchy
48
- walker = descendent.ancestor_walker
49
- begin
50
- env = walker.next
51
- break if env.nil?
52
-
53
- env.do_propagate(descendent) if env.kind_of?(Environment)
54
- end until env.equal?(self)
55
- end
56
-
57
- # Roll up associations from descendent outcome object
58
- # @param descendent [Outcome]
59
- def do_propagate(descendent)
60
- return unless descendent.success?
61
-
62
- vars.each_key do |var_name|
63
- # assocs = descendent[var_name]
64
- move_assocs(var_name, descendent)
65
- end
66
- end
67
-
68
- def merge_vars(descendent)
69
- descendent.vars.each_value { |vr| add_var(vr) }
70
- end
71
- end # class
72
- end # module
73
- end # module
@@ -1,193 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'singleton'
4
- require_relative 'binary_relation'
5
- require_relative 'cons_cell_visitor'
6
- require_relative 'duck_fiber'
7
- require_relative 'variable'
8
- require_relative 'variable_ref'
9
-
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
16
-
17
- def initialize
18
- super('equals', '==')
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(-like) instance that yields Outcomes
24
- def solver_for(actuals, anEnv)
25
- arg1, arg2 = *actuals
26
- DuckFiber.new(:custom) do
27
- outcome = unification(arg1, arg2, anEnv)
28
- outcome.prune!
29
- end
30
- end
31
-
32
- def unification(arg1, arg2, anEnv)
33
- arg1_nil = arg1.nil?
34
- arg2_nil = arg2.nil?
35
- if arg1_nil || arg2_nil
36
- if arg1_nil && arg2_nil
37
- result = Outcome.success(anEnv)
38
- else
39
- result = Outcome.failure(anEnv)
40
- end
41
- return result
42
- end
43
- new_arg1, new_arg2 = commute_cond(arg1, arg2, anEnv)
44
- do_unification(new_arg1, new_arg2, anEnv)
45
- end
46
-
47
- private
48
-
49
-
50
- # table: Unification
51
- # | arg1 | arg2 | Criterion || Unification |
52
- # | isa? Atomic | isa? Atomic | arg1.eq? arg2 is true || { "s", [] } |
53
- # | isa? Atomic | isa? Atomic | arg1.eq? arg2 is false || { "u", [] } |
54
- # | isa? CompositeTerm | isa? Atomic | dont_care || { "u", [] } |
55
- # | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.car, arg2.car) => "s" || { "s", [bindings*] } |
56
- # | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.cdr, arg2.cdr) => "u" || { "u", [] ) | |
57
- # | isa? VariableRef | isa? Atomic | arg1.fresh? is true || { "s", [arg2] } |
58
- # | isa? VariableRef | isa? Atomic | arg1.fresh? is false || |
59
- # | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
60
- # | | unification(arg1.value, arg2) => "u" || { "u", [] } |
61
- # | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is true || { "s", [arg2] } | # What if arg1 occurs in arg2?
62
- # | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is false || |
63
- # | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
64
- # | | unification(arg1.value, arg2) => "u" || { "u", [] } |
65
- # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, true] || { "s", [arg1 <=> arg2] } |
66
- # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, false] || |
67
- # | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
68
- # | | unification(arg1, arg2.value) => "u" || { "u", [] } |
69
- # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [false, false]|| |
70
- # | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
71
- # | | unification(arg1, arg2.value) => "u" || { "u", [] }
72
- def do_unification(arg1, arg2, anEnv)
73
- # require 'debug'
74
- return Outcome.success(anEnv) if arg1.equal?(arg2)
75
-
76
- result = Outcome.failure(anEnv) # default case
77
-
78
- if arg1.kind_of?(AtomicTerm)
79
- result = Outcome.success(anEnv) if arg1.eql?(arg2)
80
- elsif arg1.kind_of?(CompositeTerm)
81
- if arg2.kind_of?(CompositeTerm) # AtomicTerm is default case => fail
82
- result = unify_composite_terms(arg1, arg2, anEnv)
83
- end
84
- elsif arg1.kind_of?(VariableRef)
85
- arg1_freshness = arg1.freshness(anEnv)
86
- if arg2.kind_of?(AtomicTerm)
87
- if arg1_freshness.degree == :fresh
88
- result = Outcome.success(anEnv)
89
- arg1.associate(arg2, result)
90
- else
91
- result = Outcome.success(anEnv) if arg1.value(anEnv).eql?(arg2)
92
- end
93
- elsif arg2.kind_of?(CompositeTerm)
94
- if arg1_freshness.degree == :fresh
95
- result = Outcome.success(anEnv)
96
- arg1.associate(arg2, result)
97
- else
98
- # Ground case...
99
- arg1_associated = arg1_freshness.associated
100
- unless arg1_associated.kind_of?(AtomicTerm)
101
- result = unify_composite_terms(arg1_associated, arg2, anEnv)
102
- end
103
- end
104
- elsif arg2.kind_of?(VariableRef)
105
- freshness = [arg1.fresh?(anEnv), arg2.fresh?(anEnv)]
106
- case freshness
107
- when [false, false] # TODO: confirm this...
108
- result = unification(arg1.value(anEnv), arg2.value(anEnv), anEnv)
109
- when [true, true]
110
- result = Outcome.success(anEnv)
111
- if arg1.var_name != arg2.var_name
112
- arg1.associate(arg2, result)
113
- arg2.associate(arg1, result)
114
- end
115
- when [true, false]
116
- result = Outcome.success(anEnv)
117
- arg1.associate(arg2, result)
118
- else
119
- raise StandardError, "Unsupported freshness combination #{freshness}"
120
- end
121
- else
122
- arg_kinds = [arg1.class, arg2.class]
123
- raise StandardError, "Unsupported combination #{arg_kinds}"
124
- end
125
- end
126
-
127
- result
128
- end
129
-
130
- # @param arg1 [ConsCell]
131
- # @param arg2 [ConsCell]
132
- # @return [Outcome]
133
- def unify_composite_terms(arg1, arg2, anEnv)
134
- # require 'debug'
135
- result = Outcome.success(anEnv)
136
- # We'll do parallel iteration
137
- visitor1 = ConsCellVisitor.df_visitor(arg1)
138
- visitor2 = ConsCellVisitor.df_visitor(arg2)
139
- skip_children1 = false
140
- skip_children2 = false
141
-
142
- loop do
143
- side1, cell1 = visitor1.resume(skip_children1)
144
- side2, cell2 = visitor2.resume(skip_children2)
145
- if side1 != side2
146
- result = Outcome.failure(anEnv)
147
- elsif side1 == :stop
148
- break
149
- else
150
- case [cell1.class, cell2.class] # nil, AtomicTerm, ConsCell, VariableRef
151
- when [ConsCell, ConsCell]
152
- skip_children1 = false
153
- skip_children2 = false
154
- when [ConsCell, VariableRef]
155
- skip_children1 = true
156
- skip_children2 = false
157
- sub_result = unification(cell1, cell2, anEnv)
158
- result = merge_results(result, sub_result)
159
- when [VariableRef, ConsCell]
160
- skip_children1 = false
161
- skip_children2 = true
162
- sub_result = do_unification(cell1, cell2, anEnv)
163
- result = merge_results(result, sub_result)
164
- else
165
- skip_children1 = false
166
- skip_children2 = false
167
- sub_result = unification(cell1, cell2, anEnv)
168
- result = merge_results(result, sub_result)
169
- end
170
- end
171
-
172
- break if result.failure?
173
- end
174
-
175
- result
176
- end
177
-
178
- def merge_results(result1, result2)
179
- raise StandardError if result2.kind_of?(Hash)
180
-
181
- if result2.success?
182
- result1.merge(result2)
183
- result1
184
- else
185
- result2
186
- end
187
- end
188
- end # class
189
-
190
- Equals.instance.freeze
191
- end # module
192
- end # module
193
- end # unless