rubylog 0.0.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/README.hu.rb +58 -0
- data/README.rdoc +248 -89
- data/Rakefile +6 -1
- data/VERSION +1 -1
- data/bin/rubylog +18 -0
- data/examples/dcg.rb +35 -0
- data/examples/dcg2.rb +42 -0
- data/examples/enumerators.rb +30 -0
- data/examples/factorial.rb +9 -8
- data/examples/hanoi.rb +24 -0
- data/examples/hello.rb +11 -5
- data/examples/parsing.rb +27 -0
- data/examples/primitives.rb +24 -0
- data/examples/theory.rb +22 -10
- data/lib/rubylog/builtins/default.rb +10 -0
- data/lib/rubylog/builtins/file_system.rb +15 -0
- data/lib/rubylog/builtins/logic.rb +109 -0
- data/lib/rubylog/builtins/reflection.rb +94 -0
- data/lib/rubylog/builtins/term.rb +47 -0
- data/lib/rubylog/dsl/array_splat.rb +25 -0
- data/lib/rubylog/dsl/primitives.rb +17 -0
- data/lib/rubylog/dsl/thats.rb +22 -0
- data/lib/rubylog/dsl/variables.rb +30 -0
- data/lib/rubylog/dsl.rb +35 -17
- data/lib/rubylog/errors.rb +19 -1
- data/lib/rubylog/interfaces/assertable.rb +16 -0
- data/lib/rubylog/interfaces/callable.rb +18 -0
- data/lib/rubylog/interfaces/composite_term.rb +47 -0
- data/lib/rubylog/interfaces/predicate.rb +8 -0
- data/lib/rubylog/interfaces/procedure.rb +60 -0
- data/lib/rubylog/interfaces/term.rb +41 -0
- data/lib/rubylog/mixins/array.rb +118 -0
- data/lib/{class.rb → rubylog/mixins/class.rb} +2 -2
- data/lib/rubylog/mixins/hash.rb +8 -0
- data/lib/rubylog/mixins/kernel.rb +5 -0
- data/lib/rubylog/mixins/method.rb +3 -0
- data/lib/rubylog/mixins/object.rb +8 -0
- data/lib/rubylog/mixins/proc.rb +37 -0
- data/lib/rubylog/mixins/string.rb +104 -0
- data/lib/rubylog/mixins/symbol.rb +44 -0
- data/lib/rubylog/simple_procedure.rb +8 -0
- data/lib/rubylog/{clause.rb → structure.rb} +32 -31
- data/lib/rubylog/theory.rb +368 -79
- data/lib/rubylog/variable.rb +102 -23
- data/lib/rubylog.rb +33 -25
- data/logic/builtins/file_system_logic.rb +23 -0
- data/logic/builtins/reflection_logic.rb +40 -0
- data/logic/dereference_logic.rb +23 -0
- data/logic/directory_structure_logic.rb +19 -0
- data/logic/dsl_logic.rb +29 -0
- data/logic/errors_logic.rb +9 -0
- data/logic/guard_logic.rb +115 -0
- data/logic/list_logic.rb +55 -0
- data/logic/map_logic.rb +15 -0
- data/logic/multitheory.rb +23 -0
- data/logic/recursion_logic.rb +12 -0
- data/logic/string_logic.rb +41 -0
- data/logic/thats_logic.rb +51 -0
- data/logic/variable_logic.rb +24 -0
- data/rubylog.gemspec +85 -46
- data/spec/bartak_guide_spec.rb +57 -62
- data/spec/builtins/all_spec.rb +99 -0
- data/spec/builtins/and_spec.rb +22 -0
- data/spec/builtins/array_spec.rb +16 -0
- data/spec/builtins/branch_or_spec.rb +27 -0
- data/spec/builtins/cut_spec.rb +44 -0
- data/spec/builtins/fail_spec.rb +5 -0
- data/spec/builtins/false_spec.rb +5 -0
- data/spec/builtins/in_spec.rb +38 -0
- data/spec/builtins/is_false_spec.rb +12 -0
- data/spec/builtins/is_spec.rb +26 -0
- data/spec/builtins/matches_spec.rb +23 -0
- data/spec/builtins/or_spec.rb +22 -0
- data/spec/{rubylog/builtins → builtins}/splits_to.rb +0 -0
- data/spec/builtins/then_spec.rb +27 -0
- data/spec/builtins/true_spec.rb +5 -0
- data/spec/clause_spec.rb +82 -0
- data/spec/compilation_spec.rb +61 -0
- data/spec/custom_classes_spec.rb +43 -0
- data/spec/dereference.rb +10 -0
- data/spec/{inriasuite.rb → inriasuite_spec.rb} +2 -9
- data/spec/queries_spec.rb +150 -0
- data/spec/recursion_spec.rb +4 -4
- data/spec/ruby_code_spec.rb +52 -0
- data/spec/rules_spec.rb +97 -0
- data/spec/spec_helper.rb +6 -2
- data/spec/theory_spec.rb +28 -0
- data/spec/unification_spec.rb +84 -0
- data/spec/variable_spec.rb +26 -0
- metadata +153 -180
- data/examples/4queens.rb +0 -10
- data/examples/calculation.rb +0 -12
- data/examples/concepts.rb +0 -46
- data/examples/fp.rb +0 -56
- data/examples/historia_de_espana.rb +0 -31
- data/examples/idea.rb +0 -143
- data/examples/lists.rb +0 -5
- data/examples/mechanika.rb +0 -409
- data/examples/parse.rb +0 -15
- data/lib/array.rb +0 -24
- data/lib/method.rb +0 -4
- data/lib/object.rb +0 -5
- data/lib/proc.rb +0 -4
- data/lib/rubylog/builtins.rb +0 -193
- data/lib/rubylog/callable.rb +0 -20
- data/lib/rubylog/composite_term.rb +0 -38
- data/lib/rubylog/dsl/constants.rb +0 -15
- data/lib/rubylog/dsl/first_order_functors.rb +0 -9
- data/lib/rubylog/dsl/global_functors.rb +0 -3
- data/lib/rubylog/dsl/second_order_functors.rb +0 -8
- data/lib/rubylog/internal_helpers.rb +0 -16
- data/lib/rubylog/predicate.rb +0 -34
- data/lib/rubylog/proc_method_additions.rb +0 -69
- data/lib/rubylog/term.rb +0 -20
- data/lib/rubylog/unifiable.rb +0 -19
- data/lib/symbol.rb +0 -35
- data/script/inriasuite2spec +0 -0
- data/script/inriasuite2spec.pl +0 -22
- data/spec/rubylog/clause_spec.rb +0 -81
- data/spec/rubylog/variable_spec.rb +0 -25
- data/spec/rubylog_spec.rb +0 -914
data/lib/rubylog/theory.rb
CHANGED
@@ -1,133 +1,422 @@
|
|
1
1
|
module Rubylog
|
2
|
-
|
2
|
+
|
3
|
+
# @return [Rubylog::Theory] the current theory
|
4
|
+
def self.current_theory
|
5
|
+
Thread.current[:rubylog_current_theory]
|
6
|
+
end
|
7
|
+
|
8
|
+
# Create a new Rubylog theory or modify an existing one.
|
9
|
+
#
|
10
|
+
# You can create theories with the theory method, which is available from
|
11
|
+
# anywhere:
|
12
|
+
# theory "MyTheory" do
|
13
|
+
# # ...
|
14
|
+
# end
|
15
|
+
def self.theory full_name=nil, *args, &block
|
16
|
+
case full_name
|
17
|
+
when nil
|
18
|
+
theory = Rubylog::Theory.new
|
19
|
+
when Rubylog::Theory
|
20
|
+
theory=full_name
|
21
|
+
else
|
22
|
+
names = full_name.to_s.split("::")
|
23
|
+
parent_names, name = names[0...-1], names[-1]
|
24
|
+
parent = parent_names.inject(block.binding.eval("Module.nesting[0]") || Object) {|a,b| a.const_get b}
|
25
|
+
|
26
|
+
if not parent.const_defined?(name)
|
27
|
+
theory = Rubylog::Theory.new *args
|
28
|
+
parent.const_set name, theory
|
29
|
+
else
|
30
|
+
theory = parent.const_get name
|
31
|
+
raise TypeError, "#{name} is not a theory" unless theory.is_a? Rubylog::Theory
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
theory.amend &block
|
36
|
+
theory
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
# The Theory class represents a collection of rules.
|
42
|
+
class Rubylog::Theory
|
43
|
+
|
44
|
+
|
45
|
+
include Rubylog::DSL::Variables
|
46
|
+
|
47
|
+
# Call the given block with variables automatically resolved
|
48
|
+
def self.with_vars vars
|
49
|
+
begin
|
50
|
+
old_vars = Thread.current[:rubylog_current_variables]
|
51
|
+
Thread.current[:rubylog_current_variables] = vars
|
52
|
+
yield
|
53
|
+
ensure
|
54
|
+
Thread.current[:rubylog_current_variables] = old_vars
|
55
|
+
end
|
3
56
|
end
|
4
57
|
|
5
|
-
|
6
|
-
|
58
|
+
attr_reader :public_interface, :included_theories, :prefix_functor_modules
|
59
|
+
|
60
|
+
def initialize base=Rubylog::DefaultBuiltins, &block
|
61
|
+
clear
|
62
|
+
include base if base
|
63
|
+
|
64
|
+
if block
|
65
|
+
with_context &block
|
66
|
+
end
|
7
67
|
end
|
8
68
|
|
9
|
-
|
10
|
-
|
69
|
+
def [] indicator
|
70
|
+
@database[indicator]
|
71
|
+
end
|
72
|
+
|
73
|
+
def []= indicator, predicate
|
74
|
+
@database[indicator] = predicate
|
75
|
+
end
|
11
76
|
|
12
|
-
|
13
|
-
|
77
|
+
def keys
|
78
|
+
@database.keys
|
79
|
+
end
|
80
|
+
|
81
|
+
def each_pair
|
82
|
+
if block_given?
|
83
|
+
@database.each_pair {|*a| yield *a }
|
84
|
+
else
|
85
|
+
@database.each_pair
|
14
86
|
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Clear all data in the theory and bring it to its initial state.
|
90
|
+
def clear
|
91
|
+
@database = {}
|
92
|
+
@primitives = Rubylog::DSL::Primitives.new self
|
93
|
+
@variable_bindings = []
|
94
|
+
@public_interface = Module.new
|
95
|
+
@subjects = []
|
96
|
+
@trace = false
|
97
|
+
@implicit = false
|
98
|
+
@check_discontiguous = true
|
99
|
+
@included_theories = []
|
100
|
+
@check_number = 0
|
101
|
+
@prefix_functor_modules = []
|
102
|
+
end
|
103
|
+
|
104
|
+
def primitives
|
105
|
+
@primitives
|
106
|
+
end
|
107
|
+
private :primitives
|
108
|
+
|
109
|
+
def with_context &block
|
110
|
+
begin
|
111
|
+
# save current theory
|
112
|
+
old_theory = Thread.current[:rubylog_current_theory]
|
113
|
+
Thread.current[:rubylog_current_theory] = self
|
114
|
+
|
115
|
+
# start implicit mode
|
116
|
+
if @implicit
|
117
|
+
start_implicit
|
118
|
+
end
|
15
119
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
120
|
+
# call the block
|
121
|
+
return instance_exec &block
|
122
|
+
ensure
|
123
|
+
# restore current theory
|
124
|
+
Thread.current[:rubylog_current_theory] = old_theory
|
125
|
+
|
126
|
+
# stop implicit mode
|
127
|
+
if @implicit_started
|
128
|
+
stop_implicit
|
129
|
+
end
|
23
130
|
end
|
131
|
+
end
|
132
|
+
|
133
|
+
alias amend with_context
|
134
|
+
alias eval with_context
|
24
135
|
|
25
|
-
|
26
|
-
|
136
|
+
|
137
|
+
# directives
|
138
|
+
#
|
139
|
+
def predicate *indicators
|
140
|
+
indicators.each do |indicator|
|
141
|
+
check_indicator indicator
|
142
|
+
create_procedure indicator
|
27
143
|
end
|
144
|
+
end
|
28
145
|
|
29
|
-
|
30
|
-
|
146
|
+
def discontiguous *indicators
|
147
|
+
indicators.each do |indicator|
|
148
|
+
check_indicator indicator
|
149
|
+
create_procedure(indicator).discontiguous!
|
31
150
|
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def check_discontiguous value = true
|
154
|
+
@check_discontiguous = value
|
155
|
+
end
|
156
|
+
|
157
|
+
def check_discontiguous?
|
158
|
+
@check_discontiguous
|
159
|
+
end
|
32
160
|
|
161
|
+
def multitheory *indicators
|
162
|
+
indicators.each do |indicator|
|
163
|
+
check_indicator indicator
|
164
|
+
create_procedure(indicator).multitheory!
|
165
|
+
end
|
166
|
+
end
|
33
167
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
168
|
+
def functor *functors
|
169
|
+
functors.each do |fct|
|
170
|
+
Rubylog::DSL.add_functors_to @public_interface, fct
|
171
|
+
@subjects.each do |s|
|
172
|
+
Rubylog::DSL.add_functors_to s, fct
|
39
173
|
end
|
40
174
|
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def prefix_functor *functors
|
178
|
+
functors.each do |fct|
|
179
|
+
m = Module.new
|
180
|
+
Rubylog::DSL.add_prefix_functors_to m, fct
|
181
|
+
@prefix_functor_modules << m
|
182
|
+
extend m
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def functor_for target, *functors
|
187
|
+
functors.each do |fct|
|
188
|
+
Rubylog::DSL.add_functors_to target, fct
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def private *indicators
|
193
|
+
end
|
194
|
+
|
195
|
+
def subject *subjects
|
196
|
+
subjects.each do |s|
|
197
|
+
s.send :include, @public_interface
|
198
|
+
@subjects << s
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def include *theories
|
203
|
+
theories.each do |theory|
|
204
|
+
|
205
|
+
@included_theories << theory
|
41
206
|
|
42
|
-
|
43
|
-
|
44
|
-
|
207
|
+
# include all public_interface predicates
|
208
|
+
@public_interface.send :include, theory.public_interface
|
209
|
+
theory.each_pair do |indicator, predicate|
|
210
|
+
if keys.include? indicator and self[indicator].respond_to? :multitheory? and self[indicator].multitheory?
|
211
|
+
raise TypeError, "You can only use a procedure as a multitheory predicate (#{indicator})" unless predicate.respond_to? :each
|
212
|
+
predicate.each do |rule|
|
213
|
+
@database[indicator].assertz rule
|
214
|
+
end
|
215
|
+
else
|
216
|
+
@database[indicator] = predicate
|
217
|
+
end
|
45
218
|
end
|
219
|
+
|
220
|
+
# include prefix_functors
|
221
|
+
theory.prefix_functor_modules.each do |m|
|
222
|
+
@prefix_functor_modules << m
|
223
|
+
extend m
|
224
|
+
end
|
225
|
+
|
46
226
|
end
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
def explain c
|
231
|
+
require "rubylog/because"
|
232
|
+
include Rubylog::Because unless included_theories.include? Rubylog::Because
|
233
|
+
expl = Rubylog::Variable.new :_expl
|
234
|
+
c.because(expl).solve do
|
235
|
+
return c.because(expl.value)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def check_passed goal
|
240
|
+
print "#{n = check_number} :)\t"
|
241
|
+
end
|
47
242
|
|
48
|
-
|
49
|
-
|
243
|
+
def check_failed goal
|
244
|
+
puts "#{check_number} :/\t"
|
245
|
+
puts "Check failed: #{goal.inspect}"
|
246
|
+
puts caller[1]
|
247
|
+
#raise Rubylog::CheckFailed, goal.inspect, caller[1..-1]
|
248
|
+
end
|
50
249
|
|
250
|
+
def check_raised_exception goal, exception
|
251
|
+
puts "#{check_number} :(\t"
|
252
|
+
puts "Check raised exception: #{exception}"
|
253
|
+
puts exception.backtrace
|
254
|
+
end
|
51
255
|
|
52
|
-
|
256
|
+
# returns the line number of the most recen +check+ call
|
257
|
+
def check_number
|
258
|
+
i = caller.index{|l| l.end_with? "in `check'" }
|
259
|
+
caller[i+1] =~ /:(\d+):/
|
260
|
+
$1
|
261
|
+
end
|
262
|
+
private :check_number
|
53
263
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
264
|
+
# Tries to prove goal (or block if goal is not given). If it proves, calles
|
265
|
+
# +check_passed+. If it fails, calls +check_failed+. If it raises an exception, calls +check_raised_exception+.
|
266
|
+
def check goal=nil, &block
|
267
|
+
goal ||= block
|
268
|
+
result = nil
|
269
|
+
begin
|
270
|
+
result = true?(goal)
|
271
|
+
rescue
|
272
|
+
check_raised_exception goal, $!
|
273
|
+
else
|
274
|
+
if result
|
275
|
+
check_passed goal, &block
|
59
276
|
else
|
60
|
-
|
277
|
+
check_failed goal, &block
|
61
278
|
end
|
62
|
-
predicate << Clause.new(:-, head, body)
|
63
|
-
@last_predicate = predicate
|
64
279
|
end
|
280
|
+
result
|
281
|
+
end
|
282
|
+
|
283
|
+
# Starts (if +val+ is not given or true) or stops (if +val+ is false) implicit mode.
|
284
|
+
#
|
285
|
+
# In implicit mode you can start using infix functors without declaring them.
|
286
|
+
def implicit val=true
|
287
|
+
@implicit = val
|
288
|
+
|
289
|
+
if val
|
290
|
+
if Thread.current[:rubylog_current_theory] == self
|
291
|
+
start_implicit
|
292
|
+
end
|
293
|
+
else
|
294
|
+
if @implicit_started
|
295
|
+
stop_implicit
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def thats
|
301
|
+
Rubylog::DSL::Thats.new
|
302
|
+
end
|
303
|
+
|
304
|
+
|
305
|
+
# predicates
|
306
|
+
|
307
|
+
def assert head, body=:true
|
308
|
+
indicator = head.indicator
|
309
|
+
predicate = @database[indicator]
|
310
|
+
if predicate
|
311
|
+
check_assertable predicate, head, body
|
312
|
+
else
|
313
|
+
predicate = create_procedure indicator
|
314
|
+
end
|
315
|
+
predicate.assertz Rubylog::Structure.new(:-, head, body)
|
316
|
+
@last_predicate = predicate
|
317
|
+
end
|
65
318
|
|
66
319
|
|
67
|
-
|
320
|
+
def solve goal, &block
|
321
|
+
with_context do
|
68
322
|
goal = goal.rubylog_compile_variables
|
69
|
-
goal.prove {
|
323
|
+
goal.prove { block.call_with_rubylog_variables(goal.rubylog_variables) }
|
70
324
|
end
|
325
|
+
end
|
71
326
|
|
72
|
-
|
327
|
+
def true? goal=nil, &block
|
328
|
+
if goal.nil?
|
329
|
+
raise ArgumentError, "No goal given" if block.nil?
|
330
|
+
goal = with_context &block
|
331
|
+
end
|
332
|
+
with_context do
|
73
333
|
goal = goal.rubylog_compile_variables
|
74
334
|
goal.prove { return true }
|
75
|
-
false
|
76
335
|
end
|
336
|
+
false
|
337
|
+
end
|
77
338
|
|
78
|
-
|
79
|
-
#
|
80
|
-
#
|
81
|
-
def trace?
|
82
|
-
@trace
|
83
|
-
end
|
339
|
+
alias prove true?
|
84
340
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
341
|
+
# debugging
|
342
|
+
#
|
343
|
+
#
|
344
|
+
def trace val=true, &block
|
345
|
+
@trace=block || val
|
346
|
+
@trace_levels = 0
|
347
|
+
end
|
89
348
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
349
|
+
def print_trace level, *args
|
350
|
+
return unless @trace
|
351
|
+
if @trace.respond_to? :call
|
352
|
+
@trace.call @trace_levels, *args if not args.empty?
|
353
|
+
else
|
354
|
+
puts " "*@trace_levels + args.map{|a|a.rubylog_deep_dereference.to_s}.join(" ") if not args.empty?
|
94
355
|
end
|
356
|
+
@trace_levels += level
|
357
|
+
end
|
358
|
+
|
359
|
+
protected
|
95
360
|
|
96
|
-
protected
|
97
361
|
|
362
|
+
def check_assertable predicate, head, body
|
363
|
+
raise Rubylog::NonAssertableError, head.indicator.inspect, caller[2..-1] unless predicate.respond_to? :assertz
|
364
|
+
raise Rubylog::DiscontiguousPredicateError, head.indicator.inspect, caller[2..-1] if check_discontiguous? and not predicate.empty? and predicate != @last_predicate and not predicate.discontiguous?
|
365
|
+
end
|
366
|
+
|
367
|
+
def create_procedure indicator
|
368
|
+
functor indicator[0]
|
369
|
+
@database[indicator] = Rubylog::SimpleProcedure.new
|
370
|
+
end
|
98
371
|
|
99
|
-
|
100
|
-
|
101
|
-
|
372
|
+
def start_implicit
|
373
|
+
[@public_interface, Rubylog::Variable].each do |m|
|
374
|
+
m.send :define_method, :method_missing do |m, *args|
|
375
|
+
fct = Rubylog::DSL.normalize_functor(m)
|
376
|
+
return super if fct.nil?
|
377
|
+
raise NameError, "'#{fct}' method already exists" if respond_to? fct
|
378
|
+
Rubylog.current_theory.functor fct
|
379
|
+
self.class.send :include, Rubylog::DSL.functor_module(fct)
|
380
|
+
send m, *args
|
381
|
+
end
|
102
382
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
383
|
+
@implicit_started = true
|
384
|
+
end
|
385
|
+
|
386
|
+
def stop_implicit
|
387
|
+
[@public_interface, Rubylog::Variable].each do |m|
|
388
|
+
m.send :remove_method, :method_missing
|
107
389
|
end
|
108
|
-
|
390
|
+
@implicit_started = false
|
391
|
+
end
|
392
|
+
|
109
393
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
#else
|
115
|
-
#end
|
116
|
-
#end
|
117
|
-
#def protected *args
|
118
|
-
#if args.empty?
|
119
|
-
#@scope = :protected
|
120
|
-
#else
|
121
|
-
#end
|
122
|
-
#end
|
394
|
+
#def private *args
|
395
|
+
#if args.empty?
|
396
|
+
#@scope = :private
|
397
|
+
#else
|
123
398
|
#end
|
399
|
+
#end
|
400
|
+
#def protected *args
|
401
|
+
#if args.empty?
|
402
|
+
#@scope = :protected
|
403
|
+
#else
|
404
|
+
#end
|
405
|
+
#end
|
406
|
+
|
407
|
+
private
|
124
408
|
|
125
|
-
|
409
|
+
def check_indicator indicator
|
410
|
+
raise ArgumentError, "#{indicator.inspect} should be a predicate indicator", caller[2..-1] unless indicator.is_a? Array and
|
411
|
+
indicator.length == 2 and
|
412
|
+
indicator[0].is_a? Symbol and
|
413
|
+
indicator[1].is_a? Integer
|
126
414
|
end
|
127
|
-
end
|
128
415
|
|
129
416
|
|
130
417
|
|
418
|
+
end
|
419
|
+
|
131
420
|
|
132
421
|
|
133
422
|
|
data/lib/rubylog/variable.rb
CHANGED
@@ -4,23 +4,25 @@ module Rubylog
|
|
4
4
|
# data structure
|
5
5
|
|
6
6
|
attr_reader :name
|
7
|
-
def initialize name
|
8
|
-
@name = name
|
9
|
-
@
|
10
|
-
@dont_care = !!(name.to_s =~ /^(?:ANY|_)/)
|
7
|
+
def initialize name = :"_#{object_id}"
|
8
|
+
@name = name
|
9
|
+
@bound = false
|
10
|
+
@dont_care = !!(name.to_s =~ /^(?:ANY|_)/i)
|
11
|
+
@guards = []
|
11
12
|
end
|
12
13
|
|
13
|
-
def
|
14
|
-
@
|
14
|
+
def bound?
|
15
|
+
@bound
|
15
16
|
end
|
16
17
|
|
17
18
|
def inspect
|
18
|
-
@
|
19
|
+
if @guards.empty?
|
20
|
+
@name.to_s
|
21
|
+
else
|
22
|
+
"#{@name}#{@guards}"
|
23
|
+
end
|
19
24
|
end
|
20
25
|
|
21
|
-
def to_s
|
22
|
-
@name.to_s
|
23
|
-
end
|
24
26
|
|
25
27
|
def == other
|
26
28
|
Variable === other and @name == other.name
|
@@ -49,24 +51,43 @@ module Rubylog
|
|
49
51
|
[self]
|
50
52
|
end
|
51
53
|
|
52
|
-
#
|
53
|
-
include Unifiable
|
54
|
-
|
54
|
+
# unify two variables
|
55
55
|
def rubylog_unify other
|
56
|
-
if
|
56
|
+
# check if we are bound
|
57
|
+
if @bound
|
58
|
+
# if we are bound
|
59
|
+
# proceed to our dereferenced value
|
57
60
|
rubylog_dereference.rubylog_unify(other) do yield end
|
58
61
|
else
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
62
|
+
# if we are unbound
|
63
|
+
|
64
|
+
# dereference the other
|
65
|
+
other = other.rubylog_dereference
|
66
|
+
|
67
|
+
# if the other is a variable
|
68
|
+
if other.is_a? Rubylog::Variable
|
69
|
+
# we union our guards with the other's
|
70
|
+
other.append_guards guards do
|
71
|
+
bind_to other do
|
72
|
+
yield
|
73
|
+
end
|
74
|
+
end
|
75
|
+
else
|
76
|
+
# if the other is a value
|
77
|
+
# bind to it and
|
78
|
+
bind_to other do
|
79
|
+
# check our guards
|
80
|
+
if guards.all? {|g|g.rubylog_matches_as_guard? other}
|
81
|
+
yield
|
82
|
+
end
|
83
|
+
end
|
64
84
|
end
|
65
85
|
end
|
66
86
|
end
|
67
87
|
|
88
|
+
|
68
89
|
def rubylog_dereference
|
69
|
-
if @
|
90
|
+
if @bound
|
70
91
|
@value.rubylog_dereference
|
71
92
|
else
|
72
93
|
self
|
@@ -74,7 +95,7 @@ module Rubylog
|
|
74
95
|
end
|
75
96
|
|
76
97
|
def rubylog_deep_dereference
|
77
|
-
if @
|
98
|
+
if @bound
|
78
99
|
@value.rubylog_deep_dereference
|
79
100
|
else
|
80
101
|
self
|
@@ -85,12 +106,70 @@ module Rubylog
|
|
85
106
|
include Callable
|
86
107
|
|
87
108
|
def prove
|
88
|
-
# XXX not tested
|
89
109
|
v = value
|
90
|
-
raise InstantiationError if v.nil?
|
110
|
+
raise InstantiationError, self if v.nil?
|
91
111
|
v.prove{yield}
|
92
112
|
end
|
93
113
|
|
114
|
+
|
115
|
+
# Array splats
|
116
|
+
def to_a
|
117
|
+
[Rubylog::DSL::ArraySplat.new(self)]
|
118
|
+
end
|
119
|
+
alias to_ary to_a
|
120
|
+
|
121
|
+
# String variables
|
122
|
+
def to_s
|
123
|
+
if @guards.empty?
|
124
|
+
"#{String::RUBYLOG_VAR_START}#{@name}[]#{String::RUBYLOG_VAR_END}"
|
125
|
+
else
|
126
|
+
String::RubylogStringVariableGuards << @guards
|
127
|
+
guard_index = String::RubylogStringVariableGuards.length-1
|
128
|
+
"#{String::RUBYLOG_VAR_START}#{@name}[#{guard_index}]#{String::RUBYLOG_VAR_END}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# guards
|
133
|
+
def [] *guards
|
134
|
+
@guards += guards
|
135
|
+
self
|
136
|
+
end
|
137
|
+
|
138
|
+
attr_reader :guards
|
139
|
+
attr_writer :guards
|
140
|
+
|
141
|
+
|
142
|
+
protected
|
143
|
+
|
144
|
+
# yields with self bound to the given value
|
145
|
+
def bind_to other
|
146
|
+
begin
|
147
|
+
@bound = true; @value = other
|
148
|
+
Rubylog.current_theory.print_trace 1, "#{inspect}=#{@value.inspect}"
|
149
|
+
|
150
|
+
yield
|
151
|
+
|
152
|
+
ensure
|
153
|
+
@bound = false
|
154
|
+
Rubylog.current_theory.print_trace -1
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# yields with self.guards = self.guards + other_guards, then restores guards
|
159
|
+
def append_guards other_guards
|
160
|
+
original_guards = @guards
|
161
|
+
|
162
|
+
@guards = @guards + other_guards
|
163
|
+
|
164
|
+
begin
|
165
|
+
yield
|
166
|
+
ensure
|
167
|
+
@guards = original_guards
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
|
94
173
|
end
|
95
174
|
|
96
175
|
|