mini_kraken 0.1.07 → 0.1.08

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15536181da18de42e83ec948742a97c63338f2065841849de64d3537eeeb9bc1
4
- data.tar.gz: bb25b2bfcab17da55d5dfa6845e699fa2a48e89e19956f3c63f2e755577d4ae7
3
+ metadata.gz: 32a6f92773457338343dffa1343906883ffdbafbf7f8fd9ace170062cc34d345
4
+ data.tar.gz: cb7d3e6b88f8bea952bea7f214eaf73af97857de6958f65592a15b7ee7ef2510
5
5
  SHA512:
6
- metadata.gz: a964e66c182fa2d834007c2003fa7b16b0e91d8cc80f8525de9c95df89d81523a9a2e93c7e57c2a96605ce6af5244324d5a7bf80c877d46a8a647726a3ac1e49
7
- data.tar.gz: 6608b250a6949ce6d7f3fd4c548c7525595fc20c808b5fa327b2cdc1b48ac5b37dd05ee92ae4a8ea2e9a5f76477c24afd5b20e6d1851061afca43952f8d75949
6
+ metadata.gz: db730590bcc99203f8983fe19b5e0d0b90c77819c07875bc34c100bc843c9b3e5e3e148833149d3264847a190cc9ce5bcf1fa31f74723b8c8e8006a56948d738
7
+ data.tar.gz: 4cb5cb8f327e7cb81028621ad67e36afecee8bc90beac1cd6449573b85092191d39d55dfbe2de9f52a5868a66bf4c8288a3223b41815528fb651eead36d3f4fa
@@ -1,3 +1,9 @@
1
+ ## [0.1.08] - 2020-05-30
2
+ - Fix of nasty bug (object aliasing) that caused flaky failures in specs.
3
+
4
+ ### FIXED
5
+ - `DuckFiber#resume` each call returns a distinct `Outcome` instance when successful.
6
+
1
7
  ## [0.1.07] - 2020-05-23
2
8
  - Implementation of `disj2` (two arguments disjunction - or -)
3
9
 
@@ -6,68 +6,72 @@ require_relative 'goal'
6
6
  require_relative 'goal_relation'
7
7
  require_relative 'outcome'
8
8
 
9
- module MiniKraken
10
- module Core
11
- # The conjunction is a relation that accepts only goal(s) as its two
12
- # arguments. It succeeds if and only both its goal arguments succeeds.
13
- class Conj2 < GoalRelation
14
- include Singleton
9
+ unless MiniKraken::Core.constants(false).include? :Conj2
10
+ module MiniKraken
11
+ module Core
12
+ # The conjunction is a relation that accepts only goal(s) as its two
13
+ # arguments. It succeeds if and only both its goal arguments succeeds.
14
+ class Conj2 < GoalRelation
15
+ include Singleton
15
16
 
16
- def initialize
17
- super('conj2', nil)
18
- end
17
+ def initialize
18
+ super('conj2', nil)
19
+ end
19
20
 
20
- # @param actuals [Array<Term>] A two-elements array
21
- # @param anEnv [Vocabulary] A vocabulary object
22
- # @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
23
- def solver_for(actuals, anEnv)
24
- g1, g2 = *validated_args(actuals)
25
- Fiber.new { conjunction(g1, g2, anEnv) }
26
- end
21
+ # @param actuals [Array<Term>] A two-elements array
22
+ # @param anEnv [Vocabulary] A vocabulary object
23
+ # @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
24
+ def solver_for(actuals, anEnv)
25
+ g1, g2 = *validated_args(actuals)
26
+ Fiber.new { conjunction(g1, g2, anEnv) }
27
+ end
27
28
 
28
- # Yields [Outcome, NilClass] result of the conjunction
29
- # @param g1 [Goal] First goal argument
30
- # @param g2 [Goal] Second goal argument
31
- # @param voc [Vocabulary] A vocabulary object
32
- def conjunction(g1, g2, voc)
33
- # require 'debug'
34
- outcome1 = nil
35
- outcome2 = nil
36
- if g1.relation.kind_of?(Fail) || g2.relation.kind_of?(Fail)
37
- Fiber.yield Outcome.new(:"#u", voc)
38
- else
39
- f1 = g1.attain(voc)
40
- loop do
41
- outcome1 = f1.resume
42
- break unless outcome1
29
+ # Yields [Outcome, NilClass] result of the conjunction
30
+ # @param g1 [Goal] First goal argument
31
+ # @param g2 [Goal] Second goal argument
32
+ # @param voc [Vocabulary] A vocabulary object
33
+ def conjunction(g1, g2, voc)
34
+ # require 'debug'
35
+ outcome1 = nil
36
+ outcome2 = nil
37
+ if g1.relation.kind_of?(Fail) || g2.relation.kind_of?(Fail)
38
+ Fiber.yield Outcome.new(:"#u", voc)
39
+ else
40
+ f1 = g1.attain(voc)
41
+ loop do
42
+ outcome1 = f1.resume
43
+ break unless outcome1
43
44
 
