mini_kraken 0.1.06 → 0.1.11
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/.travis.yml +5 -1
- data/CHANGELOG.md +56 -1
- data/README.md +11 -3
- data/lib/mini_kraken/core/association_walker.rb +1 -1
- data/lib/mini_kraken/core/base_arg.rb +10 -0
- data/lib/mini_kraken/core/conj2.rb +58 -65
- data/lib/mini_kraken/core/cons_cell.rb +49 -43
- data/lib/mini_kraken/core/def_relation.rb +49 -0
- data/lib/mini_kraken/core/disj2.rb +72 -0
- data/lib/mini_kraken/core/duck_fiber.rb +1 -1
- data/lib/mini_kraken/core/environment.rb +1 -1
- data/lib/mini_kraken/core/equals.rb +134 -126
- data/lib/mini_kraken/core/fail.rb +18 -14
- data/lib/mini_kraken/core/formal_arg.rb +22 -0
- data/lib/mini_kraken/core/formal_ref.rb +24 -0
- data/lib/mini_kraken/core/goal_arg.rb +5 -3
- data/lib/mini_kraken/core/goal_relation.rb +13 -0
- data/lib/mini_kraken/core/goal_template.rb +60 -0
- data/lib/mini_kraken/core/k_boolean.rb +31 -0
- data/lib/mini_kraken/core/outcome.rb +40 -24
- data/lib/mini_kraken/core/succeed.rb +17 -13
- data/lib/mini_kraken/core/vocabulary.rb +37 -17
- data/lib/mini_kraken/glue/fresh_env.rb +45 -3
- data/lib/mini_kraken/glue/run_star_expression.rb +45 -19
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/core/conj2_spec.rb +8 -9
- data/spec/core/cons_cell_spec.rb +8 -0
- data/spec/core/def_relation_spec.rb +96 -0
- data/spec/core/disj2_spec.rb +99 -0
- data/spec/core/duck_fiber_spec.rb +12 -1
- data/spec/core/equals_spec.rb +3 -3
- data/spec/core/goal_template_spec.rb +74 -0
- data/spec/core/k_boolean_spec.rb +107 -0
- data/spec/core/outcome_spec.rb +48 -0
- data/spec/core/vocabulary_spec.rb +11 -5
- data/spec/glue/fresh_env_spec.rb +27 -1
- data/spec/glue/run_star_expression_spec.rb +538 -70
- data/spec/mini_kraken_spec.rb +2 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/factory_methods.rb +17 -1
- metadata +19 -2
@@ -7,145 +7,153 @@ require_relative 'duck_fiber'
|
|
7
7
|
require_relative 'variable'
|
8
8
|
require_relative 'variable_ref'
|
9
9
|
|
10
|
-
|
11
|
-
module
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
27
|
-
|
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
|
36
|
-
end
|
37
|
-
return result
|
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
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
super('equals', '==')
|
38
19
|
end
|
39
|
-
|
40
|
-
|
41
|
-
# anEnv
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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)
|
73
|
-
|
74
|
-
result = Outcome.new(:"#u", anEnv) # default case
|
75
|
-
|
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)
|
20
|
+
|
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
|
28
|
+
|
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.success(anEnv)
|
88
35
|
else
|
89
|
-
result = Outcome.
|
36
|
+
result = Outcome.failure(anEnv)
|
90
37
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
38
|
+
return result
|
39
|
+
end
|
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?
|
43
|
+
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
|
50
|
+
# table: Unification
|
51
|
+
# | arg1 | arg2 | Criterion || Unification |
|
52
|
+
# | isa? Atomic | isa? Atomic | arg1.eq? arg2 is true || { "s", [] } |
|
53
|
+
# | isa? Atomic | isa? Atomic | arg1.eq? arg2 is false || { "u", [] } |
|
54
|
+
# | isa? CompositeTerm | isa? Atomic | dont_care || { "u", [] } |
|
55
|
+
# | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.car, arg2.car) => "s" || { "s", [bindings*] } |
|
56
|
+
# | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.cdr, arg2.cdr) => "u" || { "u", [] ) | |
|
57
|
+
# | isa? VariableRef | isa? Atomic | arg1.fresh? is true || { "s", [arg2] } |
|
58
|
+
# | isa? VariableRef | isa? Atomic | arg1.fresh? is false || |
|
59
|
+
# | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
|
60
|
+
# | | unification(arg1.value, arg2) => "u" || { "u", [] } |
|
61
|
+
# | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is true || { "s", [arg2] } | # What if arg1 occurs in arg2?
|
62
|
+
# | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is false || |
|
63
|
+
# | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
|
64
|
+
# | | unification(arg1.value, arg2) => "u" || { "u", [] } |
|
65
|
+
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, true] || { "s", [arg1 <=> arg2] } |
|
66
|
+
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, false] || |
|
67
|
+
# | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
|
68
|
+
# | | unification(arg1, arg2.value) => "u" || { "u", [] } |
|
69
|
+
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [false, false]|| |
|
70
|
+
# | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
|
71
|
+
# | | unification(arg1, arg2.value) => "u" || { "u", [] }
|
72
|
+
def do_unification(arg1, arg2, anEnv)
|
73
|
+
# require 'debug'
|
74
|
+
return Outcome.success(anEnv) if arg1.equal?(arg2)
|
75
|
+
|
76
|
+
result = Outcome.failure(anEnv) # default case
|
77
|
+
|
78
|
+
if arg1.kind_of?(AtomicTerm)
|
79
|
+
result = Outcome.success(anEnv) if arg1.eql?(arg2)
|
80
|
+
elsif arg1.kind_of?(CompositeTerm)
|
81
|
+
if arg2.kind_of?(CompositeTerm) # AtomicTerm is default case => fail
|
82
|
+
result = unify_composite_terms(arg1, arg2, anEnv)
|
98
83
|
end
|
99
|
-
elsif
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
84
|
+
elsif arg1.kind_of?(VariableRef)
|
85
|
+
arg1_freshness = arg1.freshness(anEnv)
|
86
|
+
if arg2.kind_of?(AtomicTerm)
|
87
|
+
if arg1_freshness.degree == :fresh
|
88
|
+
result = Outcome.success(anEnv)
|
89
|
+
arg1.associate(arg2, result)
|
90
|
+
else
|
91
|
+
result = Outcome.success(anEnv) if arg1.value(anEnv).eql?(arg2)
|
92
|
+
end
|
93
|
+
elsif arg2.kind_of?(CompositeTerm)
|
94
|
+
if arg1_freshness.degree == :fresh
|
95
|
+
result = Outcome.success(anEnv)
|
96
|
+
arg1.associate(arg2, result)
|
97
|
+
else
|
98
|
+
# Ground case...
|
99
|
+
result = unify_composite_terms(arg1_freshness.associated, arg2, anEnv)
|
100
|
+
end
|
101
|
+
elsif arg2.kind_of?(VariableRef)
|
102
|
+
freshness = [arg1.fresh?(anEnv), arg2.fresh?(anEnv)]
|
103
|
+
case freshness
|
104
|
+
when [false, false] # TODO: confirm this...
|
105
|
+
result = unification(arg1.value(anEnv), arg2.value(anEnv), anEnv)
|
106
|
+
when [true, true]
|
107
|
+
result = Outcome.success(anEnv)
|
108
|
+
if arg1.var_name != arg2.var_name
|
109
|
+
arg1.associate(arg2, result)
|
110
|
+
arg2.associate(arg1, result)
|
111
|
+
end
|
112
|
+
when [true, false]
|
113
|
+
result = Outcome.success(anEnv)
|
107
114
|
arg1.associate(arg2, result)
|
108
|
-
|
115
|
+
else
|
116
|
+
raise StandardError, "Unsupported freshness combination #{freshness}"
|
109
117
|
end
|
110
118
|
else
|
111
|
-
|
119
|
+
arg_kinds = [arg1.class, arg2.class]
|
120
|
+
raise StandardError, "Unsupported combination #{arg_kinds}"
|
112
121
|
end
|
113
|
-
else
|
114
|
-
arg_kinds = [arg1.class, arg2.class]
|
115
|
-
raise StandardError, "Unsupported combination #{arg_kinds}"
|
116
122
|
end
|
123
|
+
|
124
|
+
result
|
117
125
|
end
|
118
126
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
sub_total
|
127
|
+
# @return [Freshness]
|
128
|
+
def unify_composite_terms(arg1, arg2, anEnv)
|
129
|
+
# require 'debug'
|
130
|
+
result = Outcome.failure(anEnv)
|
131
|
+
children1 = arg1.children
|
132
|
+
children2 = arg2.children
|
133
|
+
|
134
|
+
if children1.size == children2.size
|
135
|
+
i = 0
|
136
|
+
subresults = children1.map do |child1|
|
137
|
+
child2 = children2[i]
|
138
|
+
i += 1
|
139
|
+
unification(child1, child2, anEnv)
|
140
|
+
end
|
141
|
+
total_success = subresults.all?(&:successful?)
|
142
|
+
if total_success
|
143
|
+
memo = Outcome.success(anEnv)
|
144
|
+
associations = subresults.reduce(memo) do |sub_total, outcome|
|
145
|
+
sub_total.merge(outcome)
|
146
|
+
sub_total
|
147
|
+
end
|
148
|
+
result = memo
|
142
149
|
end
|
143
|
-
result = memo
|
144
150
|
end
|
151
|
+
|
152
|
+
result
|
145
153
|
end
|
154
|
+
end # class
|
146
155
|
|
147
|
-
|
148
|
-
|
149
|
-
end # class
|
156
|
+
Equals.instance.freeze
|
157
|
+
end # module
|
150
158
|
end # module
|
151
|
-
end #
|
159
|
+
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
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniKraken
|
4
|
+
module Core
|
5
|
+
# The generalization of any iem that can be
|
6
|
+
# passed as arugement to a goal.
|
7
|
+
class FormalArg
|
8
|
+
# @return [String]
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
def initialize(aName)
|
12
|
+
@name = validated_name(aName)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def validated_name(aName)
|
18
|
+
aName
|
19
|
+
end
|
20
|
+
end # class
|
21
|
+
end # module
|
22
|
+
end # module
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_arg'
|
4
|
+
|
5
|
+
module MiniKraken
|
6
|
+
module Core
|
7
|
+
# A formal reference represents the occurrence of a formal argument name in a
|
8
|
+
# goal template argument list.
|
9
|
+
class FormalRef < BaseArg
|
10
|
+
# @return [String]
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
def initialize(aName)
|
14
|
+
@name = validated_name(aName)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def validated_name(aName)
|
20
|
+
aName
|
21
|
+
end
|
22
|
+
end # class
|
23
|
+
end # module
|
24
|
+
end # module
|
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'base_arg'
|
4
|
+
|
3
5
|
module MiniKraken
|
4
6
|
module Core
|
5
|
-
# The generalization of any
|
6
|
-
# passed as arugement to a goal
|
7
|
-
class GoalArg
|
7
|
+
# The generalization of any item that can be
|
8
|
+
# passed as arugement to a goal object
|
9
|
+
class GoalArg < BaseArg
|
8
10
|
end # class
|
9
11
|
end # module
|
10
12
|
end # module
|
@@ -10,6 +10,19 @@ module MiniKraken
|
|
10
10
|
def arity
|
11
11
|
2
|
12
12
|
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def validated_args(actuals)
|
17
|
+
actuals.each do |arg|
|
18
|
+
unless arg.kind_of?(Goal)
|
19
|
+
prefix = "#{name} expects goal as argument, found a "
|
20
|
+
raise StandardError, prefix + "'#{arg.class}'"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
actuals
|
25
|
+
end
|
13
26
|
end # class
|
14
27
|
end # module
|
15
28
|
end # module
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_arg'
|
4
|
+
|
5
|
+
module MiniKraken
|
6
|
+
module Core
|
7
|
+
# A meta-goal that is parametrized with generic formal arguments.
|
8
|
+
# The individual goals are instantiated when the formal arguments
|
9
|
+
# are bound to goal arguments
|
10
|
+
class GoalTemplate < BaseArg
|
11
|
+
# @return [Array<BaseArg>}] Arguments of goal template.
|
12
|
+
attr_reader :args
|
13
|
+
|
14
|
+
# @return [Relation] Main relation for the goal template
|
15
|
+
attr_reader :relation
|
16
|
+
|
17
|
+
def initialize(aRelation, theArgs)
|
18
|
+
@relation = validated_relation(aRelation)
|
19
|
+
@args = validated_args(theArgs)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param formals [Array<FormalArg>] Array of formal arguments
|
23
|
+
# @param actuals [Array<GoalArg>] Array of actual arguments
|
24
|
+
# @return [Goal] instantiate a goal object given the actuals and environment
|
25
|
+
def instantiate(formals, actuals)
|
26
|
+
formals2actuals = {}
|
27
|
+
formals.each_with_index do |frml, i|
|
28
|
+
formals2actuals[frml.name] = actuals[i]
|
29
|
+
end
|
30
|
+
|
31
|
+
do_instantiate(formals2actuals)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def validated_relation(aRelation)
|
37
|
+
aRelation
|
38
|
+
end
|
39
|
+
|
40
|
+
def validated_args(theArgs)
|
41
|
+
theArgs
|
42
|
+
end
|
43
|
+
|
44
|
+
def do_instantiate(formals2actuals)
|
45
|
+
goal_args = []
|
46
|
+
args.each do |arg|
|
47
|
+
if arg.kind_of?(FormalRef)
|
48
|
+
goal_args << formals2actuals[arg.name]
|
49
|
+
elsif arg.kind_of?(GoalTemplate)
|
50
|
+
goal_args << arg.send(:do_instantiate, formals2actuals)
|
51
|
+
else
|
52
|
+
goal_args << arg
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Goal.new(relation, goal_args)
|
57
|
+
end
|
58
|
+
end # class
|
59
|
+
end # module
|
60
|
+
end # module
|