rubylog 1.0.0 → 2.0pre1

Sign up to get free protection for your applications and to get access to all the features.
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