44
- outcome1.parent = voc unless outcome1.parent
45
- if outcome1.successful?
46
- f2 = g2.attain(outcome1)
47
- loop do
48
- outcome2 = f2.resume
49
- break unless outcome2
45
+ outcome1.parent = voc unless outcome1.parent
46
+ if outcome1.successful?
47
+ f2 = g2.attain(outcome1)
48
+ loop do
49
+ outcome2 = f2.resume
50
+ break unless outcome2
50
51
 
51
- outcome2.parent = voc unless outcome2.parent
52
- if outcome2.successful?
53
- res = Outcome.new(:"#s", voc)
54
- res.merge(outcome1)
55
- res.merge(outcome2)
56
- Fiber.yield res
57
- else
58
- Fiber.yield outcome2
52
+ outcome2.parent = voc unless outcome2.parent
53
+ if outcome2.successful?
54
+ res = Outcome.new(:"#s", voc)
55
+ res.merge(outcome1)
56
+ res.merge(outcome2)
57
+ Fiber.yield res
58
+ else
59
+ Fiber.yield outcome2
60
+ end
61
+ outcome2.clear
59
62
  end
60
- outcome2.clear
63
+ else
64
+ Fiber.yield outcome1
61
65
  end
62
- else
63
- Fiber.yield outcome1
66
+ voc.clear if outcome1&.successful? && outcome2&.successful?
64
67
  end
65
- voc.clear if outcome1&.successful? && outcome2&.successful?
66
68
  end
69
+
70
+ Fiber.yield nil
67
71
  end
72
+ end # class
68
73
 
69
- Fiber.yield nil
70
- end
71
- end # class
74
+ Conj2.instance.freeze
75
+ end # module
72
76
  end # module
73
- end # module
77
+ end # unless
@@ -6,62 +6,66 @@ require_relative 'goal'
6
6
  require_relative 'goal_relation'
7
7
  require_relative 'outcome'
8
8
 
9
- module MiniKraken
10
- module Core
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
9
+ unless MiniKraken::Core.constants(false).include? :Disj2
10
+ module MiniKraken
11
+ module Core
12
+ # The disjunction is a relation that accepts only goal(s) as its two
13
+ # arguments. It succeeds if at least one of its goal arguments succeeds.
14
+ class Disj2 < GoalRelation
15
+ include Singleton
15
16
 
16
- def initialize
17
- super('disj2', nil)
18
- end
17
+ def initialize
18
+ super('disj2', nil)
19
+ end
19
20
 
20
- # @param actuals [Array<Term>] A two-elements array
21
- # @param anEnv [Vocabulary] A vocabulary object
22
- # @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
23
- def solver_for(actuals, anEnv)
24
- g1, g2 = *validated_args(actuals)
25
- Fiber.new { disjunction(g1, g2, anEnv) }
26
- end
21
+ # @param actuals [Array<Term>] A two-elements array
22
+ # @param anEnv [Vocabulary] A vocabulary object
23
+ # @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
24
+ def solver_for(actuals, anEnv)
25
+ g1, g2 = *validated_args(actuals)
26
+ Fiber.new { disjunction(g1, g2, anEnv) }
27
+ end
27
28
 
28
- # Yields [Outcome, NilClass] result of the disjunction
29
- # @param g1 [Goal] First goal argument
30
- # @param g2 [Goal] Second goal argument
31
- # @param voc [Vocabulary] A vocabulary object
32
- def disjunction(g1, g2, voc)
33
- # require 'debug'
34
- outcome1 = nil
35
- outcome2 = nil
36
- if g1.relation.kind_of?(Fail) && g2.relation.kind_of?(Fail)
37
- Fiber.yield Outcome.new(:"#u", voc)
38
- else
39
- f1 = g1.attain(voc)
40
- loop do
41
- outcome1 = f1.resume
42
- break unless outcome1
29
+ # Yields [Outcome, NilClass] result of the disjunction
30
+ # @param g1 [Goal] First goal argument
31
+ # @param g2 [Goal] Second goal argument
32
+ # @param voc [Vocabulary] A vocabulary object
33
+ def disjunction(g1, g2, voc)
34
+ # require 'debug'
35
+ outcome1 = nil
36
+ outcome2 = nil
37
+ if g1.relation.kind_of?(Fail) && g2.relation.kind_of?(Fail)
38
+ Fiber.yield Outcome.new(:"#u", voc)
39
+ else
40
+ f1 = g1.attain(voc)
41
+ loop do
42
+ outcome1 = f1.resume
43
+ break unless outcome1
43
44
 
