mini_kraken 0.2.03 → 0.2.04

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