rubylog 1.0.0 → 2.0pre1

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 (163) hide show
  1. data/Gemfile +3 -12
  2. data/Gemfile.lock +22 -48
  3. data/README.rdoc +38 -38
  4. data/README.rdoc.orig +284 -0
  5. data/RELEASE_NOTES.rdoc +51 -0
  6. data/Rakefile +14 -18
  7. data/TODO.txt +0 -0
  8. data/VERSION +1 -1
  9. data/examples/a_plus_b.rb +6 -0
  10. data/examples/checkmate.rb +88 -0
  11. data/examples/combination.rb +17 -0
  12. data/examples/dcg.rb +3 -2
  13. data/examples/dcg2.rb +2 -2
  14. data/{logic → examples}/directory_structure_logic.rb +3 -5
  15. data/examples/dirlist.rb +4 -0
  16. data/examples/divisors.rb +6 -0
  17. data/examples/enumerators.rb +3 -3
  18. data/examples/factorial.rb +2 -3
  19. data/examples/file_search.rb +14 -0
  20. data/examples/hanoi.rb +4 -5
  21. data/examples/hello.rb +6 -4
  22. data/examples/mice.rb +92 -0
  23. data/examples/mice2.rb +19 -0
  24. data/examples/n_queens.rb +32 -0
  25. data/examples/object_oriented.rb +14 -0
  26. data/examples/palindrome_detection.rb +18 -0
  27. data/examples/parsing.rb +6 -4
  28. data/examples/permutation.rb +12 -0
  29. data/examples/prefix.rb +13 -0
  30. data/examples/primality_by_division.rb +22 -0
  31. data/examples/primitives.rb +10 -8
  32. data/examples/sieve_of_eratosthenes.rb +14 -0
  33. data/examples/string_interpolation.rb +4 -0
  34. data/examples/sudoku.rb +52 -0
  35. data/examples/tracing.rb +19 -0
  36. data/lib/rspec/rubylog.rb +29 -0
  37. data/lib/rubylog/assertable.rb +24 -0
  38. data/lib/rubylog/builtins/arithmetics.rb +63 -0
  39. data/lib/rubylog/builtins/assumption.rb +71 -0
  40. data/lib/rubylog/builtins/ensure.rb +13 -0
  41. data/lib/rubylog/builtins/file_system.rb +30 -8
  42. data/lib/rubylog/builtins/logic.rb +69 -38
  43. data/lib/rubylog/builtins/reflection.rb +35 -50
  44. data/lib/rubylog/builtins/term.rb +15 -17
  45. data/lib/rubylog/builtins.rb +11 -0
  46. data/lib/rubylog/clause.rb +19 -0
  47. data/lib/rubylog/{interfaces/composite_term.rb → compound_term.rb} +3 -3
  48. data/lib/rubylog/context.rb +24 -0
  49. data/lib/rubylog/context_creation.rb +71 -0
  50. data/lib/rubylog/context_modules/checks.rb +35 -0
  51. data/lib/rubylog/context_modules/demonstration.rb +16 -0
  52. data/lib/rubylog/context_modules/predicates.rb +86 -0
  53. data/lib/rubylog/context_modules/primitives.rb +18 -0
  54. data/lib/rubylog/context_modules/thats.rb +13 -0
  55. data/lib/rubylog/default_context.rb +9 -0
  56. data/lib/rubylog/dsl/array_splat.rb +11 -3
  57. data/lib/rubylog/dsl/primitives.rb +24 -12
  58. data/lib/rubylog/dsl/thats.rb +6 -0
  59. data/lib/rubylog/dsl/variables.rb +56 -21
  60. data/lib/rubylog/errors.rb +26 -15
  61. data/lib/rubylog/mixins/array.rb +95 -62
  62. data/lib/rubylog/mixins/kernel.rb +3 -2
  63. data/lib/rubylog/mixins/method.rb +0 -1
  64. data/lib/rubylog/mixins/object.rb +2 -1
  65. data/lib/rubylog/mixins/proc.rb +9 -12
  66. data/lib/rubylog/mixins/string.rb +15 -23
  67. data/lib/rubylog/mixins/symbol.rb +7 -24
  68. data/lib/rubylog/nullary_predicates.rb +3 -0
  69. data/lib/rubylog/predicate.rb +53 -0
  70. data/lib/rubylog/primitive.rb +15 -0
  71. data/lib/rubylog/procedure.rb +42 -0
  72. data/lib/rubylog/rule.rb +24 -0
  73. data/lib/rubylog/structure.rb +19 -38
  74. data/lib/rubylog/{interfaces/term.rb → term.rb} +2 -7
  75. data/lib/rubylog/tracing.rb +75 -0
  76. data/lib/rubylog/variable.rb +31 -12
  77. data/lib/rubylog.rb +36 -32
  78. data/rubylog.gemspec +92 -84
  79. data/spec/inriasuite_spec.rb +906 -9
  80. data/spec/integration/custom_classes_spec.rb +61 -0
  81. data/spec/integration/dsl_spec.rb +38 -0
  82. data/spec/integration/recursion_spec.rb +14 -0
  83. data/spec/integration/theory_as_module_spec.rb +20 -0
  84. data/spec/integration/theory_as_module_with_include_spec.rb +14 -0
  85. data/spec/rspec/rubylog_spec.rb +75 -0
  86. data/spec/rubylog/assertable_spec.rb +111 -0
  87. data/spec/rubylog/builtins/arithmetics_spec.rb +94 -0
  88. data/spec/rubylog/builtins/assumption_spec.rb +70 -0
  89. data/spec/rubylog/builtins/ensure_spec.rb +8 -0
  90. data/spec/rubylog/builtins/file_system_spec.rb +40 -0
  91. data/spec/rubylog/builtins/logic_spec.rb +340 -0
  92. data/spec/rubylog/builtins/reflection_spec.rb +43 -0
  93. data/spec/rubylog/builtins/term_spec.rb +85 -0
  94. data/spec/rubylog/context_modules/demonstration_spec.rb +132 -0
  95. data/spec/rubylog/context_modules/predicates_spec.rb +57 -0
  96. data/spec/rubylog/context_modules/thats_spec.rb +94 -0
  97. data/spec/rubylog/dsl/array_splat_spec.rb +15 -0
  98. data/spec/rubylog/dsl/primitives_spec.rb +43 -0
  99. data/spec/rubylog/errors_spec.rb +18 -0
  100. data/spec/{unification_spec.rb → rubylog/interfaces/term_spec.rb} +8 -9
  101. data/spec/rubylog/mixins/array_spec.rb +80 -0
  102. data/spec/rubylog/mixins/composite_term_spec.rb +66 -0
  103. data/spec/rubylog/mixins/proc_spec.rb +59 -0
  104. data/spec/rubylog/mixins/string_spec.rb +48 -0
  105. data/spec/rubylog/mixins/symbol_spec.rb +9 -0
  106. data/spec/{clause_spec.rb → rubylog/structure_spec.rb} +16 -15
  107. data/spec/rubylog/term_spec.rb +7 -0
  108. data/spec/rubylog/tracing_spec.input +27 -0
  109. data/spec/rubylog/tracing_spec.rb +44 -0
  110. data/spec/rubylog/variable_spec.rb +279 -0
  111. data/spec/spec_helper.rb +1 -0
  112. data/vimrc +11 -0
  113. metadata +103 -123
  114. data/README.hu.rb +0 -58
  115. data/bin/rubylog +0 -18
  116. data/examples/theory.rb +0 -32
  117. data/lib/rubylog/builtins/default.rb +0 -10
  118. data/lib/rubylog/dsl.rb +0 -70
  119. data/lib/rubylog/interfaces/assertable.rb +0 -16
  120. data/lib/rubylog/interfaces/callable.rb +0 -18
  121. data/lib/rubylog/interfaces/predicate.rb +0 -8
  122. data/lib/rubylog/interfaces/procedure.rb +0 -60
  123. data/lib/rubylog/mixins/class.rb +0 -11
  124. data/lib/rubylog/simple_procedure.rb +0 -8
  125. data/lib/rubylog/theory.rb +0 -422
  126. data/logic/builtins/file_system_logic.rb +0 -23
  127. data/logic/builtins/reflection_logic.rb +0 -40
  128. data/logic/dereference_logic.rb +0 -23
  129. data/logic/dsl_logic.rb +0 -29
  130. data/logic/errors_logic.rb +0 -9
  131. data/logic/guard_logic.rb +0 -115
  132. data/logic/list_logic.rb +0 -55
  133. data/logic/map_logic.rb +0 -15
  134. data/logic/multitheory.rb +0 -23
  135. data/logic/recursion_logic.rb +0 -12
  136. data/logic/string_logic.rb +0 -41
  137. data/logic/thats_logic.rb +0 -51
  138. data/logic/variable_logic.rb +0 -24
  139. data/spec/bartak_guide_spec.rb +0 -86
  140. data/spec/builtins/all_spec.rb +0 -99
  141. data/spec/builtins/and_spec.rb +0 -22
  142. data/spec/builtins/array_spec.rb +0 -16
  143. data/spec/builtins/branch_or_spec.rb +0 -27
  144. data/spec/builtins/cut_spec.rb +0 -44
  145. data/spec/builtins/fail_spec.rb +0 -5
  146. data/spec/builtins/false_spec.rb +0 -5
  147. data/spec/builtins/in_spec.rb +0 -38
  148. data/spec/builtins/is_false_spec.rb +0 -12
  149. data/spec/builtins/is_spec.rb +0 -26
  150. data/spec/builtins/matches_spec.rb +0 -23
  151. data/spec/builtins/or_spec.rb +0 -22
  152. data/spec/builtins/splits_to.rb +0 -18
  153. data/spec/builtins/then_spec.rb +0 -27
  154. data/spec/builtins/true_spec.rb +0 -5
  155. data/spec/compilation_spec.rb +0 -61
  156. data/spec/custom_classes_spec.rb +0 -43
  157. data/spec/dereference.rb +0 -10
  158. data/spec/queries_spec.rb +0 -150
  159. data/spec/recursion_spec.rb +0 -18
  160. data/spec/ruby_code_spec.rb +0 -52
  161. data/spec/rules_spec.rb +0 -97
  162. data/spec/theory_spec.rb +0 -29
  163. data/spec/variable_spec.rb +0 -26
