porolog 0.0.4 → 1.0.0
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/README.md +30 -5
- data/Rakefile +7 -2
- data/bin/porolog +58 -1
- data/coverage/badge.svg +1 -1
- data/coverage/index.html +76733 -2638
- data/doc/Array.html +1066 -0
- data/doc/Object.html +674 -0
- data/doc/Porolog.html +4153 -74
- data/doc/Symbol.html +501 -0
- data/doc/_index.html +280 -6
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +34 -39
- data/doc/index.html +34 -39
- data/doc/method_list.html +1337 -57
- data/doc/top-level-namespace.html +4 -2
- data/lib/porolog.rb +1144 -4
- data/lib/porolog/arguments.rb +28 -24
- data/lib/porolog/core_ext.rb +188 -0
- data/lib/porolog/error.rb +9 -0
- data/lib/porolog/goal.rb +357 -0
- data/lib/porolog/instantiation.rb +346 -0
- data/lib/porolog/predicate.rb +74 -31
- data/lib/porolog/predicate/builtin.rb +825 -0
- data/lib/porolog/rule.rb +162 -0
- data/lib/porolog/scope.rb +4 -4
- data/lib/porolog/tail.rb +57 -0
- data/lib/porolog/value.rb +105 -0
- data/lib/porolog/variable.rb +325 -0
- data/test/porolog/arguments_test.rb +244 -195
- data/test/porolog/core_ext_test.rb +290 -0
- data/test/porolog/goal_test.rb +891 -0
- data/test/porolog/instantiation_test.rb +910 -0
- data/test/porolog/porolog_test.rb +2376 -13
- data/test/porolog/predicate/builtin_test.rb +1340 -0
- data/test/porolog/predicate_test.rb +84 -30
- data/test/porolog/rule_test.rb +527 -0
- data/test/porolog/scope_test.rb +0 -2
- data/test/porolog/tail_test.rb +127 -0
- data/test/porolog/value_test.rb +315 -0
- data/test/porolog/variable_test.rb +1614 -0
- data/test/samples_test.rb +277 -0
- data/test/test_helper.rb +115 -0
- metadata +34 -7
@@ -0,0 +1,346 @@
|
|
1
|
+
#
|
2
|
+
# lib/porolog/instantiation.rb - Plain Old Ruby Objects Prolog Engine -- Instantiation
|
3
|
+
#
|
4
|
+
# Luis Esteban 2 May 2018
|
5
|
+
# created
|
6
|
+
#
|
7
|
+
|
8
|
+
module Porolog
|
9
|
+
|
10
|
+
# A Porolog::Instantiation implements an instantiation of a Variable of a Goal.
|
11
|
+
# An Instantiation joins two expressions. At least one expression must have a variable.
|
12
|
+
# An index may be provided to index into the value of either expression, or both may have an index.
|
13
|
+
# Thus, an element of each list could be instantiated without reference to the other elements:
|
14
|
+
# x = [a,b,c,d,e]
|
15
|
+
# |
|
16
|
+
# y = [p,q,r,s]
|
17
|
+
#
|
18
|
+
# @author Luis Esteban
|
19
|
+
#
|
20
|
+
# @!attribute variable1
|
21
|
+
# @return [Variable,Object] one end of the instantiation.
|
22
|
+
# @!attribute variable2
|
23
|
+
# @return [Variable,Object] the other end of the instantiation.
|
24
|
+
# @!attribute index1
|
25
|
+
# @return [Object,nil] the index into the value of variable1.
|
26
|
+
# @!attribute index2
|
27
|
+
# @return [Object,nil] the index into the value of variable2.
|
28
|
+
#
|
29
|
+
class Instantiation
|
30
|
+
|
31
|
+
# Error class for rescuing or detecting any Instantiation error.
|
32
|
+
class Error < PorologError ; end
|
33
|
+
# Error class indicating that an instantiation was created without any Variables.
|
34
|
+
class NoVariableError < Error ; end
|
35
|
+
# Error class indicating that an index could not be used although requested.
|
36
|
+
class UnhandledIndexError < Error ; end
|
37
|
+
|
38
|
+
attr_accessor :variable1, :variable2, :index1, :index2
|
39
|
+
|
40
|
+
# Clears all instantiations.
|
41
|
+
# @return [Boolean] success
|
42
|
+
def self.reset
|
43
|
+
@@instantiations = {}
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
reset
|
48
|
+
|
49
|
+
# @return [Hash{Array => Porolog::Instantiation}] all Instantiations
|
50
|
+
def self.instantiations
|
51
|
+
@@instantiations
|
52
|
+
end
|
53
|
+
|
54
|
+
# Finds or creates a new instantiation. At least one of the variables must be a Variable.
|
55
|
+
# @param variable1 [Porolog::Variable,Porolog::Value,Object] one end of the instantiation to be made.
|
56
|
+
# @param variable2 [Porolog::Variable,Porolog::Value,Object] the other end of the instantiation to be made.
|
57
|
+
# @param index1 [Integer,Symbol,nil] index into the value of variable1.
|
58
|
+
# @param index2 [Integer,Symbol,nil] index into the value of variable2.
|
59
|
+
# @return [Porolog::Instantiation] the found or created Instantiation.
|
60
|
+
def self.new(variable1, index1, variable2, index2)
|
61
|
+
raise NoVariableError, "Cannot instantiate non-variables: #{variable1.inspect} and #{variable2.inspect}" unless variable1.is_a?(Variable) || variable2.is_a?(Variable)
|
62
|
+
|
63
|
+
variable1 = Value.new variable1, variable2.goal unless variable1.is_a?(Variable) || variable1.is_a?(Value)
|
64
|
+
variable2 = Value.new variable2, variable1.goal unless variable2.is_a?(Variable) || variable2.is_a?(Value)
|
65
|
+
|
66
|
+
instantiation = \
|
67
|
+
@@instantiations[[variable1, index1, variable2, index2]] ||
|
68
|
+
@@instantiations[[variable2, index2, variable1, index1]] ||
|
69
|
+
super
|
70
|
+
|
71
|
+
instantiation
|
72
|
+
end
|
73
|
+
|
74
|
+
# Initializes and registers a new Instantiation. At least one of the variables must be a Variable.
|
75
|
+
# @param variable1 [Porolog::Variable,Porolog::Value,Object] one end of the instantiation to be made.
|
76
|
+
# @param variable2 [Porolog::Variable,Porolog::Value,Object] the other end of the instantiation to be made.
|
77
|
+
# @param index1 [Integer,Symbol,nil] index into the value of variable1.
|
78
|
+
# @param index2 [Integer,Symbol,nil] index into the value of variable2.
|
79
|
+
# @return [Porolog::Instantiation] the found or created Instantiation.
|
80
|
+
def initialize(variable1, index1, variable2, index2)
|
81
|
+
@variable1 = variable1
|
82
|
+
@variable2 = variable2
|
83
|
+
@index1 = index1
|
84
|
+
@index2 = index2
|
85
|
+
|
86
|
+
@variable1.instantiations << self
|
87
|
+
@variable2.instantiations << self
|
88
|
+
|
89
|
+
@@instantiations[signature] = self
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [Array] the signature of the Instantiation, which aids in avoiding duplication instantiations.
|
93
|
+
def signature
|
94
|
+
@signature ||= [@variable1, @index1, @variable2, @index2].freeze
|
95
|
+
@signature
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [String] pretty representation.
|
99
|
+
def inspect(indent = 0)
|
100
|
+
index1 = @index1 ? "[#{@index1.inspect}]" : ''
|
101
|
+
index2 = @index2 ? "[#{@index2.inspect}]" : ''
|
102
|
+
|
103
|
+
"#{' ' * indent}#{@variable1.inspect}#{index1} = #{@variable2.inspect}#{index2}"
|
104
|
+
end
|
105
|
+
|
106
|
+
# Removes itself from its variables' Instantiations and unregisters itself.
|
107
|
+
# @return [Boolean] success
|
108
|
+
def remove
|
109
|
+
@variable1.instantiations.delete(self) if @variable1.is_a?(Variable) || @variable1.is_a?(Value)
|
110
|
+
@variable2.instantiations.delete(self) if @variable2.is_a?(Variable) || @variable2.is_a?(Value)
|
111
|
+
|
112
|
+
@deleted = true
|
113
|
+
@@instantiations.delete(signature)
|
114
|
+
@deleted
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns the Goal of the other Variable to the one provided.
|
118
|
+
# @param variable [Porolog::Variable] the provided Variable.
|
119
|
+
# @return [Porolog::Goal,nil] the Goal of the other Variable.
|
120
|
+
def other_goal_to(variable)
|
121
|
+
return nil unless variables.include?(variable)
|
122
|
+
|
123
|
+
other_variable = (variables - [variable]).first
|
124
|
+
other_variable&.goal
|
125
|
+
end
|
126
|
+
|
127
|
+
# @return [Array<Porolog::Goal>] the Goals of the Variables of the Instantiation.
|
128
|
+
def goals
|
129
|
+
[@variable1.goal,@variable2.goal]
|
130
|
+
end
|
131
|
+
|
132
|
+
# @return [Array<Porolog::Variable,Object>] the Variables of the Instantiation.
|
133
|
+
def variables
|
134
|
+
[@variable1,@variable2]
|
135
|
+
end
|
136
|
+
|
137
|
+
# @param visited [Array] prevents infinite recursion.
|
138
|
+
# @return [Array] the values of the Variables of the Instantiation.
|
139
|
+
def values(visited = [])
|
140
|
+
return [] if visited.include?(self)
|
141
|
+
|
142
|
+
values_for_variable1 = values_for(@variable1, visited)
|
143
|
+
values_for_variable2 = values_for(@variable2, visited)
|
144
|
+
|
145
|
+
(values_for_variable1 + values_for_variable2).uniq
|
146
|
+
end
|
147
|
+
|
148
|
+
# @param value [Object] the provided value.
|
149
|
+
# @param index [Integer,Symbol,Array<Integer>] the index.
|
150
|
+
# @param visited [Array] prevents infinite recursion.
|
151
|
+
# @return [Object] the indexed value of the provided value.
|
152
|
+
def value_indexed(value, index, visited = [])
|
153
|
+
return nil unless value
|
154
|
+
return nil if value.type == :variable
|
155
|
+
return nil if value == [] && index == :tail
|
156
|
+
|
157
|
+
visit = [value, index]
|
158
|
+
return nil if visited.include?(visit)
|
159
|
+
visited = visited + [visit]
|
160
|
+
|
161
|
+
case index
|
162
|
+
when Integer
|
163
|
+
if value.respond_to?(:last) && value.last == UNKNOWN_TAIL
|
164
|
+
if index >= value.length - 1
|
165
|
+
UNKNOWN_TAIL
|
166
|
+
else
|
167
|
+
value[index]
|
168
|
+
end
|
169
|
+
elsif value.is_a?(Numeric)
|
170
|
+
value
|
171
|
+
else
|
172
|
+
value[index]
|
173
|
+
end
|
174
|
+
|
175
|
+
when Symbol
|
176
|
+
value_value = value.value(visited)
|
177
|
+
if index == :flathead
|
178
|
+
# value_value = [..., 4, 5, 6]
|
179
|
+
if value_value.first == UNKNOWN_TAIL
|
180
|
+
UNKNOWN_ARRAY
|
181
|
+
else
|
182
|
+
nil
|
183
|
+
end
|
184
|
+
elsif index == :flattail
|
185
|
+
# value_value = [1, 2, 3, ...]
|
186
|
+
if value_value.first == UNKNOWN_TAIL
|
187
|
+
nil
|
188
|
+
elsif value_value.last == UNKNOWN_TAIL
|
189
|
+
UNKNOWN_ARRAY
|
190
|
+
end
|
191
|
+
elsif value_value.respond_to?(index)
|
192
|
+
value_value.send(index)
|
193
|
+
else
|
194
|
+
value
|
195
|
+
end
|
196
|
+
|
197
|
+
when Array
|
198
|
+
if index.empty?
|
199
|
+
value[1..-1]
|
200
|
+
else
|
201
|
+
value[0...index.first]
|
202
|
+
end
|
203
|
+
|
204
|
+
else
|
205
|
+
if index
|
206
|
+
raise UnhandledIndexError, "Unhandled index: #{index.inspect} of #{value.inspect}"
|
207
|
+
else
|
208
|
+
value
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# @param variable [Porolog::Variable,Porolog::Value] the specified variable (or end of the Instantiation).
|
214
|
+
# @param visited [Array] prevents infinite recursion.
|
215
|
+
# @return [Array<Object>] the values for the specified variable by finding the values of the other variable (i.e. the other end) and applying any indexes.
|
216
|
+
def values_for(variable, visited = [])
|
217
|
+
return [] if visited.include?(self)
|
218
|
+
visited = visited + [self]
|
219
|
+
|
220
|
+
if variable == @variable1
|
221
|
+
if @index1 == :flathead
|
222
|
+
flathead = value_at_index(value_indexed(@variable2.value(visited), @index2, visited), @index1).value.value
|
223
|
+
if flathead
|
224
|
+
return [[*flathead]]
|
225
|
+
else
|
226
|
+
return []
|
227
|
+
end
|
228
|
+
end
|
229
|
+
if @index1 == :flattail
|
230
|
+
flattail = value_at_index(value_indexed(@variable2.value(visited), @index2, visited), @index1).value.value
|
231
|
+
if flattail
|
232
|
+
return [[UNKNOWN_TAIL, *flattail]]
|
233
|
+
else
|
234
|
+
return []
|
235
|
+
end
|
236
|
+
end
|
237
|
+
if @index1
|
238
|
+
[value_at_index(value_indexed(@variable2.value(visited), @index2, visited), @index1)]
|
239
|
+
else
|
240
|
+
if @variable2.is_a?(Variable)
|
241
|
+
[value_indexed(@variable2.value(visited), @index2, visited)]
|
242
|
+
else
|
243
|
+
[value_indexed(@variable2, @index2, visited)]
|
244
|
+
end
|
245
|
+
end
|
246
|
+
elsif variable == @variable2
|
247
|
+
if @index2 == :flathead
|
248
|
+
flathead = value_at_index(value_indexed(@variable1.value(visited), @index1, visited), @index2).value.value
|
249
|
+
if flathead
|
250
|
+
return [[*flathead]]
|
251
|
+
else
|
252
|
+
return []
|
253
|
+
end
|
254
|
+
end
|
255
|
+
if @index2 == :flattail
|
256
|
+
flattail = value_at_index(value_indexed(@variable1.value(visited), @index1, visited), @index2).value.value
|
257
|
+
if flattail
|
258
|
+
return [[UNKNOWN_TAIL, *flattail]]
|
259
|
+
else
|
260
|
+
return []
|
261
|
+
end
|
262
|
+
end
|
263
|
+
if @index2
|
264
|
+
[value_at_index(value_indexed(@variable1.value(visited), @index1, visited), @index2)]
|
265
|
+
else
|
266
|
+
if @variable1.is_a?(Variable)
|
267
|
+
[value_indexed(@variable1.value(visited), @index1, visited)]
|
268
|
+
else
|
269
|
+
[value_indexed(@variable1, @index1, visited)]
|
270
|
+
end
|
271
|
+
end
|
272
|
+
else
|
273
|
+
[]
|
274
|
+
end.compact
|
275
|
+
end
|
276
|
+
|
277
|
+
# @param value [Object] the known value.
|
278
|
+
# @param index [Integer,Symbol,Array] the known index.
|
279
|
+
# @return [Array] an Array where the known value is at the known index.
|
280
|
+
def value_at_index(value, index)
|
281
|
+
value && case index
|
282
|
+
when Integer
|
283
|
+
result = []
|
284
|
+
result[index] = value
|
285
|
+
result << UNKNOWN_TAIL
|
286
|
+
result
|
287
|
+
|
288
|
+
when Symbol
|
289
|
+
case index
|
290
|
+
when :flathead
|
291
|
+
[*value, UNKNOWN_TAIL]
|
292
|
+
when :head
|
293
|
+
[value, UNKNOWN_TAIL]
|
294
|
+
when :tail
|
295
|
+
[nil, *value]
|
296
|
+
when :flattail
|
297
|
+
value
|
298
|
+
else
|
299
|
+
raise UnhandledIndexError, "Unhandled index: #{index.inspect} for #{value.inspect}"
|
300
|
+
end
|
301
|
+
|
302
|
+
when Array
|
303
|
+
if index.empty?
|
304
|
+
[nil, *value]
|
305
|
+
else
|
306
|
+
[value, UNKNOWN_TAIL]
|
307
|
+
end
|
308
|
+
|
309
|
+
else
|
310
|
+
if index
|
311
|
+
raise UnhandledIndexError, "Unhandled index: #{index.inspect} for #{value.inspect}"
|
312
|
+
else
|
313
|
+
value
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
# @param variable [Porolog::Variable,Porolog::Value] the specified variable.
|
319
|
+
# @return [Boolean] whether the specified variable is not indexed.
|
320
|
+
def without_index_on?(variable)
|
321
|
+
[
|
322
|
+
[@variable1,@index1],
|
323
|
+
[@variable2,@index2],
|
324
|
+
].any?{|pair|
|
325
|
+
pair.first == variable && pair.last.nil?
|
326
|
+
}
|
327
|
+
end
|
328
|
+
|
329
|
+
# @return [Boolean] whether the Instantiation has been deleted (memoized).
|
330
|
+
def deleted?
|
331
|
+
@deleted ||= @variable1.goal.deleted? || @variable2.goal.deleted?
|
332
|
+
@deleted
|
333
|
+
end
|
334
|
+
|
335
|
+
# @param goal [Porolog::Goal] the provided Goal.
|
336
|
+
# @return [Boolean] whether the Instantiation attaches to a variable in the provided Goal.
|
337
|
+
def belongs_to?(goal)
|
338
|
+
[
|
339
|
+
@variable1.goal,
|
340
|
+
@variable2.goal,
|
341
|
+
].include?(goal)
|
342
|
+
end
|
343
|
+
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|
data/lib/porolog/predicate.rb
CHANGED
@@ -10,20 +10,28 @@ module Porolog
|
|
10
10
|
# A Porolog::Predicate corresponds to a Prolog predicate.
|
11
11
|
# It contains rules (Porolog::Rule) and belongs to a Porolog::Scope.
|
12
12
|
# When provided with arguments, it produces a Porolog::Arguments.
|
13
|
+
#
|
13
14
|
# @author Luis Esteban
|
15
|
+
#
|
14
16
|
# @!attribute [r] name
|
15
17
|
# Name of the Predicate.
|
18
|
+
#
|
16
19
|
# @!attribute [r] rules
|
17
20
|
# Rules of the Predicate.
|
21
|
+
#
|
18
22
|
class Predicate
|
19
23
|
|
20
24
|
# Error class for rescuing or detecting any Predicate error.
|
21
|
-
class
|
25
|
+
class Error < PorologError ; end
|
22
26
|
# Error class indicating an error with the name of a Predicate.
|
23
|
-
class NameError <
|
27
|
+
class NameError < Error ; end
|
24
28
|
|
25
29
|
attr_reader :name, :rules
|
26
30
|
|
31
|
+
# A unique value used to verify instantiations.
|
32
|
+
UNIQUE_VALUE = Object.new.freeze
|
33
|
+
private_constant :UNIQUE_VALUE
|
34
|
+
|
27
35
|
# Returns the current scope, or sets the current scope if a paramter is provided
|
28
36
|
# (creating the new scope if necessary).
|
29
37
|
# @param scope_name [Object] the name (or otherwise object) used to register a scope.
|
@@ -40,9 +48,7 @@ module Porolog
|
|
40
48
|
# @param scope_name [Object] the name (or otherwise object) used to register a scope.
|
41
49
|
# @return [Porolog::Scope] the current Scope.
|
42
50
|
def self.scope=(scope_name)
|
43
|
-
if scope_name
|
44
|
-
@@current_scope = Scope[scope_name] || Scope.new(scope_name)
|
45
|
-
end
|
51
|
+
@@current_scope = Scope[scope_name] || Scope.new(scope_name) if scope_name
|
46
52
|
@@current_scope
|
47
53
|
end
|
48
54
|
|
@@ -65,11 +71,12 @@ module Porolog
|
|
65
71
|
# Initializes a Porolog::Predicate and registers it by its name.
|
66
72
|
# @param name [#to_sym] the input object to read from
|
67
73
|
# @param scope_name the name of the scope in which to register the Predicate; if omitted, defaults to the name of the current scope
|
68
|
-
def initialize(name, scope_name = Predicate.scope.name)
|
69
|
-
@name
|
70
|
-
@rules
|
74
|
+
def initialize(name, scope_name = Predicate.scope.name, builtin: false)
|
75
|
+
@name = name.to_sym
|
76
|
+
@rules = []
|
77
|
+
@builtin = builtin
|
71
78
|
|
72
|
-
raise NameError
|
79
|
+
raise NameError, "Cannot name a predicate 'predicate'" if @name == :predicate
|
73
80
|
|
74
81
|
scope = Scope[scope_name] || Scope.new(scope_name)
|
75
82
|
scope[@name] = self
|
@@ -78,21 +85,23 @@ module Porolog
|
|
78
85
|
# Creates a new Predicate, or returns an existing Predicate if one already exists with the given name in the current scope.
|
79
86
|
# @return [Porolog::Predicate] a new or existing Predicate.
|
80
87
|
def self.new(*args)
|
81
|
-
|
82
|
-
scope[name.to_sym] || super
|
88
|
+
scope[args.first.to_sym] || super
|
83
89
|
end
|
84
90
|
|
85
91
|
# Create Arguments for the Predicate.
|
86
|
-
# Provides the syntax
|
87
|
-
#
|
92
|
+
# Provides the syntax option:
|
93
|
+
# p.(x,y,z)
|
94
|
+
# for
|
95
|
+
# p.arguments(x,y,z)
|
88
96
|
# @return [Porolog::Arguments] Arguments of the Predicate with the given arguments.
|
89
|
-
def call(*args)
|
90
|
-
Arguments.new(self,args)
|
97
|
+
def call(*args, &block)
|
98
|
+
Arguments.new(self, args, &block)
|
91
99
|
end
|
92
100
|
|
93
101
|
# Create Arguments for the Predicate.
|
94
|
-
|
95
|
-
|
102
|
+
# @return [Porolog::Arguments] Arguments of the Predicate with the given arguments.
|
103
|
+
def arguments(*args, &block)
|
104
|
+
Arguments.new(self, args, &block)
|
96
105
|
end
|
97
106
|
|
98
107
|
# Add a Rule to the Predicate.
|
@@ -106,28 +115,62 @@ module Porolog
|
|
106
115
|
# A pretty print String of the Predicate.
|
107
116
|
# @return [String] the Predicate as a String.
|
108
117
|
def inspect
|
109
|
-
lines = []
|
110
|
-
|
111
118
|
if @rules.size == 1
|
112
|
-
|
119
|
+
"#{@name}:-#{@rules.first.inspect}"
|
113
120
|
else
|
114
|
-
|
115
|
-
@rules.each do |rule|
|
121
|
+
@rules.each_with_object(["#{@name}:-"]) do |rule, lines|
|
116
122
|
lines << rule.inspect
|
123
|
+
end.join("\n")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Satisfy the Predicate within the supplied Goal.
|
128
|
+
# Satisfy of each rule of the Predicate is called with the Goal and success block.
|
129
|
+
# @param goal [Porolog::Goal] the Goal within which to satisfy the Predicate.
|
130
|
+
# @param block [Proc] the block to be called each time a Rule of the Predicate is satisfied.
|
131
|
+
# @return [Boolean] whether any Rule was satisfied.
|
132
|
+
def satisfy(goal, &block)
|
133
|
+
if builtin?
|
134
|
+
satisfy_builtin(goal, &block)
|
135
|
+
else
|
136
|
+
satisfied = false
|
137
|
+
@rules.each do |rule|
|
138
|
+
rule.satisfy(goal) do |subgoal|
|
139
|
+
satisfied = true
|
140
|
+
block.call(subgoal)
|
141
|
+
end
|
142
|
+
break if goal.terminated?
|
117
143
|
end
|
144
|
+
satisfied
|
118
145
|
end
|
119
|
-
|
120
|
-
lines.join("\n")
|
121
146
|
end
|
122
147
|
|
123
|
-
#
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
148
|
+
# @return [Boolean] whether the Predicate is a builtin predicate.
|
149
|
+
def builtin?
|
150
|
+
@builtin
|
151
|
+
end
|
152
|
+
|
153
|
+
# Satisfy the builtin Predicate within the supplied Goal.
|
154
|
+
# Call the builtin Predicate method with the Goal and success block.
|
155
|
+
# The arguments and block are extracted from the Goal's Arguments.
|
156
|
+
# @param goal [Porolog::Goal] the Goal within which to satisfy the Predicate.
|
157
|
+
# @param block [Proc] the block to be called each time a Rule of the Predicate is satisfied.
|
158
|
+
# @return [Boolean] whether any Rule was satisfied.
|
159
|
+
def satisfy_builtin(goal, &block)
|
160
|
+
predicate = goal.arguments.predicate.name
|
161
|
+
arguments = goal.arguments.arguments
|
162
|
+
arg_block = goal.arguments.block
|
129
163
|
|
130
|
-
|
164
|
+
Predicate.call_builtin(predicate, goal, block, *arguments, &arg_block)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Call a builtin predicate
|
168
|
+
# @param predicate [Symbol] the name of the predicate to call.
|
169
|
+
# @param args [Array<Object>] arguments for the predicate call.
|
170
|
+
# @param arg_block [Proc] the block provided in the Arguments of the Predicate.
|
171
|
+
# @return [Boolean] whether the predicate was satisfied.
|
172
|
+
def self.call_builtin(predicate, *args, &arg_block)
|
173
|
+
Porolog::Predicate::Builtin.instance_method(predicate).bind(self).call(*args, &arg_block)
|
131
174
|
end
|
132
175
|
|
133
176
|
end
|