44
- outcome1.parent = voc unless outcome1.parent
45
- if outcome1.successful?
46
- Fiber.yield outcome1
47
- outcome1.clear
45
+ outcome1.parent = voc unless outcome1.parent
46
+ if outcome1.successful?
47
+ Fiber.yield outcome1
48
+ outcome1.clear
49
+ end
48
50
  end
49
- end
50
- f2 = g2.attain(voc)
51
- loop do
52
- outcome2 = f2.resume
53
- break unless outcome2
51
+ f2 = g2.attain(voc)
52
+ loop do
53
+ outcome2 = f2.resume
54
+ break unless outcome2
54
55
 
55
- outcome2.parent = voc unless outcome2.parent
56
- if outcome2.successful?
57
- Fiber.yield outcome2
58
- outcome2.clear
56
+ outcome2.parent = voc unless outcome2.parent
57
+ if outcome2.successful?
58
+ Fiber.yield outcome2
59
+ outcome2.clear
60
+ end
59
61
  end
60
62
  end
63
+
64
+ Fiber.yield nil
61
65
  end
66
+ end # class
62
67
 
63
- Fiber.yield nil
64
- end
65
- end # class
68
+ Disj2.instance.freeze
69
+ end # module
66
70
  end # module
67
- end # module
71
+ end # unless
@@ -36,7 +36,7 @@ module MiniKraken
36
36
  when :failure
37
37
  Failure
38
38
  when :success
39
- BasicSuccess
39
+ Outcome.new(:"#s")
40
40
  else
41
41
  raise StandardError, "Unknonw outcome kind #{outcomeKind}"
42
42
  end
@@ -7,145 +7,149 @@ require_relative 'duck_fiber'
7
7
  require_relative 'variable'
8
8
  require_relative 'variable_ref'
9
9
 
10
- module MiniKraken
11
- module Core
12
- # equals tries to unify two terms
13
- class Equals < BinaryRelation
14
- include Singleton
10
+ unless MiniKraken::Core.constants(false).include? :Equals
11
+ module MiniKraken
12
+ module Core
13
+ # equals tries to unify two terms
14
+ class Equals < BinaryRelation
15
+ include Singleton
15
16
 
16
- def initialize
17
- super('equals', '==')
18
- end
17
+ def initialize
18
+ super('equals', '==')
19
+ end
19
20
 
20
- # @param actuals [Array<Term>] A two-elements array
21
- # @param anEnv [Vocabulary] A vocabulary object
22
- # @return [Fiber<Outcome>] A Fiber(-like) instance that yields Outcomes
23
- def solver_for(actuals, anEnv)
24
- arg1, arg2 = *actuals
25
- DuckFiber.new(:custom) { unification(arg1, arg2, anEnv) }
26
- end
21
+ # @param actuals [Array<Term>] A two-elements array
22
+ # @param anEnv [Vocabulary] A vocabulary object
23
+ # @return [Fiber<Outcome>] A Fiber(-like) instance that yields Outcomes
24
+ def solver_for(actuals, anEnv)
25
+ arg1, arg2 = *actuals
26
+ DuckFiber.new(:custom) { unification(arg1, arg2, anEnv) }
27
+ end
27
28
 
28
- def unification(arg1, arg2, anEnv)
29
- arg1_nil = arg1.nil?
30
- arg2_nil = arg2.nil?
31
- if arg1_nil || arg2_nil
32
- if arg1_nil && arg2_nil
33
- result = Outcome.new(:"#s", anEnv)
34
- else
35
- result = Failure
29
+ def unification(arg1, arg2, anEnv)
30
+ arg1_nil = arg1.nil?
31
+ arg2_nil = arg2.nil?
32
+ if arg1_nil || arg2_nil
33
+ if arg1_nil && arg2_nil
34
+ result = Outcome.new(:"#s", anEnv)
35
+ else
36
+ result = Failure
37
+ end
38
+ return result
36
39
  end
