mini_kraken 0.2.04 → 0.3.00

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +16 -16
  4. data/lib/mini_kraken/atomic/all_atomic.rb +1 -0
  5. data/lib/mini_kraken/atomic/atomic_term.rb +32 -17
  6. data/lib/mini_kraken/atomic/k_integer.rb +0 -4
  7. data/lib/mini_kraken/atomic/k_string.rb +17 -0
  8. data/lib/mini_kraken/atomic/k_symbol.rb +0 -6
  9. data/lib/mini_kraken/composite/all_composite.rb +4 -0
  10. data/lib/mini_kraken/composite/composite_term.rb +2 -18
  11. data/lib/mini_kraken/composite/cons_cell.rb +178 -11
  12. data/lib/mini_kraken/composite/cons_cell_visitor.rb +12 -64
  13. data/lib/mini_kraken/composite/list.rb +32 -0
  14. data/lib/mini_kraken/core/all_core.rb +8 -0
  15. data/lib/mini_kraken/core/any_value.rb +31 -7
  16. data/lib/mini_kraken/core/arity.rb +69 -0
  17. data/lib/mini_kraken/core/association.rb +29 -4
  18. data/lib/mini_kraken/core/association_copy.rb +50 -0
  19. data/lib/mini_kraken/core/base_term.rb +13 -0
  20. data/lib/mini_kraken/core/blackboard.rb +315 -0
  21. data/lib/mini_kraken/core/bookmark.rb +46 -0
  22. data/lib/mini_kraken/core/context.rb +624 -0
  23. data/lib/mini_kraken/core/duck_fiber.rb +21 -19
  24. data/lib/mini_kraken/core/entry.rb +40 -0
  25. data/lib/mini_kraken/core/fail.rb +20 -18
  26. data/lib/mini_kraken/core/fusion.rb +29 -0
  27. data/lib/mini_kraken/core/goal.rb +20 -29
  28. data/lib/mini_kraken/core/log_var.rb +4 -30
  29. data/lib/mini_kraken/core/log_var_ref.rb +72 -48
  30. data/lib/mini_kraken/core/nullary_relation.rb +2 -9
  31. data/lib/mini_kraken/core/parametrized_term.rb +61 -0
  32. data/lib/mini_kraken/core/relation.rb +14 -28
  33. data/lib/mini_kraken/core/scope.rb +67 -0
  34. data/lib/mini_kraken/core/solver_adapter.rb +58 -0
  35. data/lib/mini_kraken/core/specification.rb +48 -0
  36. data/lib/mini_kraken/core/succeed.rb +21 -17
  37. data/lib/mini_kraken/core/symbol_table.rb +137 -0
  38. data/lib/mini_kraken/core/term.rb +15 -4
  39. data/lib/mini_kraken/glue/dsl.rb +35 -69
  40. data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
  41. data/lib/mini_kraken/rela/all_rela.rb +8 -0
  42. data/lib/mini_kraken/rela/binary_relation.rb +30 -0
  43. data/lib/mini_kraken/rela/conde.rb +146 -0
  44. data/lib/mini_kraken/rela/conj2.rb +65 -0
  45. data/lib/mini_kraken/rela/def_relation.rb +64 -0
  46. data/lib/mini_kraken/rela/disj2.rb +70 -0
  47. data/lib/mini_kraken/rela/fresh.rb +98 -0
  48. data/lib/mini_kraken/{core → rela}/goal_relation.rb +6 -8
  49. data/lib/mini_kraken/rela/unify.rb +258 -0
  50. data/lib/mini_kraken/version.rb +1 -1
  51. data/spec/atomic/atomic_term_spec.rb +23 -20
  52. data/spec/atomic/k_symbol_spec.rb +0 -5
  53. data/spec/composite/cons_cell_spec.rb +116 -0
  54. data/spec/composite/cons_cell_visitor_spec.rb +16 -3
  55. data/spec/composite/list_spec.rb +50 -0
  56. data/spec/core/any_value_spec.rb +52 -0
  57. data/spec/core/arity_spec.rb +91 -0
  58. data/spec/core/association_copy_spec.rb +69 -0
  59. data/spec/core/association_spec.rb +25 -0
  60. data/spec/core/blackboard_spec.rb +287 -0
  61. data/spec/core/bookmark_spec.rb +40 -0
  62. data/spec/core/context_spec.rb +221 -0
  63. data/spec/core/core_spec.rb +40 -0
  64. data/spec/core/duck_fiber_spec.rb +22 -46
  65. data/spec/core/fail_spec.rb +5 -6
  66. data/spec/core/goal_spec.rb +20 -11
  67. data/spec/core/log_var_ref_spec.rb +80 -5
  68. data/spec/core/log_var_spec.rb +35 -6
  69. data/spec/core/nullary_relation_spec.rb +33 -0
  70. data/spec/core/parametrized_tem_spec.rb +39 -0
  71. data/spec/core/relation_spec.rb +33 -0
  72. data/spec/core/scope_spec.rb +73 -0
  73. data/spec/core/solver_adapter_spec.rb +70 -0
  74. data/spec/core/specification_spec.rb +43 -0
  75. data/spec/core/succeed_spec.rb +5 -5
  76. data/spec/core/symbol_table_spec.rb +142 -0
  77. data/spec/glue/dsl_chap1_spec.rb +88 -99
  78. data/spec/glue/dsl_chap2_spec.rb +59 -41
  79. data/spec/glue/run_star_expression_spec.rb +69 -896
  80. data/spec/{core → rela}/conde_spec.rb +50 -46
  81. data/spec/rela/conj2_spec.rb +123 -0
  82. data/spec/rela/def_relation_spec.rb +119 -0
  83. data/spec/rela/disj2_spec.rb +117 -0
  84. data/spec/rela/fresh_spec.rb +147 -0
  85. data/spec/rela/unify_spec.rb +369 -0
  86. data/spec/support/factory_atomic.rb +7 -0
  87. data/spec/support/factory_composite.rb +21 -0
  88. metadata +71 -48
  89. data/lib/mini_kraken/core/association_walker.rb +0 -183
  90. data/lib/mini_kraken/core/base_arg.rb +0 -10
  91. data/lib/mini_kraken/core/binary_relation.rb +0 -63
  92. data/lib/mini_kraken/core/composite_goal.rb +0 -46
  93. data/lib/mini_kraken/core/conde.rb +0 -143
  94. data/lib/mini_kraken/core/conj2.rb +0 -79
  95. data/lib/mini_kraken/core/def_relation.rb +0 -53
  96. data/lib/mini_kraken/core/designation.rb +0 -55
  97. data/lib/mini_kraken/core/disj2.rb +0 -72
  98. data/lib/mini_kraken/core/environment.rb +0 -73
  99. data/lib/mini_kraken/core/equals.rb +0 -191
  100. data/lib/mini_kraken/core/formal_arg.rb +0 -22
  101. data/lib/mini_kraken/core/formal_ref.rb +0 -25
  102. data/lib/mini_kraken/core/freshness.rb +0 -45
  103. data/lib/mini_kraken/core/goal_arg.rb +0 -12
  104. data/lib/mini_kraken/core/goal_template.rb +0 -102
  105. data/lib/mini_kraken/core/outcome.rb +0 -63
  106. data/lib/mini_kraken/core/tap.rb +0 -46
  107. data/lib/mini_kraken/core/vocabulary.rb +0 -446
  108. data/lib/mini_kraken/glue/fresh_env.rb +0 -108
  109. data/lib/mini_kraken/glue/fresh_env_factory.rb +0 -83
  110. data/spec/core/association_walker_spec.rb +0 -194
  111. data/spec/core/conj2_spec.rb +0 -116
  112. data/spec/core/def_relation_spec.rb +0 -99
  113. data/spec/core/disj2_spec.rb +0 -100
  114. data/spec/core/environment_spec.rb +0 -144
  115. data/spec/core/equals_spec.rb +0 -319
  116. data/spec/core/goal_template_spec.rb +0 -74
  117. data/spec/core/outcome_spec.rb +0 -56
  118. data/spec/core/vocabulary_spec.rb +0 -220
  119. data/spec/glue/fresh_env_factory_spec.rb +0 -99
  120. data/spec/glue/fresh_env_spec.rb +0 -62
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ require_relative '../core/context'
6
+ require_relative '../core/duck_fiber'
7
+ require_relative 'goal_relation'
8
+
9
+ module MiniKraken
10
+ module Rela
11
+ # The conjunction is a relation that accepts only two goals as its
12
+ # arguments. It succeeds if and only if both its goal arguments succeeds.
13
+ class Conj2 < GoalRelation
14
+ include Singleton
15
+
16
+ # Default initialization
17
+ def initialize
18
+ super('conj2', 2)
19
+ end
20
+
21
+ # @param actuals [Array<Core::Term>] A two-elements array
22
+ # @param ctx [Core::Context] A context object
23
+ # @return [Fiber<Core::Context>] A Fiber that yields Context objects
24
+ def solver_for(actuals, ctx)
25
+ g1, g2 = *validated_args(actuals)
26
+ Fiber.new { conjunction(g1, g2, ctx) }
27
+ end
28
+
29
+ # Yields [Core::Context, NilClass] result of the conjunction
30
+ # @param g1 [Goal] First goal argument
31
+ # @param g2 [Goal] Second goal argument
32
+ # @param ctx [Core::Context] A ctxabulary object
33
+ def conjunction(g1, g2, ctx)
34
+ # require 'debug'
35
+ if g1.relation.kind_of?(Core::Fail) || g2.relation.kind_of?(Core::Fail)
36
+ Fiber.yield ctx.failed!
37
+ else
38
+ outcome1 = outcome2 = nil
39
+ fiber1 = g1.achieve(ctx)
40
+
41
+ loop do
42
+ outcome1 = fiber1.resume(ctx)
43
+ break if outcome1.nil?
44
+
45
+ if outcome1.success?
46
+ fiber2 = g2.achieve(ctx)
47
+ loop do
48
+ outcome2 = fiber2.resume(ctx)
49
+ break if outcome2.nil?
50
+
51
+ Fiber.yield outcome2
52
+ end
53
+ else
54
+ Fiber.yield outcome1
55
+ end
56
+ end
57
+ end
58
+
59
+ Fiber.yield nil
60
+ end
61
+ end # class
62
+
63
+ Conj2.instance.freeze
64
+ end # module
65
+ end # module
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require_relative '../core/relation'
5
+ require_relative '../core/entry'
6
+
7
+ module MiniKraken
8
+ module Rela
9
+ # A user-defined relation with:
10
+ # - a user-defined name,
11
+ # - a ordered list of generic formal arguments, and;
12
+ # - a goal template expression.
13
+ class DefRelation < Core::Relation
14
+ include Core::Entry # Add behaviour of symbol table entries
15
+
16
+ # @return [Array<FormalArg>] formal arguments of this DefRelation
17
+ attr_reader :formals
18
+
19
+ # @return [Term] Expression to be fulfilled and parametrized with formals
20
+ attr_reader :expression
21
+
22
+ # @param aName [String] name of def relation
23
+ # @param anExpression [Term]
24
+ # @param theFormals [Array<String>]
25
+ def initialize(aName, anExpression, theFormals)
26
+ @formals = validated_formals(theFormals)
27
+ formal_vars = formals.map { |nm| Core::LogVarRef.new(nm) }
28
+ raw_expression = validated_expression(anExpression)
29
+ @expression = replace_expression(raw_expression, theFormals, formal_vars)
30
+ super(aName, formals.size)
31
+ freeze
32
+ end
33
+
34
+ # @param actuals [Array<Term>] A two-elements array
35
+ # @param ctx [Context] A Context object
36
+ # @return [Fiber<Outcome>] A Fiber(-like) instance that yields Outcomes
37
+ def solver_for(actuals, ctx)
38
+ actual_expr = replace_expression(expression, formals, actuals)
39
+ actual_expr.achieve(ctx)
40
+ end
41
+
42
+ private
43
+
44
+ def validated_formals(theFormals)
45
+ # Make the formal names unique, to avoid name collision
46
+ theFormals.map { |name| "#{name}_#{SecureRandom.uuid}" }
47
+ end
48
+
49
+ def validated_expression(aGoalTemplate)
50
+ raise StandardError unless aGoalTemplate
51
+
52
+ aGoalTemplate
53
+ end
54
+
55
+ # With the given expression, create a new expression where
56
+ # each allusion to original variable is replaced by the
57
+ # by its corresponding actual value.
58
+ def replace_expression(anExpression, original, actual)
59
+ raw_pairs = original.zip(actual) # [original, actual]
60
+ anExpression.dup_cond(raw_pairs.to_h)
61
+ end
62
+ end # class
63
+ end # module
64
+ end # module
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ require_relative '../core/context'
6
+ require_relative '../core/duck_fiber'
7
+ require_relative 'goal_relation'
8
+
9
+ module MiniKraken
10
+ module Rela
11
+ # The disjunction is a relation that accepts only goal(s) as its two
12
+ # arguments. It succeeds if at least one of its goal arguments succeeds.
13
+ class Disj2 < GoalRelation
14
+ include Singleton
15
+
16
+ # Default initialization
17
+ def initialize
18
+ super('disj2', 2)
19
+ end
20
+
21
+ # @param actuals [Array<Core::Term>] A two-elements array
22
+ # @param ctx [Core::Context] A context object
23
+ # @return [Fiber<Core::Context>] A Fiber that yields Context objects
24
+ def solver_for(actuals, ctx)
25
+ g1, g2 = *validated_args(actuals)
26
+ Fiber.new { disjunction(g1, g2, ctx) }
27
+ end
28
+
29
+ # Yields [Core::Context, NilClass] result of the disjunction
30
+ # @param g1 [Goal] First goal argument
31
+ # @param g2 [Goal] Second goal argument
32
+ # @param ctx [Core::Context] A ctxabulary object
33
+ def disjunction(g1, g2, ctx)
34
+ # require 'debug'
35
+ if g1.relation.kind_of?(Core::Fail) && g2.relation.kind_of?(Core::Fail)
36
+ Fiber.yield ctx.failed!
37
+ else
38
+ ctx.place_bt_point
39
+ outcome1 = nil
40
+ outcome2 = nil
41
+ f1 = g1.achieve(ctx)
42
+ loop do
43
+ outcome1 = f1.resume(ctx)
44
+ break if outcome1.nil?
45
+
46
+ if outcome1.success?
47
+ Fiber.yield outcome1
48
+ ctx.next_alternative
49
+ end
50
+ end
51
+ f2 = g2.achieve(ctx)
52
+ loop do
53
+ outcome2 = f2.resume(ctx)
54
+ break if outcome2.nil?
55
+
56
+ if outcome2.success?
57
+ Fiber.yield outcome2
58
+ ctx.next_alternative
59
+ end
60
+ end
61
+ end
62
+
63
+ ctx.retract_bt_point
64
+ Fiber.yield nil
65
+ end
66
+ end # class
67
+
68
+ Disj2.instance.freeze
69
+ end # module
70
+ end # module
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative '../core/solver_adapter'
5
+ require_relative '../atomic/k_string'
6
+ require_relative 'conj2'
7
+ require_relative 'goal_relation'
8
+
9
+ module MiniKraken
10
+ module Rela
11
+ # A specialized relation that accepts a variable names(s) and a subgoal
12
+ # as its arguments.
13
+ class Fresh < GoalRelation
14
+ include Singleton
15
+ # Default initialization
16
+ def initialize
17
+ super('fresh', 2)
18
+ end
19
+
20
+ def self.build_goal(names, subgoals)
21
+ var_names = nil
22
+
23
+ case names
24
+ when String
25
+ var_names = Atomic::KString.new(names)
26
+
27
+ when Array
28
+ var_names = names.map do |nm|
29
+ nm.is_a?(String) ? Atomic::KString.new(nm) : nm
30
+ end
31
+ end
32
+
33
+ nested_goal = compose_goals(subgoals)
34
+ Core::Goal.new(instance, [var_names, nested_goal])
35
+ end
36
+
37
+ def self.compose_goals(subgoals)
38
+ nested_goal = nil
39
+
40
+ case subgoals
41
+ when Core::Goal
42
+ nested_goal = subgoals
43
+
44
+ when Array
45
+ goal_array = subgoals
46
+ loop do
47
+ conjunctions = []
48
+ goal_array.each_slice(2) do |uno_duo|
49
+ if uno_duo.size == 2
50
+ conjunctions << Core::Goal.new(Conj2.instance, uno_duo)
51
+ else
52
+ conjunctions << uno_duo[0]
53
+ end
54
+ end
55
+ if conjunctions.size == 1
56
+ nested_goal = conjunctions[0]
57
+ break
58
+ end
59
+ goal_array = conjunctions
60
+ end
61
+ end
62
+
63
+ nested_goal
64
+ end
65
+
66
+ # @param actuals [Array<Array<KString>, Core::Term>] A two-elements array
67
+ # First element is an array of variable names to create.
68
+ # Second is a sub-goal object
69
+ # @param ctx [Core::Context] A context object
70
+ # @return [Fiber<Core::Context>] A Fiber that yields Context objects
71
+ def solver_for(actuals, ctx)
72
+ k_names = actuals.shift
73
+ # require 'debug'
74
+ subgoal = validated_args(actuals).first
75
+
76
+ ctx.enter_scope(Core::Scope.new)
77
+ if k_names.kind_of?(Atomic::KString)
78
+ ctx.add_vars(k_names.value)
79
+ else
80
+ # ... Array of KString
81
+ names = k_names.map(&:value)
82
+ ctx.add_vars(names)
83
+ end
84
+
85
+ # Wrap the subgoal's solver by an adapter
86
+ orig_solver = subgoal.achieve(ctx)
87
+ Core::SolverAdapter.new(orig_solver) do |adp, context|
88
+ # puts "Adaptee #{adp.adaptee}"
89
+ result = adp.adaptee.resume(context)
90
+ context.leave_scope if result.nil?
91
+ result
92
+ end
93
+ end
94
+ end # class
95
+
96
+ Fresh.instance.freeze
97
+ end # module
98
+ end # module
@@ -1,21 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'relation'
3
+ require_relative '../core/goal'
4
+ require_relative '../core/relation'
4
5
 
