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 +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/mini_kraken/core/conj2.rb +57 -53
- data/lib/mini_kraken/core/disj2.rb +52 -48
- data/lib/mini_kraken/core/duck_fiber.rb +1 -1
- data/lib/mini_kraken/core/equals.rb +124 -120
- data/lib/mini_kraken/core/fail.rb +18 -14
- data/lib/mini_kraken/core/succeed.rb +17 -13
- data/lib/mini_kraken/core/vocabulary.rb +6 -4
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/core/conj2_spec.rb +8 -9
- data/spec/core/disj2_spec.rb +0 -45
- data/spec/core/duck_fiber_spec.rb +12 -1
- data/spec/glue/run_star_expression_spec.rb +35 -11
- data/spec/mini_kraken_spec.rb +2 -0
- data/spec/spec_helper.rb +0 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32a6f92773457338343dffa1343906883ffdbafbf7f8fd9ace170062cc34d345
|
4
|
+
data.tar.gz: cb7d3e6b88f8bea952bea7f214eaf73af97857de6958f65592a15b7ee7ef2510
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db730590bcc99203f8983fe19b5e0d0b90c77819c07875bc34c100bc843c9b3e5e3e148833149d3264847a190cc9ce5bcf1fa31f74723b8c8e8006a56948d738
|
7
|
+
data.tar.gz: 4cb5cb8f327e7cb81028621ad67e36afecee8bc90beac1cd6449573b85092191d39d55dfbe2de9f52a5868a66bf4c8288a3223b41815528fb651eead36d3f4fa
|
data/CHANGELOG.md
CHANGED
@@ -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
|
-
|
10
|
-
module
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def initialize
|
18
|
+
super('conj2', nil)
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
63
|
+
else
|
64
|
+
Fiber.yield outcome1
|
61
65
|
end
|
62
|
-
|
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
|
-
|
70
|
-
|
71
|
-
end # class
|
74
|
+
Conj2.instance.freeze
|
75
|
+
end # module
|
72
76
|
end # module
|
73
|
-
end #
|
77
|
+
end # unless
|
@@ -6,62 +6,66 @@ require_relative 'goal'
|
|
6
6
|
require_relative 'goal_relation'
|
7
7
|
require_relative 'outcome'
|
8
8
|
|
9
|
-
|
10
|
-
module
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def initialize
|
18
|
+
super('disj2', nil)
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
outcome1.parent = voc unless outcome1.parent
|
46
|
+
if outcome1.successful?
|
47
|
+
Fiber.yield outcome1
|
48
|
+
outcome1.clear
|
49
|
+
end
|
48
50
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
break unless outcome2
|
51
|
+
f2 = g2.attain(voc)
|
52
|
+
loop do
|
53
|
+
outcome2 = f2.resume
|
54
|
+
break unless outcome2
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
64
|
-
|
65
|
-
end # class
|
68
|
+
Disj2.instance.freeze
|
69
|
+
end # module
|
66
70
|
end # module
|
67
|
-
end #
|
71
|
+
end # unless
|
@@ -7,145 +7,149 @@ require_relative 'duck_fiber'
|
|
7
7
|
require_relative 'variable'
|
8
8
|
require_relative 'variable_ref'
|
9
9
|
|
10
|
-
|
11
|
-
module
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def initialize
|
18
|
+
super('equals', '==')
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
44
|
-
|
44
|
+
result
|
45
|
+
end
|
45
46
|
|
46
|
-
|
47
|
+
private
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
75
|
+
result = Outcome.new(:"#u", anEnv) # default case
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
120
|
-
|
120
|
+
result
|
121
|
+
end
|
121
122
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
148
|
-
|
149
|
-
|
148
|
+
result
|
149
|
+
end
|
150
|
+
end # class
|
151
|
+
|
152
|
+
Equals.instance.freeze
|
153
|
+
end # module
|
150
154
|
end # module
|
151
|
-
end #
|
155
|
+
end # unless
|
@@ -4,20 +4,24 @@ require 'singleton'
|
|
4
4
|
require_relative 'duck_fiber'
|
5
5
|
require_relative 'nullary_relation'
|
6
6
|
|
7
|
-
|
8
|
-
module
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
14
|
+
def initialize
|
15
|
+
super('fail', '#u')
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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 #
|
27
|
+
end # unless
|
@@ -4,19 +4,23 @@ require 'singleton'
|
|
4
4
|
require_relative 'duck_fiber'
|
5
5
|
require_relative 'nullary_relation'
|
6
6
|
|
7
|
-
|
8
|
-
module
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
14
|
+
def initialize
|
15
|
+
super('succeed', '#s')
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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 #
|
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
|
data/lib/mini_kraken/version.rb
CHANGED
data/spec/core/conj2_spec.rb
CHANGED
@@ -66,15 +66,14 @@ module MiniKraken
|
|
66
66
|
end
|
67
67
|
|
68
68
|
it 'should yield success and set associations' do
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
data/spec/core/disj2_spec.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
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
|
-
#
|
336
|
-
#
|
337
|
-
|
338
|
-
|
339
|
-
|
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
|
data/spec/mini_kraken_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2020-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|