@@ -0,0 +1,71 @@
1
+ module Rubylog
2
+
3
+ # Creates a new context from a new object or optionally from an existing source
4
+ # object
5
+ # @return the new context
6
+ def self.create_context source_object=Object.new
7
+ class << source_object
8
+ include Rubylog::Context
9
+ end
10
+ source_object
11
+ end
12
+
13
+
14
+ # You can use this to access Rubylog in the command line or in the main object.
15
+ #
16
+ # For example,
17
+ #
18
+ # require 'rubylog'
19
+ # extend Rubylog::Context
20
+ #
21
+ # solve A.is(5).and { puts A; true }
22
+ #
23
+ # You can also use this to convert any object to a context.
24
+ #
25
+ # You can extend Rubylog::Context to modules or classes.
26
+ #
27
+ # class A
28
+ # extend Rubylog::Context
29
+ # predicate ".good"
30
+ # end
31
+ #
32
+ # This automatically uses the class as default subject.
33
+ #
34
+ # Finally, you can include it to a class.
35
+ #
36
+ # class MyContext
37
+ # include Rubylog::Context
38
+ # end
39
+ #
40
+ # myc = MyContext.new
41
+ # myc.predicate_for A, ".bad"
42
+ #
43
+ #
44
+ module Context
45
+
46
+ def self.extended context
47
+ # We include DSL::Variables in its singleton class
48
+ class << context
49
+ include Rubylog::DSL::Variables
50
+ end
51
+
52
+ if context.is_a? Module
53
+ # if context is a class or module, we also include DSL::Variables directly
54
+ # in it, so that they can be accessed in class_eval mode or from an
55
+ # instance method.
56
+ context.send :include, Rubylog::DSL::Variables
57
+
58
+ # Also, we set self as a subject, so that +predicate+ automatically attaches
59
+ # functors to the class.
60
+ context.default_subject = context
61
+ end
62
+ end
63
+
64
+ def self.included class_or_module
65
+ class_or_module.send :include, Rubylog::DSL::Variables
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+
@@ -0,0 +1,35 @@
1
+ module Rubylog::ContextModules
2
+ module Checks
3
+ def check_passed goal
4
+ end
5
+
6
+ def check_failed goal
7
+ raise Rubylog::CheckFailed.new(goal)
8
+ end
9
+
10
+ def check_raised_exception goal, exception
11
+ raise exception
12
+ end
13
+
14
+ # Tries to prove goal (or block if goal is not given). If it proves, calles
15
+ # +check_passed+. If it fails, calls +check_failed+. If it raises an exception, calls +check_raised_exception+.
16
+ def check goal=nil, &block
17
+ goal ||= block
18
+ result = nil
19
+ begin
20
+ result = goal.true?
21
+ rescue
22
+ check_raised_exception goal, $!
23
+ else
24
+ if result
25
+ check_passed goal, &block
26
+ else
27
+ check_failed goal, &block
28
+ end
29
+ end
30
+ result
31
+ end
32
+
33
+
34
+ end
35
+ end
@@ -0,0 +1,16 @@
1
+ module Rubylog::ContextModules
2
+ module Demonstration
3
+ def solve goal, &block
4
+ goal ||= block
5
+ raise ArgumentError, "No goal given", caller if goal.nil?
6
+ goal.solve &block
7
+ end
8
+
9
+ def true? goal=nil, &block
10
+ goal ||= block
11
+ raise ArgumentError, "No goal given", caller if goal.nil?
12
+ goal.true?
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,86 @@
1
+ require 'rubylog/rule'
2
+
3
+
4
+ module Rubylog
5
+ module ContextModules
6
+ module Predicates
7
+
8
+ def predicate *indicators
9
+ each_indicator(indicators) do |indicator|
10
+ create_procedure(indicator).add_functor_to [default_subject, Variable]
11
+ end
12
+ end
13
+
14
+ def predicate_for subjects, *indicators
15
+ each_indicator(indicators) do |indicator|
16
+ create_procedure(indicator).add_functor_to [subjects, Variable]
17
+ end
18
+ end
19
+
20
+ def predicate_for_context *indicators
21
+ each_indicator(indicators) do |indicator|
22
+ create_procedure(indicator).add_functor_to ::Rubylog::Context
23
+ end
24
+ end
25
+
26
+ attr_writer :default_subject
27
+ def default_subject
28
+ @default_subject || []
29
+ end
30
+
31
+
32
+ protected
33
+
34
+ def create_procedure indicator
35
+ Rubylog::Procedure.new indicator[0], indicator[1]
36
+ end
37
+
38
+ def each_indicator indicators
39
+ # TODO check if not empty
40
+ #
41
+ indicators.
42
+ flatten.
43
+ map{|str|str.split(" ")}.
44
+ flatten.
45
+ map{|i| unhumanize_indicator(i)}.
46
+ each {|i| yield i }
47
+ end
48
+
49
+
50
+ # Makes human-friendly output from the indicator
51
+ # For example, .and()
52
+ def humanize_indicator indicator
53
+ return indicator if String === indicator
54
+ functor, arity = indicator
55
+ if arity > 1
56
+ ".#{functor}(#{ ','*(arity-2) })"
57
+ elsif arity == 1
58
+ ".#{functor}"
59
+ elsif arity == 0
60
+ ":#{functor}"
61
+ end
62
+ end
63
+
64
+ # Makes internal representation from predicate indicator
65
+ #
66
+ # For example, <tt>.and()</tt> becomes <tt>[:and,2]</tt>
67
+ def unhumanize_indicator indicator
68
+ case indicator
69
+ when Array
70
+ indicator
71
+ when /\A:(\w+)\z/
72
+ [:"#{$1}",0]
73
+ when /\A\w*\.(\w+)\z/
74
+ [:"#{$1}",1]
75
+ when /\A\w*\.(\w+)\(\w*((,\w*)*)\)\z/
76
+ [:"#{$1}",$2.count(",")+2]
77
+ else
78
+ raise ArgumentError, "invalid indicator: #{indicator.inspect}"
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+
@@ -0,0 +1,18 @@
1
+ require 'rubylog/dsl/primitives'
2
+
3
+ module Rubylog::ContextModules
4
+ module Primitives
5
+ def primitives
6
+ Rubylog::DSL::Primitives.new [default_subject, ::Rubylog::Variable]
7
+ end
8
+
9
+ def primitives_for subject
10
+ Rubylog::DSL::Primitives.new [subject, ::Rubylog::Variable]
11
+ end
12
+
13
+ def primitives_for_context
14
+ Rubylog::DSL::Primitives.new [::Rubylog::Context]
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubylog/dsl/thats'
2
+
3
+ module Rubylog::ContextModules
4
+ module Thats
5
+ def thats
6
+ Rubylog::DSL::Thats.new
7
+ end
8
+
9
+ def thats_not
10
+ Rubylog::DSL::Thats::Not.new
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Rubylog
2
+ DefaultContext = Object.new
3
+
4
+ # create the context
5
+ class << DefaultContext
6
+ include Rubylog::Context
7
+ end
8
+
9
+ end
@@ -9,11 +9,19 @@ class Rubylog::DSL::ArraySplat
9
9
  end
