mini_kraken 0.2.03 → 0.2.04

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/lib/mini_kraken/atomic/all_atomic.rb +4 -0
  4. data/lib/mini_kraken/{core → atomic}/atomic_term.rb +23 -9
  5. data/lib/mini_kraken/atomic/k_boolean.rb +42 -0
  6. data/lib/mini_kraken/{core → atomic}/k_integer.rb +2 -1
  7. data/lib/mini_kraken/{core → atomic}/k_symbol.rb +5 -3
  8. data/lib/mini_kraken/{core → composite}/composite_term.rb +6 -4
  9. data/lib/mini_kraken/composite/cons_cell.rb +132 -0
  10. data/lib/mini_kraken/{core → composite}/cons_cell_visitor.rb +1 -1
  11. data/lib/mini_kraken/core/association_walker.rb +14 -14
  12. data/lib/mini_kraken/core/binary_relation.rb +10 -10
  13. data/lib/mini_kraken/core/equals.rb +159 -161
  14. data/lib/mini_kraken/core/goal_relation.rb +2 -2
  15. data/lib/mini_kraken/core/goal_template.rb +7 -7
  16. data/lib/mini_kraken/core/{variable.rb → log_var.rb} +10 -3
  17. data/lib/mini_kraken/core/{variable_ref.rb → log_var_ref.rb} +3 -3
  18. data/lib/mini_kraken/core/tap.rb +46 -0
  19. data/lib/mini_kraken/core/vocabulary.rb +8 -8
  20. data/lib/mini_kraken/glue/dsl.rb +21 -17
  21. data/lib/mini_kraken/glue/fresh_env.rb +7 -2
  22. data/lib/mini_kraken/glue/fresh_env_factory.rb +1 -1
  23. data/lib/mini_kraken/glue/run_star_expression.rb +2 -2
  24. data/lib/mini_kraken/version.rb +1 -1
  25. data/spec/.rubocop.yml +1 -1
  26. data/spec/atomic/atomic_term_spec.rb +94 -0
  27. data/spec/{core → atomic}/k_boolean_spec.rb +19 -34
  28. data/spec/{core → atomic}/k_symbol_spec.rb +3 -11
  29. data/spec/{core → composite}/cons_cell_spec.rb +10 -8
  30. data/spec/{core → composite}/cons_cell_visitor_spec.rb +11 -10
  31. data/spec/core/association_spec.rb +6 -4
  32. data/spec/core/association_walker_spec.rb +8 -6
  33. data/spec/core/conde_spec.rb +19 -17
  34. data/spec/core/conj2_spec.rb +10 -8
  35. data/spec/core/def_relation_spec.rb +9 -7
  36. data/spec/core/disj2_spec.rb +11 -10
  37. data/spec/core/environment_spec.rb +12 -10
  38. data/spec/core/equals_spec.rb +12 -10
  39. data/spec/core/goal_spec.rb +6 -5
  40. data/spec/core/goal_template_spec.rb +5 -5
  41. data/spec/core/{variable_ref_spec.rb → log_var_ref_spec.rb} +4 -4
  42. data/spec/core/{variable_spec.rb → log_var_spec.rb} +4 -4
  43. data/spec/core/vocabulary_spec.rb +12 -11
  44. data/spec/glue/dsl_chap1_spec.rb +0 -45
  45. data/spec/glue/dsl_chap2_spec.rb +115 -7
  46. data/spec/glue/fresh_env_factory_spec.rb +11 -9
  47. data/spec/glue/fresh_env_spec.rb +3 -3
  48. data/spec/glue/run_star_expression_spec.rb +13 -11
  49. data/spec/support/factory_atomic.rb +22 -0
  50. data/spec/support/factory_methods.rb +11 -26
  51. metadata +28 -23
  52. data/lib/mini_kraken/core/cons_cell.rb +0 -82
  53. data/lib/mini_kraken/core/k_boolean.rb +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ae3703cbb0a7a854af490478227c915d7d9af72715be80cbd4e3c95cb7b2c3f
