mini_kraken 0.1.01 → 0.1.02
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 +3 -0
- data/README.md +0 -3
- data/lib/mini_kraken/core/any_value.rb +29 -0
- data/lib/mini_kraken/core/association.rb +21 -0
- data/lib/mini_kraken/core/association_walker.rb +179 -0
- data/lib/mini_kraken/core/atomic_term.rb +64 -0
- data/lib/mini_kraken/core/binary_relation.rb +61 -0
- data/lib/mini_kraken/core/composite_term.rb +54 -0
- data/lib/mini_kraken/core/cons_cell.rb +44 -0
- data/lib/mini_kraken/core/duck_fiber.rb +44 -0
- data/lib/mini_kraken/core/environment.rb +59 -0
- data/lib/mini_kraken/core/equals.rb +216 -0
- data/lib/mini_kraken/core/fail.rb +8 -5
- data/lib/mini_kraken/core/freshness.rb +42 -0
- data/lib/mini_kraken/core/goal.rb +31 -6
- data/lib/mini_kraken/core/k_integer.rb +15 -0
- data/lib/mini_kraken/core/k_symbol.rb +15 -0
- data/lib/mini_kraken/core/nullary_relation.rb +11 -3
- data/lib/mini_kraken/core/outcome.rb +35 -0
- data/lib/mini_kraken/core/relation.rb +31 -4
- data/lib/mini_kraken/core/succeed.rb +13 -1
- data/lib/mini_kraken/core/term.rb +7 -0
- data/lib/mini_kraken/core/variable.rb +45 -3
- data/lib/mini_kraken/core/variable_ref.rb +76 -0
- data/lib/mini_kraken/core/vocabulary.rb +161 -0
- data/lib/mini_kraken/glue/fresh_env.rb +31 -0
- data/lib/mini_kraken/glue/run_star_expression.rb +43 -0
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/core/association_spec.rb +38 -0
- data/spec/core/association_walker_spec.rb +191 -0
- data/spec/core/cons_cell_spec.rb +63 -0
- data/spec/core/duck_fiber_spec.rb +62 -0
- data/spec/core/environment_spec.rb +154 -0
- data/spec/core/equals_spec.rb +289 -0
- data/spec/core/fail_spec.rb +16 -0
- data/spec/core/goal_spec.rb +36 -10
- data/spec/core/k_symbol_spec.rb +72 -0
- data/spec/core/succeed_spec.rb +43 -0
- data/spec/core/variable_ref_spec.rb +31 -0
- data/spec/core/variable_spec.rb +11 -3
- data/spec/core/vocabulary_spec.rb +188 -0
- data/spec/glue/fresh_env_spec.rb +36 -0
- data/spec/glue/run_star_expression_spec.rb +247 -0
- data/spec/support/factory_methods.rb +54 -0
- metadata +46 -13
- data/lib/mini_kraken/core/facade.rb +0 -45
- data/lib/mini_kraken/core/formal_arg.rb +0 -6
- data/lib/mini_kraken/core/publisher.rb +0 -27
- data/lib/mini_kraken/core/run_star_expression.rb +0 -34
- data/lib/mini_kraken/dsl/kraken_dsl.rb +0 -12
- data/spec/core/facade_spec.rb +0 -38
- data/spec/core/run_star_expression_spec.rb +0 -43
- data/spec/dsl/kraken_dsl_spec.rb +0 -31
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'composite_term'
|
2
|
+
|
3
|
+
module MiniKraken
|
4
|
+
module Core
|
5
|
+
class ConsCell < CompositeTerm
|
6
|
+
attr_reader :car
|
7
|
+
attr_reader :cdr
|
8
|
+
|
9
|
+
def initialize(obj1, obj2 = nil)
|
10
|
+
@car = obj1
|
11
|
+
@cdr = obj2
|
12
|
+
end
|
13
|
+
|
14
|
+
def children
|
15
|
+
[car, cdr]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return true if it is an empty list, otherwise false.
|
19
|
+
# A list is empty, when both car and cdr fields are nil.
|
20
|
+
def null?
|
21
|
+
car.nil? && cdr.nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
return false unless other.respond_to?(:car)
|
26
|
+
(car == other.car) && (cdr == other.cdr)
|
27
|
+
end
|
28
|
+
|
29
|
+
def eql?(other)
|
30
|
+
(self.class == other.class) && car.eql?(other.car) && cdr.eql?(other.cdr)
|
31
|
+
end
|
32
|
+
|
33
|
+
def quote(anEnv)
|
34
|
+
return self if null?
|
35
|
+
new_car = car.nil? ? nil : car.quote(anEnv)
|
36
|
+
new_cdr = cdr.nil? ? nil : cdr.quote(anEnv)
|
37
|
+
ConsCell.new(new_car, new_cdr)
|
38
|
+
end
|
39
|
+
end # class
|
40
|
+
|
41
|
+
# Constant representing the null (empty) list.
|
42
|
+
NullList = ConsCell.new(nil, nil).freeze
|
43
|
+
end # module
|
44
|
+
end # module
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'outcome'
|
2
|
+
|
3
|
+
module MiniKraken
|
4
|
+
module Core
|
5
|
+
# A mock class that mimicks the behavior of a Fiber instance.
|
6
|
+
class DuckFiber
|
7
|
+
# @return [Outcome] The sole outcome to yield.
|
8
|
+
attr_reader :outcome
|
9
|
+
|
10
|
+
# @return [Symbol] one of: :initial, :yielded
|
11
|
+
attr_reader :state
|
12
|
+
|
13
|
+
# @param outcomeKind [Symbol] One of: :failure, :basic_success, :custom
|
14
|
+
def initialize(outcomeKind, &customization)
|
15
|
+
@state = :initial
|
16
|
+
if outcomeKind == :custom && block_given?
|
17
|
+
@outcome = customization.call
|
18
|
+
else
|
19
|
+
@outcome = valid_outcome(outcomeKind)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def resume(*_args)
|
24
|
+
if state == :initial
|
25
|
+
@state = :yielded
|
26
|
+
return outcome
|
27
|
+
else
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def valid_outcome(outcomeKind)
|
33
|
+
case outcomeKind
|
34
|
+
when :failure
|
35
|
+
Failure
|
36
|
+
when :success
|
37
|
+
BasicSuccess
|
38
|
+
else
|
39
|
+
raise StandardError, "Unknonw outcome kind #{outcomeKind}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end # class
|
43
|
+
end # module
|
44
|
+
end # module
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative 'vocabulary'
|
2
|
+
|
3
|
+
module MiniKraken
|
4
|
+
module Core
|
5
|
+
class Environment
|
6
|
+
include Vocabulary # Use mix-in module
|
7
|
+
|
8
|
+
# @return [Hash] Pairs of the kind {String => Variable}
|
9
|
+
attr_reader :vars
|
10
|
+
|
11
|
+
# @param aParent [Environment, NilClass] Parent environment to this one.
|
12
|
+
def initialize(aParent = nil)
|
13
|
+
init_vocabulary(aParent)
|
14
|
+
@vars = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param aVariable [Variable]
|
18
|
+
def add_var(aVariable)
|
19
|
+
name = aVariable.name
|
20
|
+
if vars.include?(name)
|
21
|
+
err_msg = "Variable with name '#{name}' already exists."
|
22
|
+
raise StandardError, err_msg
|
23
|
+
end
|
24
|
+
vars[name] = aVariable
|
25
|
+
end
|
26
|
+
|
27
|
+
# Handler for the event: an outcome has been produced.
|
28
|
+
# Can be overridden in other to propagate associations from child
|
29
|
+
# @param descendent [Outcome]
|
30
|
+
def propagate(descendent)
|
31
|
+
# Rollout associations from hierarchy
|
32
|
+
walker = descendent.ancestor_walker
|
33
|
+
begin
|
34
|
+
env = walker.resume
|
35
|
+
break if env.nil?
|
36
|
+
env.do_propagate(descendent) if env.kind_of?(Environment)
|
37
|
+
end until env.equal?(self)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Move associations from descendent outcome object
|
41
|
+
def do_propagate(descendent)
|
42
|
+
return unless descendent.successful?
|
43
|
+
|
44
|
+
vars.each_key do |var_name|
|
45
|
+
assocs = descendent[var_name]
|
46
|
+
assocs.each do |assoc|
|
47
|
+
own = self[var_name]
|
48
|
+
add_assoc(assoc) unless assoc.equal?(own)
|
49
|
+
end
|
50
|
+
descendent.associations.delete(var_name) unless assocs.empty?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def merge_vars(descendent)
|
55
|
+
descendent.vars.each_value { |vr| add_var(vr) }
|
56
|
+
end
|
57
|
+
end # class
|
58
|
+
end # module
|
59
|
+
end # module
|
@@ -0,0 +1,216 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require_relative 'binary_relation'
|
3
|
+
# require_relative 'any_value'
|
4
|
+
require_relative 'duck_fiber'
|
5
|
+
require_relative 'variable'
|
6
|
+
require_relative 'variable_ref'
|
7
|
+
|
8
|
+
module MiniKraken
|
9
|
+
module Core
|
10
|
+
# equals tries to unify two terms
|
11
|
+
class Equals < BinaryRelation
|
12
|
+
include Singleton
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
super('equals', '==')
|
16
|
+
end
|
17
|
+
|
18
|
+
=begin
|
19
|
+
double data flow:
|
20
|
+
A goal has actual arguments
|
21
|
+
When its corresponding relation is invoked
|
22
|
+
this one will return two things: (a success, the bindings/constraints
|
23
|
+
resulting from the relation)
|
24
|
+
the bindings/constraints can be undone if enclosing fails, otherwise
|
25
|
+
the bindings/constraints are rolled up.
|
26
|
+
=end
|
27
|
+
=begin
|
28
|
+
def unify(aGoal, vars)
|
29
|
+
arg1, arg2 = aGoal.actuals
|
30
|
+
arg_kinds = [arg1.kind_of?(VariableRef), arg2.kind_of?(VariableRef)]
|
31
|
+
case arg_kinds
|
32
|
+
when [false, false]
|
33
|
+
if arg1.eql?(arg2)
|
34
|
+
result = Outcome.new(:"#s", [])
|
35
|
+
else
|
36
|
+
result = Failure
|
37
|
+
end
|
38
|
+
when [false, true]
|
39
|
+
if arg2.fresh?
|
40
|
+
arg2.bind_to(arg1)
|
41
|
+
result = Outcome.new(:"#s", [arg1])
|
42
|
+
else
|
43
|
+
if arg2.value.eql?(arg1)
|
44
|
+
result = Outcome.new(:"#s", [arg1])
|
45
|
+
else
|
46
|
+
result = Failure
|
47
|
+
end
|
48
|
+
end
|
49
|
+
when [true, false]
|
50
|
+
if arg1.fresh?
|
51
|
+
arg1.bind_to(arg2)
|
52
|
+
result = Outcome.new(:"#s", [arg2])
|
53
|
+
else
|
54
|
+
if arg1.value.eql?(arg2)
|
55
|
+
result = Outcome.new(:"#s", [arg2])
|
56
|
+
else
|
57
|
+
result = Failure
|
58
|
+
end
|
59
|
+
end
|
60
|
+
when [true, true]
|
61
|
+
case [arg1.fresh?, arg2.fresh?]
|
62
|
+
when [false, false]
|
63
|
+
if arg1.value == arg2.value
|
64
|
+
result = Outcome.new(:"#s", [arg1.value])
|
65
|
+
else
|
66
|
+
result = Failure
|
67
|
+
end
|
68
|
+
when [false, true]
|
69
|
+
arg2.bind_to(arg1)
|
70
|
+
result = Outcome.new(:"#s", arg1.value)
|
71
|
+
when [true, false]
|
72
|
+
arg1.bind_to(arg2)
|
73
|
+
result = Outcome.new(:"#s", arg2.value)
|
74
|
+
when [true, true]
|
75
|
+
if arg1.variable.name == arg2.variable.name
|
76
|
+
result = Outcome.new(:"#s", [])
|
77
|
+
else
|
78
|
+
# TODO: add constraints
|
79
|
+
result = Outcome.new(:"#s", [])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
result
|
85
|
+
end
|
86
|
+
=end
|
87
|
+
|
88
|
+
def solver_for(actuals, anEnv)
|
89
|
+
arg1, arg2 = *actuals
|
90
|
+
DuckFiber.new(:custom) { unification(arg1, arg2, anEnv) }
|
91
|
+
end
|
92
|
+
|
93
|
+
def unification(arg1, arg2, anEnv)
|
94
|
+
arg1_nil = arg1.nil?
|
95
|
+
arg2_nil = arg2.nil?
|
96
|
+
if arg1_nil || arg2_nil
|
97
|
+
if arg1_nil && arg2_nil
|
98
|
+
result = Outcome.new(:"#s", anEnv)
|
99
|
+
else
|
100
|
+
result = Failure
|
101
|
+
end
|
102
|
+
return result
|
103
|
+
end
|
104
|
+
new_arg1, new_arg2 = commute_cond(arg1, arg2, anEnv)
|
105
|
+
result = do_unification(new_arg1, new_arg2, anEnv)
|
106
|
+
# anEnv.merge(result) if result.successful? && !result.association.empty?
|
107
|
+
|
108
|
+
result
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
# table: Unification
|
114
|
+
# | arg1 | arg2 | Criterion || Unification |
|
115
|
+
# | isa? Atomic | isa? Atomic | arg1.eq? arg2 is true || { "s", [] } |
|
116
|
+
# | isa? Atomic | isa? Atomic | arg1.eq? arg2 is false || { "u", [] } |
|
117
|
+
# | isa? CompositeTerm | isa? Atomic | dont_care || { "u", [] } |
|
118
|
+
# | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.car, arg2.car) => "s" || { "s", [bindings*] } |
|
119
|
+
# | isa? CompositeTerm | isa? CompositeTerm | unification(arg1.cdr, arg2.cdr) => "u" || { "u", [] ) | |
|
120
|
+
# | isa? VariableRef | isa? Atomic | arg1.fresh? is true || { "s", [arg2] } |
|
121
|
+
# | isa? VariableRef | isa? Atomic | arg1.fresh? is false || |
|
122
|
+
# | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
|
123
|
+
# | | unification(arg1.value, arg2) => "u" || { "u", [] } |
|
124
|
+
# | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is true || { "s", [arg2] } | # What if arg1 occurs in arg2?
|
125
|
+
# | isa? VariableRef | isa? CompositeTerm | arg1.fresh? is false || |
|
126
|
+
# | | unification(arg1.value, arg2) => "s" || { "s", [bindings*] } |
|
127
|
+
# | | unification(arg1.value, arg2) => "u" || { "u", [] } |
|
128
|
+
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, true] || { "s", [arg1 <=> arg2] } |
|
129
|
+
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [true, false] || |
|
130
|
+
# | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
|
131
|
+
# | | unification(arg1, arg2.value) => "u" || { "u", [] } |
|
132
|
+
# | isa? VariableRef | isa? VariableRef | arg1.fresh?, arg2.fresh? => [false, false]|| |
|
133
|
+
# | | unification(arg1, arg2.value) => "s" || { "s", [bindings*] } |
|
134
|
+
# | | unification(arg1, arg2.value) => "u" || { "u", [] }
|
135
|
+
def do_unification(arg1, arg2, anEnv)
|
136
|
+
# require 'debug'
|
137
|
+
return Outcome.new(:"#s", anEnv) if arg1.equal?(arg2)
|
138
|
+
result = Outcome.new(:"#u", anEnv) # default case
|
139
|
+
|
140
|
+
if arg1.kind_of?(AtomicTerm)
|
141
|
+
result = BasicSuccess if arg1.eql?(arg2)
|
142
|
+
elsif arg1.kind_of?(CompositeTerm)
|
143
|
+
if arg2.kind_of?(CompositeTerm) # AtomicTerm is default case => fail
|
144
|
+
result = unify_composite_terms(arg1, arg2, anEnv)
|
145
|
+
end
|
146
|
+
elsif arg1.kind_of?(VariableRef)
|
147
|
+
arg1_freshness = arg1.freshness(anEnv)
|
148
|
+
if arg2.kind_of?(AtomicTerm)
|
149
|
+
if arg1_freshness.degree == :fresh
|
150
|
+
result = Outcome.new(:"#s", anEnv)
|
151
|
+
arg1.associate(arg2, result)
|
152
|
+
else
|
153
|
+
result = Outcome.new(:"#s", anEnv) if arg1.value(anEnv).eql?(arg2)
|
154
|
+
end
|
155
|
+
elsif arg2.kind_of?(CompositeTerm)
|
156
|
+
if arg1_freshness.degree == :fresh
|
157
|
+
result = Outcome.new(:"#s", anEnv)
|
158
|
+
arg1.associate(arg2, result)
|
159
|
+
else
|
160
|
+
# Ground case...
|
161
|
+
result = unify_composite_terms(arg1_freshness.associated, arg2, anEnv)
|
162
|
+
end
|
163
|
+
elsif arg2.kind_of?(VariableRef)
|
164
|
+
freshness = [arg1.fresh?(anEnv), arg2.fresh?(anEnv)]
|
165
|
+
case freshness
|
166
|
+
when [false, false] # TODO: confirm this...
|
167
|
+
result = unification(arg1.value(anEnv), arg2.value(anEnv), anEnv)
|
168
|
+
when [true, true]
|
169
|
+
result = Outcome.new(:"#s", anEnv)
|
170
|
+
if arg1.var_name != arg2.var_name
|
171
|
+
arg1.associate(arg2, result)
|
172
|
+
arg2.associate(arg1, result)
|
173
|
+
end
|
174
|
+
else
|
175
|
+
raise StandardError, "Unsupported freshness combination #{freshness}"
|
176
|
+
end
|
177
|
+
else
|
178
|
+
arg_kinds = [arg1.class, arg2.class]
|
179
|
+
raise StandardError, "Unsupported combination #{arg_kinds}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
result
|
184
|
+
end
|
185
|
+
|
186
|
+
# @return [Freshness]
|
187
|
+
def unify_composite_terms(arg1, arg2, anEnv)
|
188
|
+
# require 'debug'
|
189
|
+
result = Outcome.new(:"#u", anEnv)
|
190
|
+
children1 = arg1.children
|
191
|
+
children2 = arg2.children
|
192
|
+
|
193
|
+
if children1.size == children2.size
|
194
|
+
i = 0
|
195
|
+
subresults = children1.map do |child1|
|
196
|
+
child2 = children2[i]
|
197
|
+
i += 1
|
198
|
+
unification(child1, child2, anEnv)
|
199
|
+
end
|
200
|
+
total_success = subresults.all?(&:successful?)
|
201
|
+
if total_success
|
202
|
+
memo = Outcome.new(:"#s", anEnv)
|
203
|
+
associations = subresults.reduce(memo) do |sub_total, outcome|
|
204
|
+
sub_total.merge(outcome)
|
205
|
+
sub_total
|
206
|
+
end
|
207
|
+
result = memo
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
result
|
212
|
+
end
|
213
|
+
|
214
|
+
end # class
|
215
|
+
end # module
|
216
|
+
end # module
|
@@ -1,17 +1,20 @@
|
|
1
1
|
require 'singleton'
|
2
|
+
require_relative 'duck_fiber'
|
2
3
|
require_relative 'nullary_relation'
|
3
4
|
|
4
5
|
module MiniKraken
|
5
6
|
module Core
|
7
|
+
# A nullary relation that unconditionally always fails.
|
6
8
|
class Fail < NullaryRelation
|
7
9
|
include Singleton
|
8
|
-
|
10
|
+
|
9
11
|
def initialize
|
10
|
-
super('fail')
|
12
|
+
super('fail', '#u')
|
11
13
|
end
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
|
15
|
+
# @return [DuckFiber]
|
16
|
+
def solver_for(_actuals, _env)
|
17
|
+
DuckFiber.new(:failure)
|
15
18
|
end
|
16
19
|
end # class
|
17
20
|
end # module
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module MiniKraken
|
2
|
+
module Core
|
3
|
+
# Freshness: fresh, bound, ground
|
4
|
+
# fresh: no association at all
|
5
|
+
# bound: associated to something that is itself not ground.
|
6
|
+
# ground: associated to something that is either an atomic, a composite with ground members,
|
7
|
+
# a variable reference to something that is itself ground.
|
8
|
+
# RS fresh == fresh or bound
|
9
|
+
# RS not fresh == ground
|
10
|
+
# RS result == fresh => any or bound => expr(any)
|
11
|
+
Freshness = Struct.new(:degree, :associated) do
|
12
|
+
def initialize(aDegree, anAssociated)
|
13
|
+
super(aDegree, valid_associated(anAssociated))
|
14
|
+
end
|
15
|
+
|
16
|
+
def fresh?
|
17
|
+
self.degree == :fresh
|
18
|
+
end
|
19
|
+
|
20
|
+
def bound?
|
21
|
+
self.degree == :bound
|
22
|
+
end
|
23
|
+
|
24
|
+
def ground?
|
25
|
+
self.degree == :ground
|
26
|
+
end
|
27
|
+
|
28
|
+
# Does this instance represent something fresh according to
|
29
|
+
# "Reasoned Schemer" book ?
|
30
|
+
def rs_fresh?
|
31
|
+
self.degree != ground
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def valid_associated(anAssociated)
|
37
|
+
raise StandardError, 'Wrong argument' if anAssociated.kind_of?(self.class)
|
38
|
+
anAssociated
|
39
|
+
end
|
40
|
+
end # struct
|
41
|
+
end # module
|
42
|
+
end # module
|
@@ -1,17 +1,42 @@
|
|
1
|
+
require_relative 'environment'
|
2
|
+
|
1
3
|
module MiniKraken
|
2
4
|
module Core
|
3
5
|
class Goal
|
6
|
+
# @return [Relation] The relation corresponding to this goal
|
4
7
|
attr_reader :relation
|
5
8
|
|
9
|
+
# @return [Array<Term>] The actual aguments of the goal
|
10
|
+
attr_reader :actuals
|
11
|
+
|
12
|
+
# @param aRelation [Relation] The relation corresponding to this goal
|
13
|
+
# @param args [Array<Term>] The actual aguments of the goal
|
6
14
|
def initialize(aRelation, args)
|
7
15
|
@relation = aRelation
|
16
|
+
@actuals = validated_actuals(args)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Attempt to achieve the goal for a given context (environment)
|
20
|
+
# @param anEnv [Environment] The context in which the goal take place.
|
21
|
+
# @return [Fiber<Outcome>] A Fiber object that will generate the results.
|
22
|
+
def attain(anEnv)
|
23
|
+
relation.solver_for(actuals, anEnv)
|
8
24
|
end
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def validated_actuals(args)
|
29
|
+
if args.size != relation.arity
|
30
|
+
err_msg = "Goal has #{args.size} arguments, expected #{relation.arity}"
|
31
|
+
raise StandardError, err_msg
|
32
|
+
end
|
33
|
+
|
34
|
+
prefix = "Invalid goal argument "
|
35
|
+
args.each do |actl|
|
36
|
+
raise StandardError, prefix + actl.to_s unless actl.kind_of?(Term)
|
37
|
+
end
|
38
|
+
|
39
|
+
args.dup
|
15
40
|
end
|
16
41
|
end # class
|
17
42
|
end # module
|