10
10
 
11
11
  def inspect
12
- "*#{var}"
12
+ "*#{var.inspect}"
13
13
  end
14
14
 
15
- # CompositeTerm methods
16
- include Rubylog::CompositeTerm
15
+ def eql? other
16
+ self.class == other.class && @var.eql?(other.var)
17
+ end
18
+
19
+ def == other
20
+ self.class == other.class && @var == other.var
21
+ end
22
+
23
+ # CompoundTerm methods
24
+ include Rubylog::CompoundTerm
17
25
 
18
26
  def rubylog_clone &block
19
27
  block[Rubylog::DSL::ArraySplat.new(@var.rubylog_clone(&block))]
@@ -1,17 +1,29 @@
1
- class Rubylog::DSL::Primitives
2
- def initialize theory
3
- @theory = theory
4
- end
1
+ module Rubylog
2
+ class DSL::Primitives
3
+ def initialize subject
4
+ @subject = subject
5
+ end
6
+
7
+
8
+ def singleton_method_added name
9
+ unless name == :singleton_method_added
10
+ m = method(name)
5
11
 
6
- def singleton_method_added name
7
- unless name == :singleton_method_added
8
- m = method(name)
9
- @theory.functor name unless m.arity.zero?
10
- @theory[[name, m.arity]] = m
12
+ predicate = Rubylog::Primitive.new(name, m)
13
+
14
+ # nullary predicate
15
+ if m.arity.zero?
16
+ Rubylog::NullaryPredicates[name] = predicate
17
+ else
18
+ predicate.add_functor_to(@subject)
19
+ end
20
+ end
11
21
  end
