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