4
- data.tar.gz: 27e04d428e1015f176fbafba7c047a0b12c28e9150161a7a5702190c8dd2e2e9
3
+ metadata.gz: 96c07c3751d0c46963e19f8e25dc429a77dbd630c22b32018914ccf36f471689
4
+ data.tar.gz: 26efce2c03a89fd4129c756ce64daa80783cc9bfcebc0358b493076f33ef9434
5
5
  SHA512:
6
- metadata.gz: bd9cd60953f88610ce3a5009e0cad4099e1896643ad01f1c6616a4c20fb4495a71f5c5ed11efe98873362055bda2ec9d0ba4a1ce972844588cacab528ac5333d
7
- data.tar.gz: 1c9d27a9fb7fa014f9789a159aff5309bd9c03ce810e994951373ee550698518683359815c4db43b9d95b00ba889670b824af18c1019af5442f2ca3b42c795ba
6
+ metadata.gz: 1df0d6a9397a804e35700aaedb1fcc1ae7fb91a4b3b237ffe138be5cc63b51113d1af5a57f5cd216a41217993dce5dc5186d0690ab992d2eab229995321e7dfb
7
+ data.tar.gz: d5af2983495e31e77e8947bffeca99a9b88b916208cb511a88e3f0e397ec2f105cd60d333d2663b78c4e943edad9e4c76c8aa7eabd07a94a733e9821d63de305
@@ -1,3 +1,12 @@
1
+ ## [0.2.04] - 2020-09-02
2
+ - Intermediate version before vast code rework.
3
+
4
+ ### CHANGED
5
+ - Classes `KBoolean`, `KSymbol`, `KInteger` moved to namespace `Atomic`
6
+ - Classes `ConsCell`, `ConsCellVisitor` moved to namespace `Composite`
7
+ - Class `Variable` renamed to `LogVar`
8
+ - Class `VariableRef` renamed to `LogVarRef`
9
+
1
10
  ## [0.2.03] - 2020-09-02
2
11
  - The DSL (Domain Specific Language) supports the `caro` relation & passes frames up to 2-8 from Chapter 2.
3
12
 
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'k_boolean'
4
+ require_relative 'k_symbol'
@@ -1,16 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'term'
4
- require_relative 'freshness'
3
+ require_relative '../core/term'
4
+ require_relative '../core/freshness'
5
5
 
6
6
  module MiniKraken
7
- module Core
8
- # An atomic term is an elementary Minikraken term that cannot be
9
- # decomposed into simpler MiniKraken data value(s).
10
- class AtomicTerm < Term
11
- # @return [Object] Internal representation of a MiniKraken data value
7
+ # This module packages the atomic term classes, that is,
8
+ # the basic MiniKraken datatypes.
9
+ module Atomic
10
+ # An atomic term is an elementary MiniKraken term, a data value
11
+ # that cannot be decomposed into simpler MiniKraken term(s).
12
+ # Typically, an atomic term encapsulates a Ruby primitive data object.
13
+ # MiniKraken treats atomic terms as immutable objects.
14
+ class AtomicTerm < Core::Term
15
+ # @return [Object] Internal representation of a MiniKraken data value.
12
16
  attr_reader :value
13
17
 
18
+ # Initialize an atomic term with the given data object.
14
19
  # @param aValue [Object] Ruby representation of MiniKraken data value
15
20
  def initialize(aValue)
16
21
  super()
@@ -23,7 +28,7 @@ module MiniKraken
23
28
  # @param _env [Vocabulary]
24
29
  # @return [Freshness]
25
30
  def freshness(_env)
26
- Freshness.new(:ground, self)
31
+ Core::Freshness.new(:ground, self)
27
32
  end
28
33
 
29
34
  # An atomic term is a ground term: by definition it doesn't contain
@@ -35,19 +40,27 @@ module MiniKraken
35
40
  end
36
41
 
37
42
  # An atomic term is a ground term: by definition it doesn't contain
38
- # any fresh variable.
43
+ # any (fresh) variable.
39
44
  # @param _env [Vocabulary]
40
45
  # @return [TrueClass]
41
46
  def ground?(_env)