37
- return result
38
- end
39
- new_arg1, new_arg2 = commute_cond(arg1, arg2, anEnv)
40
- result = do_unification(new_arg1, new_arg2, anEnv)
41
- # anEnv.merge(result) if result.successful? && !result.association.empty?
40
+ new_arg1, new_arg2 = commute_cond(arg1, arg2, anEnv)
41
+ result = do_unification(new_arg1, new_arg2, anEnv)
42
+ # anEnv.merge(result) if result.successful? && !result.association.empty?
42
43
 
43
- result
44
- end
44
+ result
45
+ end
45
46
 
46
- private
47
+ private
47
48
 
48
- # table: Unification
49
- # | arg1 | arg2 | Criterion || Unification |
50
- # | isa? Atomic | isa? Atomic | arg1.eq? arg2 is true || { "s", [] } |
51
- # | isa? Atomic | isa? Atomic | arg1.eq? arg2 is false || { "u", [] } |
52
- # | isa? CompositeTerm | isa? Atomic | dont_care || { "u", [] } |
53
- # | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.car, arg2.car) => "s" || { "s", [bindings*] } |
54
- # | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.cdr, arg2.cdr) => "u" || { "u", [] ) | |
55
- # | isa? VariableRef | isa? Atomic | arg1.fresh? is true || { "s", [arg2] } |
56
- # | isa? VariableRef | isa? Atomic | arg1.fresh? is false || |
57
- # | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
58
- # | | unification(arg1.value, arg2) => "u" || { "u", [] } |
59
- # | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is true || { "s", [arg2] } | # What if arg1 occurs in arg2?
60
- # | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is false || |
61
- # | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
62
- # | | unification(arg1.value, arg2) => "u" || { "u", [] } |
63
- # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, true] || { "s", [arg1 <=> arg2] } |
64
- # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, false] || |
65
- # | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
66
- # | | unification(arg1, arg2.value) => "u" || { "u", [] } |
67
- # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [false, false]|| |
68
- # | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
69
- # | | unification(arg1, arg2.value) => "u" || { "u", [] }
70
- def do_unification(arg1, arg2, anEnv)
71
- # require 'debug'
72
- return Outcome.new(:"#s", anEnv) if arg1.equal?(arg2)
49
+ # table: Unification
50
+ # | arg1 | arg2 | Criterion || Unification |
51
+ # | isa? Atomic | isa? Atomic | arg1.eq? arg2 is true || { "s", [] } |
52
+ # | isa? Atomic | isa? Atomic | arg1.eq? arg2 is false || { "u", [] } |
53
+ # | isa? CompositeTerm | isa? Atomic | dont_care || { "u", [] } |
54
+ # | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.car, arg2.car) => "s" || { "s", [bindings*] } |
55
+ # | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.cdr, arg2.cdr) => "u" || { "u", [] ) | |
56
+ # | isa? VariableRef | isa? Atomic | arg1.fresh? is true || { "s", [arg2] } |
57
+ # | isa? VariableRef | isa? Atomic | arg1.fresh? is false || |
58
+ # | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
59
+ # | | unification(arg1.value, arg2) => "u" || { "u", [] } |
60
+ # | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is true || { "s", [arg2] } | # What if arg1 occurs in arg2?
61
+ # | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is false || |
62
+ # | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
63
+ # | | unification(arg1.value, arg2) => "u" || { "u", [] } |
64
+ # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, true] || { "s", [arg1 <=> arg2] } |
65
+ # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, false] || |
66
+ # | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
67
+ # | | unification(arg1, arg2.value) => "u" || { "u", [] } |
68
+ # | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [false, false]|| |
69
+ # | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
70
+ # | | unification(arg1, arg2.value) => "u" || { "u", [] }
71
+ def do_unification(arg1, arg2, anEnv)
72
+ # require 'debug'
73
+ return Outcome.new(:"#s", anEnv) if arg1.equal?(arg2)
73
74
 
74
- result = Outcome.new(:"#u", anEnv) # default case
75
+ result = Outcome.new(:"#u", anEnv) # default case
75
76
 
