rubylog 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/Gemfile +1 -1
  2. data/README.hu.rb +58 -0
  3. data/README.rdoc +248 -89
  4. data/Rakefile +6 -1
  5. data/VERSION +1 -1
  6. data/bin/rubylog +18 -0
  7. data/examples/dcg.rb +35 -0
  8. data/examples/dcg2.rb +42 -0
  9. data/examples/enumerators.rb +30 -0
  10. data/examples/factorial.rb +9 -8
  11. data/examples/hanoi.rb +24 -0
  12. data/examples/hello.rb +11 -5
  13. data/examples/parsing.rb +27 -0
  14. data/examples/primitives.rb +24 -0
  15. data/examples/theory.rb +22 -10
  16. data/lib/rubylog/builtins/default.rb +10 -0
  17. data/lib/rubylog/builtins/file_system.rb +15 -0
  18. data/lib/rubylog/builtins/logic.rb +109 -0
  19. data/lib/rubylog/builtins/reflection.rb +94 -0
  20. data/lib/rubylog/builtins/term.rb +47 -0
  21. data/lib/rubylog/dsl/array_splat.rb +25 -0
  22. data/lib/rubylog/dsl/primitives.rb +17 -0
  23. data/lib/rubylog/dsl/thats.rb +22 -0
  24. data/lib/rubylog/dsl/variables.rb +30 -0
  25. data/lib/rubylog/dsl.rb +35 -17
  26. data/lib/rubylog/errors.rb +19 -1
  27. data/lib/rubylog/interfaces/assertable.rb +16 -0
  28. data/lib/rubylog/interfaces/callable.rb +18 -0
  29. data/lib/rubylog/interfaces/composite_term.rb +47 -0
  30. data/lib/rubylog/interfaces/predicate.rb +8 -0
  31. data/lib/rubylog/interfaces/procedure.rb +60 -0
  32. data/lib/rubylog/interfaces/term.rb +41 -0
  33. data/lib/rubylog/mixins/array.rb +118 -0
  34. data/lib/{class.rb → rubylog/mixins/class.rb} +2 -2
  35. data/lib/rubylog/mixins/hash.rb +8 -0
  36. data/lib/rubylog/mixins/kernel.rb +5 -0
  37. data/lib/rubylog/mixins/method.rb +3 -0
  38. data/lib/rubylog/mixins/object.rb +8 -0
  39. data/lib/rubylog/mixins/proc.rb +37 -0
  40. data/lib/rubylog/mixins/string.rb +104 -0
  41. data/lib/rubylog/mixins/symbol.rb +44 -0
  42. data/lib/rubylog/simple_procedure.rb +8 -0
  43. data/lib/rubylog/{clause.rb → structure.rb} +32 -31
  44. data/lib/rubylog/theory.rb +368 -79
  45. data/lib/rubylog/variable.rb +102 -23
  46. data/lib/rubylog.rb +33 -25
  47. data/logic/builtins/file_system_logic.rb +23 -0
  48. data/logic/builtins/reflection_logic.rb +40 -0
  49. data/logic/dereference_logic.rb +23 -0
  50. data/logic/directory_structure_logic.rb +19 -0
  51. data/logic/dsl_logic.rb +29 -0
  52. data/logic/errors_logic.rb +9 -0
  53. data/logic/guard_logic.rb +115 -0
  54. data/logic/list_logic.rb +55 -0
  55. data/logic/map_logic.rb +15 -0
  56. data/logic/multitheory.rb +23 -0
  57. data/logic/recursion_logic.rb +12 -0
  58. data/logic/string_logic.rb +41 -0
  59. data/logic/thats_logic.rb +51 -0
  60. data/logic/variable_logic.rb +24 -0
  61. data/rubylog.gemspec +85 -46
  62. data/spec/bartak_guide_spec.rb +57 -62
  63. data/spec/builtins/all_spec.rb +99 -0
  64. data/spec/builtins/and_spec.rb +22 -0
  65. data/spec/builtins/array_spec.rb +16 -0
  66. data/spec/builtins/branch_or_spec.rb +27 -0
  67. data/spec/builtins/cut_spec.rb +44 -0
  68. data/spec/builtins/fail_spec.rb +5 -0
  69. data/spec/builtins/false_spec.rb +5 -0
  70. data/spec/builtins/in_spec.rb +38 -0
  71. data/spec/builtins/is_false_spec.rb +12 -0
  72. data/spec/builtins/is_spec.rb +26 -0
  73. data/spec/builtins/matches_spec.rb +23 -0
  74. data/spec/builtins/or_spec.rb +22 -0
  75. data/spec/{rubylog/builtins → builtins}/splits_to.rb +0 -0
  76. data/spec/builtins/then_spec.rb +27 -0
  77. data/spec/builtins/true_spec.rb +5 -0
  78. data/spec/clause_spec.rb +82 -0
  79. data/spec/compilation_spec.rb +61 -0
  80. data/spec/custom_classes_spec.rb +43 -0
  81. data/spec/dereference.rb +10 -0
  82. data/spec/{inriasuite.rb → inriasuite_spec.rb} +2 -9
  83. data/spec/queries_spec.rb +150 -0
  84. data/spec/recursion_spec.rb +4 -4
  85. data/spec/ruby_code_spec.rb +52 -0
  86. data/spec/rules_spec.rb +97 -0
  87. data/spec/spec_helper.rb +6 -2
  88. data/spec/theory_spec.rb +28 -0
  89. data/spec/unification_spec.rb +84 -0
  90. data/spec/variable_spec.rb +26 -0
  91. metadata +153 -180
  92. data/examples/4queens.rb +0 -10
  93. data/examples/calculation.rb +0 -12
  94. data/examples/concepts.rb +0 -46
  95. data/examples/fp.rb +0 -56
  96. data/examples/historia_de_espana.rb +0 -31
  97. data/examples/idea.rb +0 -143
  98. data/examples/lists.rb +0 -5
  99. data/examples/mechanika.rb +0 -409
  100. data/examples/parse.rb +0 -15
  101. data/lib/array.rb +0 -24
  102. data/lib/method.rb +0 -4
  103. data/lib/object.rb +0 -5
  104. data/lib/proc.rb +0 -4
  105. data/lib/rubylog/builtins.rb +0 -193
  106. data/lib/rubylog/callable.rb +0 -20
  107. data/lib/rubylog/composite_term.rb +0 -38
  108. data/lib/rubylog/dsl/constants.rb +0 -15
  109. data/lib/rubylog/dsl/first_order_functors.rb +0 -9
  110. data/lib/rubylog/dsl/global_functors.rb +0 -3
  111. data/lib/rubylog/dsl/second_order_functors.rb +0 -8
  112. data/lib/rubylog/internal_helpers.rb +0 -16
  113. data/lib/rubylog/predicate.rb +0 -34
  114. data/lib/rubylog/proc_method_additions.rb +0 -69
  115. data/lib/rubylog/term.rb +0 -20
  116. data/lib/rubylog/unifiable.rb +0 -19
  117. data/lib/symbol.rb +0 -35
  118. data/script/inriasuite2spec +0 -0
  119. data/script/inriasuite2spec.pl +0 -22
  120. data/spec/rubylog/clause_spec.rb +0 -81
  121. data/spec/rubylog/variable_spec.rb +0 -25
  122. data/spec/rubylog_spec.rb +0 -914