42
47
  true
43
48
  end
44
49
 
50
+ # Return a String representation of the atomic term
51
+ # @return [String]
52
+ def to_s
53
+ value.to_s
54
+ end
55
+
56
+ # Treat this object as a data value.
45
57
  # @return [AtomicTerm]
46
58
  def quote(_env)
47
59
  self
48
60
  end
49
61
 
50
62
  # Data equality testing
63
+ # @param other [AtomicTerm, #value]
51
64
  # @return [Boolean]
52
65
  def ==(other)
53
66
  if other.respond_to?(:value)
@@ -58,6 +71,7 @@ module MiniKraken
58
71
  end
59
72
 
60
73
  # Type and data equality testing
74
+ # @param other [AtomicTerm]
61
75
  # @return [Boolean]
62
76
  def eql?(other)
63
77
  (self.class == other.class) && value.eql?(other.value)
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'atomic_term'
4
+
5
+ module MiniKraken
6
+ module Atomic
7
+ # A specialized atomic term that implements a boolean (true/false) value
8
+ # in MiniKraken.
9
+ class KBoolean < AtomicTerm
10
+ # Initialize a MiniKraken boolean with a given data value.
11
+ # @example
12
+ # # Initialize with a Ruby boolean
13
+ # truthy = KBoolean.new(true)
14
+ # falsey = KBoolean.new(false)
15
+ # # Initialize with a String inspired from canonical miniKanren
16
+ # truthy = KBoolean.new('#t') # In Scheme #t means true
17
+ # falsey = KBoolean.new('#f') # In Scheme #f means false
18
+ # # Initialize with a Symbol inspired from canonical miniKanren
19
+ # truthy = KBoolean.new(:"#t") # In Scheme #t means true
20
+ # falsey = KBoolean.new(:"#f") # In Scheme #f means false
21
+ # @param aValue [Boolean, String, Symbol] Ruby representation of boolean value.
22
+ def initialize(aValue)
23
+ super(validated_value(aValue))
24
+ end
25
+
26
+ private
27
+
28
+ def validated_value(aValue)
29
+ case aValue
30
+ when true, false
31
+ aValue
32
+ when :"#t", '#t'
33
+ true
34
+ when :"#f", '#f'
35
+ false
36
+ else
37
+ raise StandardError, "Invalid boolean literal '#{aValue}'"
38
+ end
39
+ end
40
+ end # class
41
+ end # module
42
+ end # module
@@ -3,9 +3,10 @@
3
3
  require_relative 'atomic_term'
4
4
 
5
5
  module MiniKraken
6
- module Core
6
+ module Atomic
7
7
  # A specialized atomic term that represents an integer value.
8
8
  # in MiniKraken
9
+ # @note As MiniKraken doesn't support integer values yet, this class is WIP.
9
10
  class KInteger < AtomicTerm
10
11
  # @param aValue [Integer] Ruby representation of integer value
11
12
  def initialize(aValue)
@@ -3,10 +3,11 @@
3
3
  require_relative 'atomic_term'
4
4
 
5
5
  module MiniKraken
6
- module Core
7
- # A specialized atomic term that represents a symbolic value.
8
- # in MiniKraken
6
+ module Atomic
7
+ # A specialized atomic term that represents a symbolic value
8
+ # in MiniKraken.
9
9
  class KSymbol < AtomicTerm
10
+ # Initialize a MiniKraken symbol with a given Ruby Symbol value.
10
11
  # @param aValue [Symbol] Ruby representation of symbol value
11
12
  def initialize(aValue)
12
13
  super(aValue)
@@ -19,6 +20,7 @@ module MiniKraken
19
20
  end
20
21
 
21
22
  # Returns a string representing the MiniKraken symbol.
23
+ # @return [String]
22
24
  def to_s
23
25
  ":#{id2name}"
24
26
  end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'term'
4
- require_relative 'freshness'
3
+ require_relative '../core/term'
4
+ require_relative '../core/freshness'
5
5
 
6
6
  module MiniKraken