76
- if arg1.kind_of?(AtomicTerm)
77
- result = BasicSuccess if arg1.eql?(arg2)
78
- elsif arg1.kind_of?(CompositeTerm)
79
- if arg2.kind_of?(CompositeTerm) # AtomicTerm is default case => fail
80
- result = unify_composite_terms(arg1, arg2, anEnv)
81
- end
82
- elsif arg1.kind_of?(VariableRef)
83
- arg1_freshness = arg1.freshness(anEnv)
84
- if arg2.kind_of?(AtomicTerm)
85
- if arg1_freshness.degree == :fresh
86
- result = Outcome.new(:"#s", anEnv)
87
- arg1.associate(arg2, result)
88
- else
89
- result = Outcome.new(:"#s", anEnv) if arg1.value(anEnv).eql?(arg2)
90
- end
91
- elsif arg2.kind_of?(CompositeTerm)
92
- if arg1_freshness.degree == :fresh
93
- result = Outcome.new(:"#s", anEnv)
94
- arg1.associate(arg2, result)
95
- else
96
- # Ground case...
97
- result = unify_composite_terms(arg1_freshness.associated, arg2, anEnv)
77
+ if arg1.kind_of?(AtomicTerm)
78
+ result = BasicSuccess if arg1.eql?(arg2)
79
+ elsif arg1.kind_of?(CompositeTerm)
80
+ if arg2.kind_of?(CompositeTerm) # AtomicTerm is default case => fail
81
+ result = unify_composite_terms(arg1, arg2, anEnv)
98
82
  end
99
- elsif arg2.kind_of?(VariableRef)
100
- freshness = [arg1.fresh?(anEnv), arg2.fresh?(anEnv)]
101
- case freshness
102
- when [false, false] # TODO: confirm this...
103
- result = unification(arg1.value(anEnv), arg2.value(anEnv), anEnv)
104
- when [true, true]
105
- result = Outcome.new(:"#s", anEnv)
106
- if arg1.var_name != arg2.var_name
83
+ elsif arg1.kind_of?(VariableRef)
84
+ arg1_freshness = arg1.freshness(anEnv)
85
+ if arg2.kind_of?(AtomicTerm)
86
+ if arg1_freshness.degree == :fresh
87
+ result = Outcome.new(:"#s", anEnv)
88
+ arg1.associate(arg2, result)
89
+ else
90
+ result = Outcome.new(:"#s", anEnv) if arg1.value(anEnv).eql?(arg2)
91
+ end
92
+ elsif arg2.kind_of?(CompositeTerm)
93
+ if arg1_freshness.degree == :fresh
94
+ result = Outcome.new(:"#s", anEnv)
107
95
  arg1.associate(arg2, result)
108
- arg2.associate(arg1, result)
96
+ else
97
+ # Ground case...
98
+ result = unify_composite_terms(arg1_freshness.associated, arg2, anEnv)
99
+ end
100
+ elsif arg2.kind_of?(VariableRef)
101
+ freshness = [arg1.fresh?(anEnv), arg2.fresh?(anEnv)]
102
+ case freshness
103
+ when [false, false] # TODO: confirm this...
104
+ result = unification(arg1.value(anEnv), arg2.value(anEnv), anEnv)
105
+ when [true, true]
106
+ result = Outcome.new(:"#s", anEnv)
107
+ if arg1.var_name != arg2.var_name
108
+ arg1.associate(arg2, result)
109
+ arg2.associate(arg1, result)
110
+ end
111
+ else
112
+ raise StandardError, "Unsupported freshness combination #{freshness}"
109
113
  end
110
114
  else
111
- raise StandardError, "Unsupported freshness combination #{freshness}"
115
+ arg_kinds = [arg1.class, arg2.class]
116
+ raise StandardError, "Unsupported combination #{arg_kinds}"
112
117
  end
113
- else
114
- arg_kinds = [arg1.class, arg2.class]
115
- raise StandardError, "Unsupported combination #{arg_kinds}"
116
118
  end
117
- end
118
119
 
119
- result
120
- end
120
+ result
121
+ end
121
122
 
122
- # @return [Freshness]
123
- def unify_composite_terms(arg1, arg2, anEnv)
124
- # require 'debug'
125
- result = Outcome.new(:"#u", anEnv)
126
- children1 = arg1.children
127
- children2 = arg2.children
123
+ # @return [Freshness]
124
+ def unify_composite_terms(arg1, arg2, anEnv)
125
+ # require 'debug'
126
+ result = Outcome.new(:"#u", anEnv)
127
+ children1 = arg1.children
128
+ children2 = arg2.children
128
129
 
