mini_kraken 0.1.07 → 0.1.08

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