7
- module Core
7
+ # This module packages the composite term classes.
8
+ # These hold one or more MiniKanren objects.
9
+ module Composite
8
10
  # An composite term is an Minikraken term that can be
9
11
  # decomposed into simpler MiniKraken data value(s).
10
- class CompositeTerm < Term
12
+ class CompositeTerm < Core::Term
11
13
  # Abstract method (to override). Return the child terms.
12
14
  # @return [Array<Term>]
13
15
  def children
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'composite_term'
4
+
5
+ module MiniKraken
6
+ module Composite
7
+ # In Lisp dialects, a cons cell (or a pair) is a data structure with two
8
+ # fields called the car and cdr fields (for historical reasons).
9
+ # Cons cells are the key ingredient for building lists in Lisp.
10
+ # A cons cell can be depicted as a box with two parts, car and cdr each
11
+ # containing a reference to another object.
12
+ # +-----------+
13
+ # | car | cdr |
14
+ # +--|-----|--+
15
+ # | |
16
+ # V V
17
+ # obj1 obj2
18
+ #
19
+ # The list (1 2 3) can be constructed as follows:
20
+ # +-----------+
21
+ # | car | cdr |
22
+ # +--|-----|--+
23
+ # | |
24
+ # V V
25
+ # 1 +-----------+
26
+ # | car | cdr |
27
+ # +--|-----|--+
28
+ # | |
29
+ # V V
30
+ # 2 +-----------+
31
+ # | car | cdr |
32
+ # +--|-----|--+
33
+ # | |
34
+ # V V
35
+ # 3 nil
36
+ class ConsCell < CompositeTerm
37
+ # The first slot in a ConsCell
38
+ # @return [Term]
39
+ attr_reader :car
40
+
41
+ # The second slot in a ConsCell
42
+ # @return [Term]
43
+ attr_reader :cdr
44
+
45
+ # Construct a new conscell whose car and cdr are obj1 and obj2.
46
+ # @param obj1 [Term, NilClass]
47
+ # @param obj2 [Term, NilClass]
48
+ def initialize(obj1, obj2 = nil)
49
+ super()
50
+ @car = obj1
51
+ if obj2.kind_of?(ConsCell) && obj2.null?
52
+ @cdr = nil
53
+ else
54
+ @cdr = obj2
55
+ end
56
+ end
57
+
58
+ def children
59
+ [car, cdr]
60
+ end
61
+
62
+ # Return true if it is an empty list, otherwise false.
63
+ # A list is empty, when both car and cdr fields are nil.
64
+ # @return [Boolean]
65
+ def null?
66
+ car.nil? && cdr.nil?
67
+ end
68
+
69
+ # Return true if car and cdr fields have the same values as the other
70
+ # ConsCell.
71
+ # @param other [ConsCell]
72
+ # @return [Boolean]
73
+ def ==(other)
74
+ return false unless other.respond_to?(:car)
75
+
76
+ (car == other.car) && (cdr == other.cdr)
77
+ end
78
+
79
+ # Test for type and data value equality.
80
+ # @param other [ConsCell]
81
+ # @return [Boolean]
82
+ def eql?(other)
83
+ (self.class == other.class) && car.eql?(other.car) && cdr.eql?(other.cdr)
84
+ end
85
+
86
+ # Return a data object that is a copy of the ConsCell
87
+ # @param anEnv [Core::Environment]
88
+ # @return [ConsCell]
89
+ def quote(anEnv)
90
+ return self if null?
91
+
92
+ new_car = car.nil? ? nil : car.quote(anEnv)
93
+ new_cdr = cdr.nil? ? nil : cdr.quote(anEnv)
94
+ ConsCell.new(new_car, new_cdr)
95
+ end
96
+
97
+ # Use the list notation from Lisp as a text representation.
98
+ # @return [String]
99
+ def to_s
100
+ return '()' if null?
101
+
102
+ "(#{pair_to_s})"
103
+ end
104
+
105
+ # Change the cdr of ConsCell to 'another'.
106
+ # Analogue of set-cdr! procedure in Scheme.
107
+ # @param another [Term]
108
+ def set_cdr!(another)
109
+ @cdr = another
110
+ end
111
+
112
+ protected
113
+
114
+ def pair_to_s
115
+ result = +car.to_s
116
+ if cdr
117
+ result << ' '
118
+ if cdr.kind_of?(ConsCell)
119
+ result << cdr.pair_to_s
120
+ else
121
+ result << ". #{cdr}"
122
+ end
123
+ end
124
+
125
+ result
126
+ end
127
+ end # class
128
+
129
+ # Constant representing the null (empty) list.
130
+ NullList = ConsCell.new(nil, nil).freeze
131
+ end # module
132
+ end # module
@@ -4,7 +4,7 @@ require 'set'
4
4
  require_relative 'cons_cell'