129
- if children1.size == children2.size
130
- i = 0
131
- subresults = children1.map do |child1|
132
- child2 = children2[i]
133
- i += 1
134
- unification(child1, child2, anEnv)
135
- end
136
- total_success = subresults.all?(&:successful?)
137
- if total_success
138
- memo = Outcome.new(:"#s", anEnv)
139
- associations = subresults.reduce(memo) do |sub_total, outcome|
140
- sub_total.merge(outcome)
141
- sub_total
130
+ if children1.size == children2.size
131
+ i = 0
132
+ subresults = children1.map do |child1|
133
+ child2 = children2[i]
134
+ i += 1
135
+ unification(child1, child2, anEnv)
136
+ end
137
+ total_success = subresults.all?(&:successful?)
138
+ if total_success
139
+ memo = Outcome.new(:"#s", anEnv)
140
+ associations = subresults.reduce(memo) do |sub_total, outcome|
141
+ sub_total.merge(outcome)
142
+ sub_total
143
+ end
144
+ result = memo
142
145
  end
143
- result = memo
144
146
  end
145
- end
146
147
 
147
- result
148
- end
149
- end # class
148
+ result
149
+ end
150
+ end # class
151
+
152
+ Equals.instance.freeze
153
+ end # module
150
154
  end # module
151
- end # module
155
+ end # unless
@@ -4,20 +4,24 @@ require 'singleton'
4
4
  require_relative 'duck_fiber'
5
5
  require_relative 'nullary_relation'
6
6
 
7
- module MiniKraken
8
- module Core
9
- # A nullary relation that unconditionally always fails.
10
- class Fail < NullaryRelation
11
- include Singleton
7
+ unless MiniKraken::Core.constants(false).include? :Fail
8
+ module MiniKraken
9
+ module Core
10
+ # A nullary relation that unconditionally always fails.
11
+ class Fail < NullaryRelation
12
+ include Singleton
12
13
 
13
- def initialize
14
- super('fail', '#u')
15
- end
14
+ def initialize
15
+ super('fail', '#u')
16
+ end
16
17
 
17
- # @return [DuckFiber]
18
- def solver_for(_actuals, _env)
19
- DuckFiber.new(:failure)
20
- end
21
- end # class
18
+ # @return [DuckFiber]
19
+ def solver_for(_actuals, _env)
20
+ DuckFiber.new(:failure)
21
+ end
22
+ end # class
23
+
24
+ Fail.instance.freeze
25
+ end # module
22
26
  end # module
23
- end # module
27
+ end # unless
@@ -4,19 +4,23 @@ require 'singleton'
4
4
  require_relative 'duck_fiber'
5
5
  require_relative 'nullary_relation'
6
6
 
7
- module MiniKraken
8
- module Core
9
- # A nullary relation that unconditionally always fails.
10
- class Succeed < NullaryRelation
11
- include Singleton
7
+ unless MiniKraken::Core.constants(false).include? :Succeed
8
+ module MiniKraken
9
+ module Core
10
+ # A nullary relation that unconditionally always fails.
11
+ class Succeed < NullaryRelation
12
+ include Singleton
12
13
 
13
- def initialize
14
- super('succeed', '#s')
15
- end
14
+ def initialize
15
+ super('succeed', '#s')
16
+ end
16
17
 
17
- def solver_for(_actuals, _env)
18
- DuckFiber.new(:success)
19
- end
20
- end # class
18
+ def solver_for(_actuals, _env)
19
+ DuckFiber.new(:success)
20
+ end
21
+ end # class
22
+
23
+ Succeed.instance.freeze
24
+ end # module
21
25
  end # module
22
- end # module
26
+ end # unless
@@ -67,20 +67,22 @@ module MiniKraken
67
67
 
68
68
  raise StandardError unless orphan
69
69
 
70
+ rank = nil
70
71
  if orphan.rankings.include?(aName)
71
- orphan.rankings[aName]
72
+ rank = orphan.rankings[aName]
72
73
  else
73
74
  other = alternate_names.find do |a_name|
74
- orphan.rankings.include?(a_name)
75
+ rank = orphan.rankings.include?(a_name)
75
76
  end
76
77
  if other
77
- get_rank(other)
78
+ rank = get_rank(other)
78
79
  else
79
80
  rank = orphan.rankings.keys.size
80
81
  orphan.rankings[aName] = rank
81
- rank
82
82
  end
83
83
  end
84
+
85
+ rank
84
86
  end
85
87
 