5
6
  module MiniKraken
6
- module Core
7
+ module Rela
7
8
  # A specialization of a relation that accepts only goal(s)
8
9
  # as its arguments.
9
- class GoalRelation < Relation
10
- def arity
11
- 2
12
- end
13
-
10
+ class GoalRelation < Core::Relation
14
11
  protected
15
12
 
13
+ # Validate that actuals
16
14
  def validated_args(actuals)
17
15
  actuals.each do |arg|
18
- unless arg.kind_of?(Goal) || arg.kind_of?(Glue::FreshEnv)
16
+ unless arg.kind_of?(Core::Goal)
19
17
  prefix = "#{name} expects goal as argument, found a "
20
18
  raise StandardError, prefix + "'#{arg.class}': #{arg}"
21
19
  end
@@ -0,0 +1,258 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative 'binary_relation'
5
+
6
+ require_relative '../core/duck_fiber'
7
+ require_relative '../core/log_var_ref'
8
+ require_relative '../atomic/atomic_term'
9
+ require_relative '../composite/all_composite'
10
+
11
+
12
+ module MiniKraken
13
+ module Rela
14
+ # Corresponds to the '==' relation in canonical miniKanren implementation
15
+ # in Scheme. Implements the core of the unification algorithm.
16
+ class Unify < BinaryRelation
17
+ include Singleton
18
+
19
+ symmetric # Unify relation is symmetric ("First Law of ==")
20
+
21
+ # Constructor. Initialize the name of the relation
22
+ def initialize
23
+ super('unify')
24
+ freeze
25
+ end
26
+
27
+ # @param actuals [Array<Term>] A two-elements array
28
+ # @param ctx [Context] A context object
29
+ # @return [Fiber<Context>] A DuckFiber instance that yields one Context.
30
+ def solver_for(actuals, ctx)
31
+ arg1, arg2 = *actuals
32
+ # context = unification(arg1, arg2, ctx)
33
+ # Core::DuckFiber.new(-> { context })
34
+ Core::DuckFiber.new(-> { unification(arg1, arg2, ctx) })
35
+ end
36
+
37
+ # @param arg1 [Term]
38
+ # @param arg2 [Term]
39
+ # @param ctx [Context] A context object
40
+ # @return [Context] The updated context
41
+ def unification(arg1, arg2, ctx)
42
+ return ctx.succeeded! if arg1.equal?(arg2)
43
+ return ctx.failed! if arg1.nil? || arg2.nil?
44
+
45
+ new_arg1, new_arg2 = commute_cond(arg1, arg2, ctx)
46
+ do_unification(new_arg1, new_arg2, ctx)
47
+ end
48
+
49
+ protected
50
+
51
+ def do_unification(arg1, arg2, ctx)
52
+ table = [
53
+ # cond1 cond2 selector
54
+ [kind_of(Atomic::AtomicTerm), kind_of(Atomic::AtomicTerm),
55
+ :unify_atomic_terms],
56
+ [kind_of(Composite::CompositeTerm), kind_of(Atomic::AtomicTerm),
57
+ :unify_composite_atomic],
58
+ [kind_of(Composite::CompositeTerm), kind_of(Composite::CompositeTerm),
59
+ :unify_composite_terms],
60
+ [kind_of(Core::LogVarRef), kind_of(Atomic::AtomicTerm),
61
+ :unify_ref_atomic],
62
+ [kind_of(Core::LogVarRef), kind_of(Composite::CompositeTerm),
63
+ :unify_ref_composite],
64
+ [kind_of(Core::LogVarRef), kind_of(Core::LogVarRef),
65
+ :unify_references]
66
+ ]
67
+
68
+ # require 'debug'
69
+
70
+ table.each do |(cond1, cond2, selector)|
71
+ if cell_success(arg1, cond1, ctx) &&
72
+ cell_success(arg2, cond2, ctx)
73
+ return send(selector, arg1, arg2, ctx)
74
+ end
75
+ end
76
+
77
+ ctx.failed
78
+ end
79
+
80
+ =begin
81
+ # table: Commute
82
+ # |arg1 | arg2 | arg2.ground? || Commute |
83
+ # | isa? Atomic | isa? Atomic | dont_care || Yes |
84
+ # | isa? Atomic | isa? CompositeTerm | dont_care || Yes |
85
+ # | isa? Atomic | isa? LogVarRef | dont_care || Yes |
86
+ # | isa? CompositeTerm | isa? Atomic | true || No |
87
+ # | isa? CompositeTerm | isa? CompositeTerm | false || Yes |
88
+ # | isa? CompositeTerm | isa? CompositeTerm | true || No |
89
+ # | isa? CompositeTerm | isa? LogVarRef | dont_care || Yes |
90
+ # | isa? LogVarRef | isa? Atomic | dont_care || No |
91
+ # | isa? LogVarRef | isa? CompositeTerm | dont_care || No |
92
+ # | isa? LogVarRef | isa? LogVarRef | false || Yes |
93
+ # | isa? LogVarRef | isa? LogVarRef | true || No |
94
+ =end
95
+
96
+ def weight_arg(arg, ctx)
97
+ case arg
98
+ when Atomic::AtomicTerm
99
+ 1
100
+ when Composite::CompositeTerm
101
+ 2
102
+ when Core::LogVarRef
103
+ # Move unbound argument to the right...
104
+ arg.unbound?(ctx) ? 3 : 4
105
+ else
106
+ raise StandardError
107
+ end
108
+ end
109
+
110
+ def kind_of(aClass)
111
+ ->(ar, _) { ar.kind_of?(aClass) }
112
+ end
113
+
114
+ private
115
+
116
+ def cell_success(arg, cond, ctx)
117
+ case cond
118
+ when Class
119
+ arg.kind_of?(cond.class)
120
+ when Proc
121
+ cond.call(arg, ctx)
122
+ else
123
+ raise StandardError
124
+ end
125
+ end
126
+
127
+ # Unification of two atomic terms
128
+ # @param arg1 [Atomic::AtomicTerm]
129
+ # @param arg2 [Atomic::AtomicTerm]
130
+ # @param ctx [Core::Context] A context object
131
+ # @return [Core::Context] Updated context
132
+ def unify_atomic_terms(arg1, arg2, ctx)
133
+ arg1.eql?(arg2) ? ctx.succeeded! : ctx.failed!
134
+ end
135
+
136
+ # Unification of a composite term with an atomic term
137
+ # @param _composite [Composite::CompositeTerm]
138
+ # @param _atomic [Atomic::AtomicTerm]
139
+ # @param ctx [Core::Context] A context object
140
+ # @return [Core::Context] a failure Context
141
+ def unify_composite_atomic(_composite, _atomic, ctx)
142
+ ctx.failed!
143
+ end
144
+
145
+ # Unification of a composite term with an atomic term
146
+ # @param arg1 [Composite::CompositeTerm]
147
+ # @param arg2 [Composite::CompositeTerm]
148
+ # @param ctx [Core::Context] A context object
149
+ # @return [Core::Context] Updated context
150
+ def unify_composite_terms(arg1, arg2, ctx)
151
+ return ctx.succeeded! if arg1.null? && arg2.null?
152
+
153
+ # We do parallel iteration
154
+ visitor1 = Composite::ConsCellVisitor.df_visitor(arg1)
155
+ visitor2 = Composite::ConsCellVisitor.df_visitor(arg2)
156
+ skip_children1 = skip_children2 = false
157
+
158
+ loop do
159
+ # side.. can be: :car, :cdr, :stop
160
+ side1, cell1 = visitor1.resume(skip_children1)
161
+ side2, cell2 = visitor2.resume(skip_children2)
162
+ if side1 != side2
163
+ ctx.failed
164
+ elsif side1 == :stop
165
+ break
166
+ else
167
+ # A cell can be: nil, Atomic::AtomicTerm, Composite::ConsCell, LogVarRef
168
+ case [cell1.class, cell2.class]
169
+ when [Composite::ConsCell, Composite::ConsCell]
170
+ skip_children1 = skip_children2 = false
171
+ ctx.blackboard.succeeded!
172
+ when [Composite::ConsCell, Core::LogVarRef]
173
+ skip_children1 = true
174
+ skip_children2 = false
175
+ unification(cell1, cell2, ctx)
176
+ when [Core::LogVarRef, Composite::ConsCell]
177
+ skip_children1 = false
178
+ skip_children2 = true
179
+ do_unification(cell1, cell2, ctx)
180
+ else
181
+ skip_children1 = skip_children2 = false
182
+ unification(cell1, cell2, ctx)
183
+ end
184
+ end
185
+
186
+ break if ctx.failure?
187
+ end
188
+
189
+ ctx
190
+ end
191
+
192
+ # Unification of a logical variable reference with an atomic term
193
+ # @param ref [Core::LogVarRef]
194
+ # @param atomic [Atomic::AtomicTerm]
195
+ # @param ctx [Core::Context] A context object
196
+ # @return [Core::Context] Updated context
197
+ def unify_ref_atomic(ref, atomic, ctx)
198
+ if ref.unbound?(ctx)
199
+ ctx.associate(ref, atomic)
200
+ ctx.succeeded!
201
+ else
202
+ assocs = ctx.associations_for(ref.name)
203
+ first_assoc = assocs.first
204
+ if first_assoc.kind_of?(Core::Association) &&
205
+ first_assoc.value.eql?(atomic)
206
+ # Trying to associate again to the same value is OK
207
+ ctx.succeeded!
208
+ else
209
+ ctx.failed!
210
+ end
211
+ end
212
+ end
213
+
214
+ # Unification of a logical variable and a composite
215
+ # @param ref [Core::LogVarRef]
216
+ # @param composite [Core::CompositeTerm]
217
+ # @param ctx [Core::Context] A context object
218
+ # @return [Core::Context] Updated context
219
+ def unify_ref_composite(ref, composite, ctx)
220
+ if ref.unbound?(ctx)
221
+ ctx.associate(ref, composite)
222
+ else
223
+ # Assumption: ref has only one existing association...
224
+ as = ctx.associations_for(ref.name)
225
+ first_assoc = as.first.value
226
+ unification(first_assoc, composite, ctx)
227
+ return ctx if ctx.failure?
228
+
229
+ # The association can be sometimes be redundant...
230
+ ctx.associate(ref, composite) unless first_assoc.pinned?(ctx)
231
+ end
232
+ ctx.succeeded!
233
+ ctx
234
+ end
235
+
236
+ # Unification of two logical variable references
237
+ # @param ref1 [Core::LogVarRef]
238
+ # @param ref2 [Core::LogVarRef]
239
+ # @param ctx [Core::Context] A context object
240
+ # @return [Core::Context] Updated context
241
+ def unify_references(ref1, ref2, ctx)
242
+ return ctx.succeeded! if ref1.name == ref2.name
243
+
244
+ if ref1.unbound?(ctx) || ref2.unbound?(ctx)
245
+ ctx.fuse([ref1.name, ref2.name])
246
+ ctx.succeeded!
247
+ else
248
+ raise NotImplentedError
249
+ end
250
+ # if both refs are fresh, fuse them
251
+ # if one ref is fresh & the other one isn't then bind fresh one (occurs check)
252
+ # More cases...
253
+
254
+ ctx
255
+ end
256
+ end # class
257
+ end # module
258
+ end # module