5
5
 
6
6
  module MiniKraken
7
- module Core
7
+ module Composite
8
8
  # Factory class.
9
9
  # Purpose: to create an enumerator specialized in the visit of cons cells.
10
10
  class ConsCellVisitor
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'set'
4
- require_relative 'atomic_term'
5
- require_relative 'cons_cell'
4
+ require_relative '../atomic/atomic_term'
5
+ require_relative '../composite/cons_cell'
6
6
 
7
7
  module MiniKraken
8
8
  module Core
@@ -26,7 +26,7 @@ module MiniKraken
26
26
  # Treat easy cases first...
27
27
  return nil if assocs.empty?
28
28
 
29
- assoc_atomic = assocs.find { |assc| assc.value.kind_of?(AtomicTerm) }
29
+ assoc_atomic = assocs.find { |assc| assc.value.kind_of?(Atomic::AtomicTerm) }
30
30
  return assoc_atomic.value if assoc_atomic
31
31
 
32
32
  result = nil
@@ -45,17 +45,17 @@ module MiniKraken
45
45
  end
46
46
 
47
47
  def walk_value(aTerm, anEnv)
48
- return aTerm if aTerm.kind_of?(AtomicTerm) || aTerm.kind_of?(AnyValue)
48
+ return aTerm if aTerm.kind_of?(Atomic::AtomicTerm) || aTerm.kind_of?(AnyValue)
49
49
 
50
50
  result = nil
51
51
 
52
- if aTerm.kind_of?(CompositeTerm)
52
+ if aTerm.kind_of?(Composite::CompositeTerm)
53
53
  children = aTerm.children.compact
54
54
  walk_results = children.map do |child|
55
55
  walk_value(child, anEnv)
56
56
  end
57
57
  result = aTerm unless walk_results.any?(&:nil?)
58
- else # VariableRef or Variable
58
+ else # LogVarRef or Variable
59
59
  name = aTerm.respond_to?(:name) ? aTerm.name : aTerm.var_name
60
60
  result = find_ground(name, anEnv)
61
61
  end
@@ -74,9 +74,9 @@ module MiniKraken
74
74
  # require 'debug'
75
75
  result = nil
76
76
 
77
- if aTerm.kind_of?(AtomicTerm)
77
+ if aTerm.kind_of?(Atomic::AtomicTerm)
78
78
  result = Freshness.new(:ground, aTerm)
79
- elsif aTerm.kind_of?(CompositeTerm)
79
+ elsif aTerm.kind_of?(Composite::CompositeTerm)
80
80
  children = aTerm.children.compact
81
81
  walk_results = children.map { |chd| determine_freshness(chd, anEnv) }
82
82
 
@@ -89,7 +89,7 @@ module MiniKraken
89
89
  degree = :bound
90
90
  end
91
91
  result = Freshness.new(degree, aTerm)
92
- else # VariableRef or Variable
92
+ else # LogVarRef or Variable
93
93
  name = aTerm.respond_to?(:name) ? aTerm.name : aTerm.var_name
94
94
  assocs = anEnv[name]
95
95
  if assocs.empty?
@@ -103,7 +103,7 @@ module MiniKraken
103
103
  end
104
104
 
105
105
  def freshness_associated(assocs, anEnv)