86
88
  # Record an association between a variable with given user-defined name
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniKraken
4
- VERSION = '0.1.07'
4
+ VERSION = '0.1.08'
5
5
  end
@@ -66,15 +66,14 @@ module MiniKraken
66
66
  end
67
67
 
68
68
  it 'should yield success and set associations' do
69
- # # Weird: this example succeeds if run alone...
70
- # # Covers frame 1-51
71
- # env.add_var(var_q)
72
- # sub_goal = Goal.new(Equals.instance, [corn, ref_q])
73
- # solver = subject.solver_for([succeeds, sub_goal], env)
74
- # outcome = solver.resume
75
- # expect(outcome).to be_successful
76
- # expect(outcome.associations).not_to be_empty
77
- # expect(outcome.associations['q'].first.value).to eq(corn)
69
+ # Covers frame 1-51
70
+ env.add_var(var_q)
71
+ sub_goal = Goal.new(Equals.instance, [corn, ref_q])
72
+ solver = subject.solver_for([succeeds, sub_goal], env)
73
+ outcome = solver.resume
74
+ expect(outcome).to be_successful
75
+ expect(outcome.associations).not_to be_empty
76
+ expect(outcome.associations['q'].first.value).to eq(corn)
78
77
  end
79
78
 
80
79
  it 'should yield fails and set no associations' do
@@ -93,51 +93,6 @@ module MiniKraken
93
93
  expect(outcome2.associations['q'].first.value).to eq(oil)
94
94
  expect(solver.resume).to be_nil
95
95
  end
96
-
97
- it 'should yield success and set associations' do
98
- # # Weird: this example succeeds if run alone...
99
- # # Covers frame 1-51
100
- # env.add_var(var_q)
101
- # sub_goal = Goal.new(Equals.instance, [corn, ref_q])
102
- # solver = subject.solver_for([succeeds, sub_goal], env)
103
- # outcome = solver.resume
104
- # expect(outcome).to be_successful
105
- # expect(outcome.associations).not_to be_empty
106
- # expect(outcome.associations['q'].first.value).to eq(corn)
107
- end
108
-
109
- # it 'should yield fails and set no associations' do
110
- # # Covers frame 1-52
111
- # env.add_var(var_q)
112
- # sub_goal = Goal.new(Equals.instance, [corn, ref_q])
113
- # solver = subject.solver_for([fails, sub_goal], env)
114
- # outcome = solver.resume
115
- # expect(outcome).not_to be_successful
116
- # expect(outcome.associations).to be_empty
117
- # end
118
-
119
- # it 'should yield fails when sub-goals are incompatible' do
120
- # # Covers frame 1-53
121
- # env.add_var(var_q)
122
- # sub_goal1 = Goal.new(Equals.instance, [corn, ref_q])
123
- # sub_goal2 = Goal.new(Equals.instance, [meal, ref_q])
124
- # solver = subject.solver_for([sub_goal1, sub_goal2], env)
125
- # outcome = solver.resume
126
- # expect(outcome).not_to be_successful
127
- # expect(outcome.associations).to be_empty
128
- # end
129
-
130
- # it 'should yield success when sub-goals are same and successful' do
131
- # # Covers frame 1-54
132
- # env.add_var(var_q)
133
- # sub_goal1 = Goal.new(Equals.instance, [corn, ref_q])
134
- # sub_goal2 = Goal.new(Equals.instance, [corn, ref_q])
135
- # solver = subject.solver_for([sub_goal1, sub_goal2], env)
136
- # outcome = solver.resume
137
- # expect(outcome).to be_successful
138
- # expect(outcome.associations).not_to be_empty
139
- # expect(outcome.associations['q'].first.value).to eq(corn)
140
- # end
141
96
  end # context
142
97
  end # describe
143
98
  end # module
@@ -40,12 +40,23 @@ module MiniKraken
40
40
  succeeding = DuckFiber.new(:success)
41
41
  outcome = nil
42
42
  expect { outcome = succeeding.resume }.not_to raise_error
43
- expect(outcome).to eq(BasicSuccess)
43
+ expect(outcome).to be_successful
44
+ expect(outcome.parent).to be_nil
44
45
 
45
46
  # Only one result should be yielded
46
47
  expect(succeeding.resume).to be_nil
47
48
  end
48
49
 
