rubylog 0.0.1 → 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.
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