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,34 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../core/any_value'
4
- require_relative '../core/cons_cell'
5
- require_relative 'fresh_env'
3
+ require_relative '../core/context'
4
+ require_relative '../composite/list'
5
+ require_relative '../rela/fresh'
6
6
 
7
7
  module MiniKraken
8
8
  module Glue
9
9
  class RunStarExpression
10
- # @return [FreshEnv] The environment in which run* variables will reside.
11
- attr_reader :env
10
+ # @return [Core::Context] The context in which run* variables
11
+ # will be evaluated.
12
+ attr_reader :ctx
13
+
14
+ # @return [Core::Goal] The main goal to satisfy
15
+ attr_reader :goal
12
16
 
13
17
  # @param var_names [String, Array<String>] One variable name or an array of names
14
- # @param goal [Core::Goal, Array<Core::Goal>] A single goal or an array of goals to conjunct
15
- def initialize(var_names, goal)
16
- vnames = var_names.kind_of?(String) ? [var_names] : var_names
17
- @env = FreshEnv.new(vnames, goal)
18
+ # @param aGoal [Core::Goal, Array<Core::Goal>] A single goal or an array of goals to conjunct
19
+ def initialize(var_names, aGoal)
20
+ @ctx = Core::Context.new
21
+ ctx.add_vars(var_names)
22
+ @goal = Rela::Fresh.compose_goals(aGoal)
18
23
  end
19
24
 
25
+ # Run the query, that is, try to find ALL solutions
26
+ # of the provided qoal.
27
+ # One solution corresponds to allowed value associated
28
+ # to the provided logical variable(s).
29
+ #
20
30
  def run
21
31
  result = []
22
- solver = env.goal.attain(env)
32
+ solver = goal.achieve(ctx) # A solver == Fiber(-like) yielding Context
23
33
  # require 'debug'
34
+
24
35
  loop do
25
- env.clear
26
- env.clear_rankings
27
- outcome = solver.resume
28
- break if outcome.nil?
36
+ outcome = solver.resume(ctx)
37
+ break if outcome.nil? # No other solution?
29
38
 
30
- env.propagate(outcome) if result.empty? && outcome.success?
31
- result << build_solution(outcome)
39
+ result << outcome.build_solution.values if outcome.success?
32
40
  end
33
41
 
34
42
  format_solutions(result)
@@ -36,13 +44,6 @@ module MiniKraken
36
44
 
37
45
  private
38
46
 
39
- # @return [Array] A vector of assignment for each variable
40
- def build_solution(outcome)
41
- env.vars.values.map do |var|
42
- outcome.success? ? var.quote(outcome) : nil
43
- end
44
- end
45
-
46
47
  # Transform the solutions into sequence of conscells.
47
48
  # @param solutions [Array<Array>] An array of solution.
48
49
  # A solution is in itself an array of bindings (one per variable)
@@ -55,14 +56,11 @@ module MiniKraken
55
56
  # @param anArray [Array]
56
57
  # @param simplify [Boolean]
57
58
  def arr2list(anArray, simplify)
58
- return anArray[0] if anArray.size == 1 && simplify
59
-
60
- new_tail = nil
61
- anArray.reverse_each do |elem|
62
- new_tail = Core::ConsCell.new(elem, new_tail)
59
+ if anArray.size == 1 && simplify
60
+ anArray[0]
61
+ else
62
+ Composite::List.make_list(anArray)
63
63
  end
64
-
65
- new_tail
66
64
  end
67
65
  end # class