50
+ it 'should yield a distinct success object' do
51
+ instance1 = DuckFiber.new(:success)
52
+ outcome1 = instance1.resume
53
+
54
+ instance2 = DuckFiber.new(:success)
55
+ outcome2 = instance1.resume
56
+
57
+ expect(outcome1).not_to be_equal(outcome2)
58
+ end
59
+
49
60
  it 'should behave like a Fiber yielding a custom outcome' do
50
61
  tailored = DuckFiber.new(:custom) { Outcome.new(:"#s", parent) }
51
62
  outcome = nil
@@ -43,6 +43,7 @@ module MiniKraken
43
43
  let(:meal) { k_symbol(:meal) }
44
44
  let(:oil) { k_symbol(:oil) }
45
45
  let(:olive) { k_symbol(:olive) }
46
+ let(:virgin) { k_symbol(:virgin) }
46
47
  let(:ref_q) { Core::VariableRef.new('q') }
47
48
  let(:ref_x) { Core::VariableRef.new('x') }
48
49
  let(:ref_y) { Core::VariableRef.new('y') }
@@ -88,8 +89,7 @@ module MiniKraken
88
89
  end
89
90
 
90
91
  it 'should return a null list with the succeed goal' do
91
- success = Core::Goal.new(Core::Succeed.instance, [])
92
- instance = RunStarExpression.new('q', success)
92
+ instance = RunStarExpression.new('q', succeeds)
93
93
 
94
94
  # (display (run* q succeed)) ;; => (_0)
95
95
  # Reasoned S2, frame 1:16
@@ -327,16 +327,16 @@ module MiniKraken
327
327
  end
328
328
 
329
329
  # TODO: fix erratic RSpec failure
330
- # it 'should support conjunction of one succeed and a successful goal' do
331
- # subgoal = equals_goal(corn, ref_q)
332
- # goal = conj2_goal(succeeds, subgoal)
333
- # instance = RunStarExpression.new('q', goal)
330
+ it 'should support conjunction of one succeed and a successful goal' do
331
+ subgoal = equals_goal(corn, ref_q)
332
+ goal = conj2_goal(succeeds, subgoal)
333
+ instance = RunStarExpression.new('q', goal)
334
334
 
335
- # # Reasoned S2, frame 1:51
336
- # # (run* q (conj2 succeed (== 'corn q)) ;; => ('corn)
337
- # result = instance.run
338
- # expect(result.car).to eq(corn)
339
- # end
335
+ # Reasoned S2, frame 1:51
336
+ # (run* q (conj2 succeed (== 'corn q)) ;; => ('corn)
337
+ result = instance.run
338
+ expect(result.car).to eq(corn)
339
+ end
340
340
 
341
341
  it 'should support conjunction of one fail and a successful goal' do
342
342
  subgoal = equals_goal(corn, ref_q)
@@ -480,6 +480,30 @@ module MiniKraken
480
480
  expect(result.car).to eq(oil)
481
481
  expect(result.cdr.car).to eq(olive)
482
482
  end
483
+
484
+ it 'should accept nesting of disj2 and conj2 (IV)' do
485
+ oil_goal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
486
+ disja = disj2_goal(succeeds, oil_goal)
487
+ olive_goal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
488
+ disjb = disj2_goal(olive_goal, disja)
489
+ virgin_goal = Core::Goal.new(Core::Equals.instance, [virgin, ref_x])
490
+ conjunction = conj2_goal(virgin_goal, fails)
491
+ goal = disj2_goal(conjunction, disjb)
492
+ instance = RunStarExpression.new('x', goal)
493
+
494
+ # Reasoned S2, frame 1:65
495
+ # (run* x (disj2
496
+ # (conj2(== 'virgin x) fails)
497
+ # (disj2
498
+ # (== 'olive x)
499
+ # (dis2
500
+ # succeeds
501
+ # (== 'oil x))))) ;; => (olive _0 oil)
502
+ result = instance.run
503
+ expect(result.car).to eq(olive)
504
+ expect(result.cdr.car).to eq(any_value(0))
505
+ expect(result.cdr.cdr.car).to eq(oil)
506
+ end
483
507
  end # context
484
508
  end # describe
485
509
  end # module
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../lib/mini_kraken/version/'
4
+
3
5
  RSpec.describe MiniKraken do
4
6
  it 'has a version number' do
5
7
  expect(MiniKraken::VERSION).not_to be nil
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/setup'
4
3
  require 'rspec' # Use the RSpec framework
5
4
 
6
5
  RSpec.configure do |config|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_kraken
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.07
4
+ version: 0.1.08
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-23 00:00:00.000000000 Z
11
+ date: 2020-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler