mini_kraken 0.2.04 → 0.3.00

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