@@ -1,133 +1,422 @@
1
1
  module Rubylog
2
- class Cut < StandardError
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
- def self.theory
6
- Thread.current[:rubylog_theory]
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
- class Theory
10
- include Rubylog::DSL::Constants
69
+ def [] indicator
70
+ @database[indicator]
71
+ end
72
+
73
+ def []= indicator, predicate
74
+ @database[indicator] = predicate
75
+ end
11
76
 
12
- def self.new!
13
- Thread.current[:rubylog_theory] = new
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
- def initialize
17
- @database = Hash.new{|h,k| h[k] =
18
- {}
19
- }.merge! BUILTINS
20
- @variable_bindings = []
21
- @public_interface = Module.new
22
- @trace = false
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
- def [] *args
26
- database[*args]
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
- def clear
30
- initialize
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
- # directives
35
- #
36
- def predicate *descs
37
- descs.each do |desc|
38
- create_predicate *desc
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
- def discontinuous *descs
43
- descs.each do |desc|
44
- create_predicate(*desc).discontinuous!
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
- attr_reader :database
49
- attr_reader :public_interface
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
- # predicates
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
- def assert head, body=:true
55
- functor, arity = head.functor, head.arity
56
- predicate = database[functor][arity]
57
- if predicate
58
- check_assertable predicate, head, body
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
- predicate = create_predicate functor, arity
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
- def solve goal
320
+ def solve goal, &block
321
+ with_context do
68
322
  goal = goal.rubylog_compile_variables
69
- goal.prove { yield(*goal.rubylog_variables.map{|v|v.value}) }
323
+ goal.prove { block.call_with_rubylog_variables(goal.rubylog_variables) }
70
324
  end
325
+ end
71
326
 
72
- def true? goal
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
- # debugging
79
- #
80
- #
81
- def trace?
82
- @trace
83
- end
339
+ alias prove true?
84
340
 
85
- def trace!
86
- @trace=true
87
- @trace_levels = 0
88
- end
341
+ # debugging
342
+ #
343
+ #
344
+ def trace val=true, &block
345
+ @trace=block || val
346
+ @trace_levels = 0
347
+ end
89
348
 
90
- def trace level, *args
91
- return unless @trace
92
- @trace_levels += level
93
- puts " "*level + args.map{|a|a.inspect}.join(" ") if not args.empty?
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
- def check_assertable predicate, head, body
100
- raise BuiltinPredicateError, head.desc.inspect, caller[2..-1] unless predicate.is_a? Predicate
101
- raise DiscontinuousPredicateError, head.desc.inspect, caller[2..-1] if not predicate.empty? and predicate != @last_predicate and not predicate.discontinuous?
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
- def create_predicate functor, arity
105
- DSL.add_functors_to @public_interface, functor
106
- database[functor][arity] = Predicate.new
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
- #class << self
111
- #def private *args
112
- #if args.empty?
113
- #@scope = :private
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
 
@@ -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
- @assigned = false
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 assigned?
14
- @assigned
14
+ def bound?
15
+ @bound
15
16
  end
16
17
 
17
18
  def inspect
18
- @name.to_s
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
- # Unifiable methods
53
- include Unifiable
54
-
54
+ # unify two variables
55
55
  def rubylog_unify other
56
- if @assigned
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
- begin
60
- @assigned = true; @value = other
61
- yield
62
- ensure
63
- @assigned = false
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 @assigned
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 @assigned
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