12
- end
13
22
 
14
- def inspect
15
- "primitives"
23
+ def inspect
24
+ if @subject
25
+ "primitives_for(#{@subject.inspect})"
26
+ end
27
+ end
16
28
  end
17
29
  end
@@ -20,3 +20,9 @@ class Rubylog::DSL::Thats < BasicObject
20
20
  end
21
21
  end
22
22
  end
23
+
24
+ class Rubylog::DSL::Thats::Not < Rubylog::DSL::Thats
25
+ def rubylog_matches_as_guard? other
26
+ not super
27
+ end
28
+ end
@@ -1,30 +1,65 @@
1
- module Rubylog::DSL::Variables
2
- def self.included mod
3
- def mod.const_missing c
4
- # different semantics in native blocks than otherwise
5
- # @see Proc#call_with_rubylog_variables
6
- if vars = Thread.current[:rubylog_current_variables]
7
- # in native blocks
1
+ require 'rubylog/variable'
8
2
 
9
- # find the appropriate variable name
10
- var = vars.find{|v|v.name == c}
3
+ module Rubylog::DSL
4
+ # When included to a module, defines const_missing. This method creates a new
5
+ # variable automatically, but if a variable with that name
6
+ # exists in the current dynamic context, returns its deeply dereferenced
7
+ # value.
8
+ #
9
+ # For example,
10
+ #
11
+ # A # => A
12
+ #
13
+ # In blocks, variables are substituted with the deeply dereferenced value:
14
+ #
15
+ # A.is(5).map{A} # => [5]
16
+ # B.is(5).and(A.is([2,B])).map{A} # => [[2,5]]
17
+ #
18
+ # When variables do not have a value, it is not substituted.
19
+ #
20
+ # A.is(B).map{A} # => [B]
21
+ # B.is(C).and(A.is([2,B])).map{A} # => [[2,C]]
22
+ # A.is([B]).map{A} # => [[B]]
23
+ #
24
+ # @see Proc#call_with_rubylog_variables
25
+ module Variables
26
+ def self.included mod
11
27
 