68
66
  end # module
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'conj2'
4
+ require_relative 'disj2'
5
+ require_relative 'fresh'
6
+ require_relative 'unify'
7
+ require_relative 'conde'
8
+ require_relative 'def_relation'
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/relation'
4
+
5
+ module MiniKraken
6
+ module Rela
7
+ # A binary relation between sets X and Y is a subset of the Cartesian product
8
+ # X × Y; that is, it is a set of ordered pairs (x, y) consisting of elements
9
+ # x in X and y in Y.
10
+ class BinaryRelation < Core::Relation
11
+ # @param aName [String] Name of the relation.
12
+ def initialize(aName)
13
+ super(aName, 2)
14
+ freeze
15
+ end
16
+
17
+ def self.symmetric
18
+ define_method :commute_cond do |arg1, arg2, ctx|
19
+ w1 = weight_arg(arg1, ctx)
20
+ w2 = weight_arg(arg2, ctx)
21
+ if w2 > w1
22
+ [arg2, arg1]
23
+ else
24
+ [arg1, arg2]
25
+ end
26
+ end
27
+ end
28
+ end # class
29
+ end # module
30
+ end # module
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative '../core/context'
5
+ require_relative '../core/duck_fiber'
6
+ require_relative '../core/fail'
7
+ require_relative '../core/goal'
8
+ require_relative 'conj2'
9
+
10
+ require_relative 'goal_relation'
11
+
12
+ module MiniKraken
13
+ module Rela
14
+ # A polyadic relation (i.e. it can takes an arbitrary number of argumentt)
15
+ # that behaves as the disjunction of its arguments.
16
+ # It succeeds if at least one of its goal arguments succeeds.
17
+ class Conde < GoalRelation
18
+ include Singleton
19
+
20
+ def initialize
21
+ super('conde', Core::Arity.new(1, '*'))
22
+ end
23
+
24
+ # A relation is polyadic when it accepts an arbitrary number of arguments.
25
+ # @return [TrueClass]
26
+ def polyadic?
27
+ true
28
+ end
29
+
30
+ # @param actuals [Array<Term>] A two-elements array
31
+ # @param ctx [Core::Context] A vocabulary object
32
+ # @return [Fiber<Context>] A Fiber that yields Outcomes objects
33
+ def solver_for(actuals, ctx)
34
+ args = *validated_args(actuals)
35
+ Fiber.new { cond(args, ctx) }
36
+ end
37
+
38
+ # Yields [Context, NilClass] result of the disjunction
39
+ # @param goals [Array<Goal>] Array of goals
40
+ # @param ctx [Context] A ctxabulary object
41
+ def cond(goals, ctx)
42
+ # require 'debug'
43
+ success = false
44
+ ctx.place_bt_point
45
+
46
+ goals.each do |g|
47
+ fiber = nil
48
+
49
+ case g
50
+ when Core::Goal
51
+ fiber = g.achieve(ctx)
52
+ when Array
53
+ conjunct = conjunction(g)
54
+ fiber = conjunct.achieve(ctx)
55
+ # when Core::ConsCell
56
+ # goal_array = to_goal_array(g)
57
+ # conjunct = conjunction(goal_array)
58
+ # fiber = conjunct.achieve(ctx)
59
+ else
60
+ raise NotImplementedError
61
+ end
62
+
63
+ loop do
64
+ outcome = fiber.resume(ctx)
65
+ break if outcome.nil?
66
+
67
+ if outcome.success?
68
+ success = true
69
+ Fiber.yield outcome
70
+ end
71
+ end
72
+
73
+ ctx.next_alternative
74
+ end
75
+
76
+ Fiber.yield ctx.failed! unless success
77
+ ctx.retract_bt_point
78
+ Fiber.yield nil
79
+ end
80
+
81
+ private
82
+
83
+ def validated_args(actuals)
84
+ result = []
85
+
86
+ actuals.each do |arg|
87
+ case arg
88
+ when Core::Goal, Core::Context
89
+ result << arg
90
+
91
+ when Array
92
+ result << validated_args(arg)
93
+
94
+ else
95
+ prefix = "#{name} expects goal as argument, found a "
96
+ raise StandardError, prefix + "'#{arg.class}'"
97
+ end
98
+ end
99
+
100
+ result
101
+ end
102
+
103
+ # Given an array of goals, build the conjunction of these.
104
+ def conjunction(goal_array)
105
+ result = nil
106
+
107
+ loop do
108
+ conjunctions = []
109
+ goal_array.each_slice(2) do |uno_duo|
110
+ if uno_duo.size == 2
111
+ conjunctions << Core::Goal.new(Rela::Conj2.instance, uno_duo)
112
+ else
113
+ conjunctions << uno_duo[0]
114
+ end
115
+ end
116
+ if conjunctions.size == 1
117
+ result = conjunctions[0]
118
+ break
119
+ end
120
+ goal_array = conjunctions
121
+ end
122
+
123
+ result
124
+ end
125
+
126
+ def to_goal_array(aCons)
127
+ array = []
128
+ curr_node = aCons
129
+ loop do
130
+ array << curr_node.car if curr_node.car.kind_of?(Core::Goal)
131
+ break unless curr_node.cdr
132
+ break unless curr_node.car.kind_of?(Core::Goal)
133
+
134
+ curr_node = curr_node.cdr
135
+ end
136
+
137
+ array
138
+ end
139
+ end # class
140
+
141
+ Conde.instance.freeze
142
+ end # module
143
+ end # module
@@ -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,93 @@
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
+ compose_goals(aGoalTemplate)
53
+ end
54
+
55
+ def compose_goals(subgoals)
56
+ nested_goal = nil
57
+
58
+ case subgoals
59
+ when Core::Goal
60
+ nested_goal = subgoals
61
+
62
+ when Array
63
+ goal_array = subgoals
64
+ loop do
65
+ conjunctions = []
66
+ goal_array.each_slice(2) do |uno_duo|
67
+ if uno_duo.size == 2
68
+ conjunctions << Core::Goal.new(Conj2.instance, uno_duo)
69
+ else
70
+ conjunctions << uno_duo[0]
71
+ end
72
+ end
73
+ if conjunctions.size == 1
74
+ nested_goal = conjunctions[0]
75
+ break
76
+ end
77
+ goal_array = conjunctions
78
+ end
79
+ end
80
+
81
+ nested_goal
82
+ end
83
+
84
+ # With the given expression, create a new expression where
85
+ # each allusion to original variable is replaced by the
86
+ # by its corresponding actual value.
87
+ def replace_expression(anExpression, original, actual)
88
+ raw_pairs = original.zip(actual) # [original, actual]
89
+ anExpression.dup_cond(raw_pairs.to_h)
90
+ end
91
+ end # class
92
+ end # module
93
+ 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