106
- assoc_atomic = assocs.find { |assc| assc.value.kind_of?(AtomicTerm) }
106
+ assoc_atomic = assocs.find { |assc| assc.value.kind_of?(Atomic::AtomicTerm) }
107
107
  return Freshness.new(:ground, assoc_atomic.value) if assoc_atomic
108
108
 
109
109
  raw_results = assocs.map do |assc|
@@ -133,11 +133,11 @@ module MiniKraken
133
133
  # require 'debug'
134
134
  result = nil
135
135
 
136
- if aTerm.kind_of?(AtomicTerm)
136
+ if aTerm.kind_of?(Atomic::AtomicTerm)
137
137
  result = aTerm.quote(anEnv)
138
- elsif aTerm.kind_of?(ConsCell)
138
+ elsif aTerm.kind_of?(Composite::ConsCell)
139
139
  result = aTerm.quote(anEnv)
140
- else # VariableRef or Variable
140
+ else # LogVarRef or Variable
141
141
  name = aTerm.respond_to?(:name) ? aTerm.name : aTerm.var_name
142
142
  assocs = anEnv[name]
143
143
  if assocs.empty?
@@ -151,7 +151,7 @@ module MiniKraken
151
151
  end
152
152
 
153
153
  def quote_associated(assocs, anEnv)
154
- assoc_atomic = assocs.find { |assc| assc.value.kind_of?(AtomicTerm) }
154
+ assoc_atomic = assocs.find { |assc| assc.value.kind_of?(Atomic::AtomicTerm) }
155
155
  return assoc_atomic.value if assoc_atomic
156
156
 
157
157
  raw_results = assocs.map do |assc|
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'relation'
4
- require_relative 'composite_term'
4
+ require_relative '../composite/composite_term'
5
5
 
6
6
  module MiniKraken
7
7
  module Core
@@ -25,26 +25,26 @@ module MiniKraken
25
25
  # |arg1 | arg2 | arg2.ground? || Commute |
26
26
  # | isa? Atomic | isa? Atomic | dont_care || Yes |
27
27
  # | isa? Atomic | isa? CompositeTerm | dont_care || Yes |
28
- # | isa? Atomic | isa? VariableRef | dont_care || Yes |
28
+ # | isa? Atomic | isa? LogVarRef | dont_care || Yes |
29
29
  # | isa? CompositeTerm | isa? Atomic | true || No |
30
30
  # | isa? CompositeTerm | isa? CompositeTerm | false || Yes |
31
31
  # | isa? CompositeTerm | isa? CompositeTerm | true || No |
32
- # | isa? CompositeTerm | isa? VariableRef | dont_care || Yes |
33
- # | isa? VariableRef | isa? Atomic | dont_care || No |
34
- # | isa? VariableRef | isa? CompositeTerm | dont_care || No |
35
- # | isa? VariableRef | isa? VariableRef | false || Yes |
36
- # | isa? VariableRef | isa? VariableRef | true || No |
32
+ # | isa? CompositeTerm | isa? LogVarRef | dont_care || Yes |
33
+ # | isa? LogVarRef | isa? Atomic | dont_care || No |
34
+ # | isa? LogVarRef | isa? CompositeTerm | dont_care || No |
35
+ # | isa? LogVarRef | isa? LogVarRef | false || Yes |
36
+ # | isa? LogVarRef | isa? LogVarRef | true || No |
37
37
  def commute_cond(arg1, arg2, env)
38
38
  commuting = true
39
- arg2_is_var_ref = arg2.kind_of?(VariableRef)
39
+ arg2_is_var_ref = arg2.kind_of?(LogVarRef)
40
40
 
41
- if arg1.kind_of?(CompositeTerm)
41
+ if arg1.kind_of?(Composite::CompositeTerm)
42
42
  if arg2_is_var_ref
43
43
  commuting = true
44
44
  else
45
45
  commuting = !arg2.ground?(env)
46
46
  end
47
- elsif arg1.kind_of?(VariableRef)
47
+ elsif arg1.kind_of?(LogVarRef)
48
48
  if arg2_is_var_ref
49
49
  commuting = !arg2.ground?(env)
50
50
  else