12
- # return new variable if not found (probably the user wants to start using a new
13
- # one)
14
- return Rubylog::Variable.new c unless var
28
+ def mod.const_missing c
29
+ if vars = Thread.current[:rubylog_current_variables]
30
+ # in native blocks
15
31
 
16
- # dereference it
17
- result = var.rubylog_deep_dereference
32
+ # find the appropriate variable name
33
+ var = vars.find{|v|v.name == c}
18
34
 
19
- # return nil if not bound
20
- return nil if result.is_a? Rubylog::Variable
35
+ # return new variable if not found (most probably the user wants to start using a new
36
+ # one)
37
+ return Rubylog::Variable.new c unless var
21
38
 
22
- # return the value
23
- result
24
- else
25
- Rubylog::Variable.new c
39
+ # dereference it
40
+ result = var.rubylog_deep_dereference
41
+
42
+ # return the value
43
+ result
44
+ else
45
+ Rubylog::Variable.new c
46
+ end
47
+ end
48
+
49
+ # Call the given block with variables substituted with their
50
+ def self.with_vars vars
51
+ begin
52
+ old_vars = Thread.current[:rubylog_current_variables]
53
+ if old_vars
54
+ Thread.current[:rubylog_current_variables] = vars + old_vars
55
+ else
56
+ Thread.current[:rubylog_current_variables] = vars
57
+ end
58
+ yield
59
+ ensure
60
+ Thread.current[:rubylog_current_variables] = old_vars
61
+ end
26
62
  end
27
63
  end
28
64
  end
29
65
  end
30
-
@@ -1,35 +1,46 @@
1
1
  module Rubylog
2
+
2
3
  class RubylogError < StandardError
3
- end
4
-
5
- class SyntaxError < RubylogError
6
- end
4
+ def initialize *args
5
+ super
7
6
 
8
- class DiscontiguousPredicateError < RubylogError
9
- end
7
+ remove_internal_lines
8
+ end
10
9
 
11
- class MultitheoryError < RubylogError
10
+ def remove_internal_lines
11
+ internal_dir = File.dirname(__FILE__) or return
12
+ return unless backtrace
13
+ index = backtrace.index{|l| not l.start_with?(internal_dir) } or return
14
+ set_backtrace backtrace[index..-1]
15
+ end
12
16
  end
13
-
14
- class BuiltinPredicateError < RubylogError
17
+
18
+ class SyntaxError < RubylogError
15
19
  end
16
20
 
17
21
  class ExistenceError < RubylogError
22
+ def initialize clause
23
+ super "Predicate #{clause.inspect} does not exist"
24
+ end
18
25
  end
19
26
 
20
27
  class InvalidStateError < RubylogError
21
28
  end
22
29
 
23
30
  class InstantiationError < RubylogError
24
- end
25
-
26
- class TypeError < RubylogError
27
- end
28
-
29
- class UnknownVariableError < RubylogError
31
+ def initialize functor, args=nil
32
+ if args
33
+ super "Instantiation error in #{args[0].inspect}.#{functor}(#{args[1..-1].map{|a|a.rubylog_deep_dereference.inspect}.join(", ")})"
34
+ else
35
+ super "Instantiation error in #{functor.inspect}"
36
+ end
37
+ end
30
38
  end
31
39
 
32
40
  class CheckFailed < StandardError
41
+ def initialize goal
42
+ super "Check failed #{goal.inspect}"
43
+ end
33
44
  end
34
45
 
35
46
  end
@@ -1,100 +1,131 @@
1
- module Rubylog
2
- # for the future
3
- def self.unify_arrays a, a_start, a_end, b, b_start, b_end
4
- case
5
- when (a_start > a_end and b_start > b_end)
6
- yield
7
- when (a_start > a_end or b_start > b_end)
8
- return
9
- when (a[a_start].is_a? Rubylog::DSL::ArraySplat and b[b_start].is_a? Rubylog::DSL::ArraySplat)
10
- raise "not implemented"
11
- when (a[a_start].is_a? Rubylog::DSL::ArraySplat)
12
- # A=[]
13
- a[a_start].var.rubylog_unify [] do
14
- unify_arrays(a, a_start+1, a_end, b, b_start, b_end) { yield }
15
- end
16
-
17
- # A=[_X, *_Y]
18
- new_arr = [Rubylog::Variable.new, Rubylog::DSL::ArraySplat.new(Rubylog::Variable.new)]
19
- a[a_start].var.rubylog_unify new_arr do
20
- unify_arrays(a[a_start].var, a_start+1, a_end, b, b_start, b_end) { yield }
21
- end
22
- when (b[b_start].is_a? Rubylog::DSL::ArraySplat)
23
- raise "not implemented"
24
- else
25
- a[a_start].rubylog_unify b[b_start] do
26
- unify_arrays(a, a_start+1, a_end, b, b_start+1, b_end) { yield }
27
- end
28
- end
29
- end
30
- end
1
+ require 'rubylog/dsl/array_splat'
31
2
 
32
3
  class Array
33
4
 
34
5
  # Term methods
35
6
  def rubylog_unify other
36
- return super{yield} unless other.instance_of? self.class
37
- #Rubylog.unify_arrays(self, 0, length-1, other, 0, other.length-1) { yield }
38
- if empty?
39
- if other.empty?
7
+ case
8
+
9
+ when ! other.is_a?(Array)
10
+ # [...] = 1
11
+ return super{yield}
12
+
13
+ when empty?
14
+ case
15
+ when other.empty?
16
+ # if [] = []
17
+ # then true
40
18
  yield
41
- elsif other[0].is_a? Rubylog::DSL::ArraySplat
42
- other[0].var.rubylog_unify [] do
19
+ when other[0].is_a?(Rubylog::DSL::ArraySplat)
20
+ # if [] = [*A,...]
21
+ # then [] = A, []=[...]
22
+ [].rubylog_unify other[0].var do
43
23
  self.rubylog_unify other[1..-1] do
44
24
  yield
45
25
  end
46
26
  end
47
27
  else
48
- # failed
28
+ # fail
49
29
  end
50
- else
51
- if self[0].is_a? Rubylog::DSL::ArraySplat
52
- # optimize [*A] = [*B]
53
- if other[0].is_a? Rubylog::DSL::ArraySplat and self.length == 1 and other.length == 1
30
+
31
+ when self[0].is_a?(Rubylog::DSL::ArraySplat)
32
+ # if [*A,...] = [...]
33
+ case
34
+ when other.empty?
35
+ # [*A,...] = []
36
+ self[0].var.rubylog_unify [] do
37
+ self[1..-1].rubylog_unify [] do
38
+ yield
39
+ end
40
+ end
41
+ when other[0].is_a?(Rubylog::DSL::ArraySplat )
42
+ # [*A,...] = [*B,...]
43
+ case
44
+ when self.length == 1 && other.length == 1
45
+ # if [*A] = [*B]
46
+ # then A=B
54
47
  self[0].var.rubylog_unify other[0].var do
55
48
  yield
56
49
  end
57
- return
50
+ when self.length == 1
51
+ # if [*A] = [*B,...]
52
+ # then A=[...]
53
+ self[0].var.rubylog_unify other do
54
+ yield
55
+ end
56
+ # TODO: we can also optimize ends of arrays.
57
+ else
58
+ # this may lead to infinite loop if variables are unbound
59
+ # TODO: Maybe an InstantiationError may be better.
60
+ # if [*A,...] = [*B,...]
61
+ # then eiter A=[], [...]=[*B,...]
62
+ self[0].var.rubylog_unify [] do
63
+ self[1..-1].rubylog_unify other do
64
+ yield
65
+ end
66
+ end
67
+ # or A=[H,*T], [H,*T,...]=[*B,...]
68
+ part = [Rubylog::Variable.new, Rubylog::DSL::ArraySplat.new]
69
+ self[0].var.rubylog_unify part do
70
+ (part + self[1..-1]).rubylog_unify other do
71
+ yield
72
+ end
73
+ end
74
+
58
75
  end
59
76
 
77
+ else
78
+ # if [*A,...] = [X,...]
79
+ # then eiter A=[], [...]=[X,...]
60
80
  self[0].var.rubylog_unify [] do
61
81
  self[1..-1].rubylog_unify other do
62
82
  yield
63
83
  end
64
84
  end
85
+ # or A=[H,*T], [H,*T,...]=[X,...]
65
86
  part = [Rubylog::Variable.new, Rubylog::DSL::ArraySplat.new]
66
87
  self[0].var.rubylog_unify part do
67
88
  (part + self[1..-1]).rubylog_unify other do
68
89
  yield
69
90
  end
70
91
  end
71
- else
72
- if other[0].is_a? Rubylog::DSL::ArraySplat
73
- other[0].var.rubylog_unify [] do
74
- self.rubylog_unify other[1..-1] do
75
- yield
76
- end
92
+ end
93
+
94
+ else
95
+ # if [H,...]
96
+ case
97
+ when other.empty?
98
+ # [H,...] = []
99
+ # fail
100
+ when other[0].is_a?(Rubylog::DSL::ArraySplat)
101
+ # [H,...] = [*A,...]
102
+ # either []=A, [H,...]=[...]
103
+ [].rubylog_unify other[0].var do
104
+ self.rubylog_unify other[1..-1] do
105
+ yield
77
106
  end
78
- part = [Rubylog::Variable.new, Rubylog::DSL::ArraySplat.new]
79
- other[0].var.rubylog_unify part do
80
- self.rubylog_unify(part + other[1..-1]) do
81
- yield
82
- end
107
+ end
108
+ # or A=[X,*R], [H,...]=[X,*R,...]
109
+ part = [Rubylog::Variable.new, Rubylog::DSL::ArraySplat.new]
110
+ other[0].var.rubylog_unify part do
111
+ self.rubylog_unify(part + other[1..-1]) do
112
+ yield
83
113
  end
84
- else
85
- return if other.empty?
86
- self[0].rubylog_unify other[0] do
87
- self[1..-1].rubylog_unify other[1..-1] do
88
- yield
89
- end
114
+ end
115
+ else
116
+ # if [H,...]=[X,...]
117
+ # then H=X, [...]=[...]
118
+ self[0].rubylog_unify other[0] do
119
+ self[1..-1].rubylog_unify other[1..-1] do
120
+ yield
90
121
  end
91
122
  end
92
123
  end
93
124
  end
94
125
  end
95
126
 
96
- # CompositeTerm methods
97
- include Rubylog::CompositeTerm
127
+ # CompoundTerm methods
128
+ include Rubylog::CompoundTerm
98
129
  def rubylog_clone &block
99
130
  block[map{|t|t.rubylog_clone &block}]
100
131
  end
@@ -104,9 +135,11 @@ class Array
104
135
  case t
105
136
  when Rubylog::DSL::ArraySplat
106
137
  v = t.var.rubylog_dereference
107
- if v.is_a? Array
138
+ if v.is_a?(Array)
139
+ # if it could be resolved
108
140
  v.rubylog_deep_dereference
109
141
  else
142
+ # if it is still a variable
110
143
  [t]
111
144
  end
112
145
  else