mini_kraken 0.2.04 → 0.3.00

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +16 -16
  4. data/lib/mini_kraken/atomic/all_atomic.rb +1 -0
  5. data/lib/mini_kraken/atomic/atomic_term.rb +32 -17
  6. data/lib/mini_kraken/atomic/k_integer.rb +0 -4
  7. data/lib/mini_kraken/atomic/k_string.rb +17 -0
  8. data/lib/mini_kraken/atomic/k_symbol.rb +0 -6
  9. data/lib/mini_kraken/composite/all_composite.rb +4 -0
  10. data/lib/mini_kraken/composite/composite_term.rb +2 -18
  11. data/lib/mini_kraken/composite/cons_cell.rb +178 -11
  12. data/lib/mini_kraken/composite/cons_cell_visitor.rb +12 -64
  13. data/lib/mini_kraken/composite/list.rb +32 -0
  14. data/lib/mini_kraken/core/all_core.rb +8 -0
  15. data/lib/mini_kraken/core/any_value.rb +31 -7
  16. data/lib/mini_kraken/core/arity.rb +69 -0
  17. data/lib/mini_kraken/core/association.rb +29 -4
  18. data/lib/mini_kraken/core/association_copy.rb +50 -0
  19. data/lib/mini_kraken/core/base_term.rb +13 -0
  20. data/lib/mini_kraken/core/blackboard.rb +315 -0
  21. data/lib/mini_kraken/core/bookmark.rb +46 -0
  22. data/lib/mini_kraken/core/context.rb +624 -0
  23. data/lib/mini_kraken/core/duck_fiber.rb +21 -19
  24. data/lib/mini_kraken/core/entry.rb +40 -0
  25. data/lib/mini_kraken/core/fail.rb +20 -18
  26. data/lib/mini_kraken/core/fusion.rb +29 -0
  27. data/lib/mini_kraken/core/goal.rb +20 -29
  28. data/lib/mini_kraken/core/log_var.rb +4 -30
  29. data/lib/mini_kraken/core/log_var_ref.rb +72 -48
  30. data/lib/mini_kraken/core/nullary_relation.rb +2 -9
  31. data/lib/mini_kraken/core/parametrized_term.rb +61 -0
  32. data/lib/mini_kraken/core/relation.rb +14 -28
  33. data/lib/mini_kraken/core/scope.rb +67 -0
  34. data/lib/mini_kraken/core/solver_adapter.rb +58 -0
  35. data/lib/mini_kraken/core/specification.rb +48 -0
  36. data/lib/mini_kraken/core/succeed.rb +21 -17
  37. data/lib/mini_kraken/core/symbol_table.rb +137 -0
  38. data/lib/mini_kraken/core/term.rb +15 -4
  39. data/lib/mini_kraken/glue/dsl.rb +35 -69
  40. data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
  41. data/lib/mini_kraken/rela/all_rela.rb +8 -0
  42. data/lib/mini_kraken/rela/binary_relation.rb +30 -0
  43. data/lib/mini_kraken/rela/conde.rb +146 -0
  44. data/lib/mini_kraken/rela/conj2.rb +65 -0
  45. data/lib/mini_kraken/rela/def_relation.rb +64 -0
  46. data/lib/mini_kraken/rela/disj2.rb +70 -0
  47. data/lib/mini_kraken/rela/fresh.rb +98 -0
  48. data/lib/mini_kraken/{core → rela}/goal_relation.rb +6 -8
  49. data/lib/mini_kraken/rela/unify.rb +258 -0
  50. data/lib/mini_kraken/version.rb +1 -1
  51. data/spec/atomic/atomic_term_spec.rb +23 -20
  52. data/spec/atomic/k_symbol_spec.rb +0 -5
  53. data/spec/composite/cons_cell_spec.rb +116 -0
  54. data/spec/composite/cons_cell_visitor_spec.rb +16 -3
  55. data/spec/composite/list_spec.rb +50 -0
  56. data/spec/core/any_value_spec.rb +52 -0
  57. data/spec/core/arity_spec.rb +91 -0
  58. data/spec/core/association_copy_spec.rb +69 -0
  59. data/spec/core/association_spec.rb +25 -0
  60. data/spec/core/blackboard_spec.rb +287 -0
  61. data/spec/core/bookmark_spec.rb +40 -0
  62. data/spec/core/context_spec.rb +221 -0
  63. data/spec/core/core_spec.rb +40 -0
  64. data/spec/core/duck_fiber_spec.rb +22 -46
  65. data/spec/core/fail_spec.rb +5 -6
  66. data/spec/core/goal_spec.rb +20 -11
  67. data/spec/core/log_var_ref_spec.rb +80 -5
  68. data/spec/core/log_var_spec.rb +35 -6
  69. data/spec/core/nullary_relation_spec.rb +33 -0
  70. data/spec/core/parametrized_tem_spec.rb +39 -0
  71. data/spec/core/relation_spec.rb +33 -0
  72. data/spec/core/scope_spec.rb +73 -0
  73. data/spec/core/solver_adapter_spec.rb +70 -0
  74. data/spec/core/specification_spec.rb +43 -0
  75. data/spec/core/succeed_spec.rb +5 -5
  76. data/spec/core/symbol_table_spec.rb +142 -0
  77. data/spec/glue/dsl_chap1_spec.rb +88 -99
  78. data/spec/glue/dsl_chap2_spec.rb +59 -41
  79. data/spec/glue/run_star_expression_spec.rb +69 -896
  80. data/spec/{core → rela}/conde_spec.rb +50 -46
  81. data/spec/rela/conj2_spec.rb +123 -0
  82. data/spec/rela/def_relation_spec.rb +119 -0
  83. data/spec/rela/disj2_spec.rb +117 -0
  84. data/spec/rela/fresh_spec.rb +147 -0
  85. data/spec/rela/unify_spec.rb +369 -0
  86. data/spec/support/factory_atomic.rb +7 -0
  87. data/spec/support/factory_composite.rb +21 -0
  88. metadata +71 -48
  89. data/lib/mini_kraken/core/association_walker.rb +0 -183
  90. data/lib/mini_kraken/core/base_arg.rb +0 -10
  91. data/lib/mini_kraken/core/binary_relation.rb +0 -63
  92. data/lib/mini_kraken/core/composite_goal.rb +0 -46
  93. data/lib/mini_kraken/core/conde.rb +0 -143
  94. data/lib/mini_kraken/core/conj2.rb +0 -79
  95. data/lib/mini_kraken/core/def_relation.rb +0 -53
  96. data/lib/mini_kraken/core/designation.rb +0 -55
  97. data/lib/mini_kraken/core/disj2.rb +0 -72
  98. data/lib/mini_kraken/core/environment.rb +0 -73
  99. data/lib/mini_kraken/core/equals.rb +0 -191
  100. data/lib/mini_kraken/core/formal_arg.rb +0 -22
  101. data/lib/mini_kraken/core/formal_ref.rb +0 -25
  102. data/lib/mini_kraken/core/freshness.rb +0 -45
  103. data/lib/mini_kraken/core/goal_arg.rb +0 -12
  104. data/lib/mini_kraken/core/goal_template.rb +0 -102
  105. data/lib/mini_kraken/core/outcome.rb +0 -63
  106. data/lib/mini_kraken/core/tap.rb +0 -46
  107. data/lib/mini_kraken/core/vocabulary.rb +0 -446
  108. data/lib/mini_kraken/glue/fresh_env.rb +0 -108
  109. data/lib/mini_kraken/glue/fresh_env_factory.rb +0 -83
  110. data/spec/core/association_walker_spec.rb +0 -194
  111. data/spec/core/conj2_spec.rb +0 -116
  112. data/spec/core/def_relation_spec.rb +0 -99
  113. data/spec/core/disj2_spec.rb +0 -100
  114. data/spec/core/environment_spec.rb +0 -144
  115. data/spec/core/equals_spec.rb +0 -319
  116. data/spec/core/goal_template_spec.rb +0 -74
  117. data/spec/core/outcome_spec.rb +0 -56
  118. data/spec/core/vocabulary_spec.rb +0 -220
  119. data/spec/glue/fresh_env_factory_spec.rb +0 -99
  120. data/spec/glue/fresh_env_spec.rb +0 -62
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96c07c3751d0c46963e19f8e25dc429a77dbd630c22b32018914ccf36f471689
4
- data.tar.gz: 26efce2c03a89fd4129c756ce64daa80783cc9bfcebc0358b493076f33ef9434
3
+ metadata.gz: ceccf4c9ffeb71fc8759bbf1ea1db003d83e9e150df95793de7e729716c9538d
4
+ data.tar.gz: bab9d4b22a86eec0fcd95955a799a40bfd51c032d05a56b5c2569e0e833b4d2d
5
5
  SHA512:
6
- metadata.gz: 1df0d6a9397a804e35700aaedb1fcc1ae7fb91a4b3b237ffe138be5cc63b51113d1af5a57f5cd216a41217993dce5dc5186d0690ab992d2eab229995321e7dfb
7
- data.tar.gz: d5af2983495e31e77e8947bffeca99a9b88b916208cb511a88e3f0e397ec2f105cd60d333d2663b78c4e943edad9e4c76c8aa7eabd07a94a733e9821d63de305
6
+ metadata.gz: 5b8dac966209d3209419b2721c67ecd9ec1d1985b0a457877def4e9eda3a305b6952d996e1791436db33f863a046553df1567ef142fc7fe6d5d8092b7d0e2ecd
7
+ data.tar.gz: 5c99a985287a32d2e6a2c6dbbcabeaea07203e533372183817660eb2259edda6465b4993997b9dcb8dd0db59e0b69772f31409f17a587a3d55381d7f06151a74
@@ -1,3 +1,10 @@
1
+ ## [0.3.00] - 2020-12-16
2
+ - Version number bump because this is a vast code rewrite
3
+
4
+ ### CHANGED
5
+ - Most classes have been reworked
6
+ - Relation `Equals` has been renamed to `Unify`
7
+
1
8
  ## [0.2.04] - 2020-09-02
2
9
  - Intermediate version before vast code rework.
3
10
 
data/README.md CHANGED
@@ -4,10 +4,9 @@
4
4
  [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/famished-tiger/mini_kraken/blob/master/LICENSE.txt)
5
5
 
6
6
  ### What is __mini_kraken__ ?
7
- A library containing an implementation of the [miniKanren](http://minikanren.org/)
8
- relational programming language in Ruby.
9
- *miniKanren* is a small language for relational (logic) programming.
10
- Based on the reference implementation, in Scheme from the "The Reasoned Schemer" book.
7
+ A library containing an implementation in Ruby of the [miniKanren](http://minikanren.org/)
8
+ language.
9
+ *miniKanren* is a small language for relational (logic) programming as defined in the "The Reasoned Schemer" book.
11
10
  Daniel P. Friedman, William E. Byrd, Oleg Kiselyov, and Jason Hemann: "The Reasoned Schemer", Second Edition,
12
11
  ISBN: 9780262535519, (2018), MIT Press.
13
12
 
@@ -15,6 +14,7 @@ ISBN: 9780262535519, (2018), MIT Press.
15
14
  - Pure Ruby implementation, not a port from another language
16
15
  - Object-Oriented design
17
16
  - No runtime dependencies
17
+ - Test suite patterned on the examples from the reference book.
18
18
 
19
19
  ### miniKanren Features
20
20
  - [X] ==
@@ -24,14 +24,14 @@ ISBN: 9780262535519, (2018), MIT Press.
24
24
  - [X] conj2
25
25
  - [X] disj2
26
26
  - [X] defrel
27
- - [X] caro
27
+ - [X] caro
28
+ - [X] cdro
28
29
 
29
30
  ### TODO
30
31
 
31
32
  - [ ] Occurs check
32
33
 
33
- List-centric relations from Chapter 2
34
- - [ ] cdro
34
+ List-centric relations from Chapter 2
35
35
  - [ ] conso
36
36
  - [ ] nullo
37
37
  - [ ] pairo
@@ -65,7 +65,7 @@ require 'mini_kraken' # Load MiniKraken library
65
65
 
66
66
  extend(MiniKraken::Glue::DSL) # Add DSL method to self (object in context)
67
67
 
68
- result = run_star('q', equals(q, :pea))
68
+ result = run_star('q', unify(q, :pea))
69
69
  puts result # => (:pea)
70
70
  ```
71
71
 
@@ -79,11 +79,11 @@ and satisfying one or more goals to the `run_star method.
79
79
  In our example, the `run_star` method instructs `MiniKraken` to find all solutions,
80
80
  knowing that each successful solution:
81
81
  - binds a value to the provided variable `q` and
82
- - meets the goal `equals(q, :pea)`.
82
+ - meets the goal `unify(q, :pea)`.
83
83
 
84
- The goal `equals(q, :pea)` succeeds because the logical variable `q` is _fresh_ (that is,
84
+ The goal `unify(q, :pea)` succeeds because the logical variable `q` is _fresh_ (that is,
85
85
  not yet bound to a value) and will be bound to the symbol `:pea` as a side effect
86
- of the goal `equals`.
86
+ of the goal `unify`.
87
87
 
88
88
  So the above program succeeds and the only found solution is obtained by binding
89
89
  the variable `q` to the value :pea. Hence the result of the `puts` method.
@@ -97,11 +97,11 @@ So the above program succeeds and the only found solution is obtained by binding
97
97
  extend(MiniKraken::Glue::DSL) # Add DSL method to self (object in context)
98
98
 
99
99
  # Following miniKanren program fails
100
- result = run_star('q', [equals(q, :pea), equals(q, :pod)])
100
+ result = run_star('q', [unify(q, :pea), unify(q, :pod)])
101
101
  puts result # => ()
102
102
  ```
103
103
  In this example, we learn that `run_star` can take multiple goals placed in an array.
104
- The program fails to find a solution since it is not possible to satisfy the two `equals` goals simultaneously.
104
+ The program fails to find a solution since it is not possible to satisfy the two `unify` goals simultaneously.
105
105
  In case of failure, the `run_star` returns an empty list represented as `()` in the output.
106
106
 
107
107
 
@@ -111,7 +111,7 @@ In case of failure, the `run_star` returns an empty list represented as `()` in
111
111
  ```ruby
112
112
  # In this example and following, one assumes that DSL is loaded as shown in Example 1
113
113
 
114
- result = run_star(['x', 'y'], [equals(:hello, x), equals(y, :world)])
114
+ result = run_star(['x', 'y'], [unify(:hello, x), unify(y, :world)])
115
115
  puts result # => ((:hello :world))
116
116
  ```
117
117
 
@@ -122,8 +122,8 @@ This time, `run_star` takes two logical variables -`x` and `y`- and successfully
122
122
  ```ruby
123
123
  result = run_star(['x', 'y'],
124
124
  [
125
- disj2(equals(x, :blue), equals(x, :red)),
126
- disj2(equals(y, :sea), equals(:mountain, y))
125
+ disj2(unify(x, :blue), unify(x, :red)),
126
+ disj2(unify(y, :sea), unify(:mountain, y))
127
127
  ])
128
128
  puts result # => ((:blue :sea) (:blue :mountain) (:red :sea) (:red :mountain))
129
129
  ```
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'k_boolean'
4
+ require_relative 'k_string'
4
5
  require_relative 'k_symbol'
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../core/term'
4
- require_relative '../core/freshness'
4
+ # require_relative '../core/freshness'
5
5
 
6
6
  module MiniKraken
7
7
  # This module packages the atomic term classes, that is,
8
- # the basic MiniKraken datatypes.
8
+ # the basic datatypes that cannot be decomposed
9
+ # in MiniKraken into simpler, smaller data values.
9
10
  module Atomic
10
11
  # An atomic term is an elementary MiniKraken term, a data value
11
12
  # that cannot be decomposed into simpler MiniKraken term(s).
@@ -23,27 +24,25 @@ module MiniKraken
23
24
  @value.freeze
24
25
  end
25
26
 
26
- # An atomic term is by definition a ground term: since it doesn't contain
27
- # any bound variable (in Prolog sense).
28
- # @param _env [Vocabulary]
29
- # @return [Freshness]
30
- def freshness(_env)
31
- Core::Freshness.new(:ground, self)
27
+ # An atomic term, by definition is bound to a definite value.
28
+ # @param _ctx [Context]
29
+ # @return [FalseClass]
30
+ def unbound?(_ctx)
31
+ false
32
32
  end
33
33
 
34
- # An atomic term is a ground term: by definition it doesn't contain
35
- # any fresh variable.
36
- # @param _env [Vocabulary]
34
+ # An atomic term has a definite value, therefore it is not floating.
35
+ # @param _ctx [Context]
37
36
  # @return [FalseClass]
38
- def fresh?(_env)
37
+ def floating?(_ctx)
39
38
  false
40
39
  end
41
40
 
42
- # An atomic term is a ground term: by definition it doesn't contain
43
- # any (fresh) variable.
44
- # @param _env [Vocabulary]
41
+ # An atomic term is a pinned term: by definition, it has a definite
42
+ # value.
43
+ # @param _ctx [Context]
45
44
  # @return [TrueClass]
46
- def ground?(_env)
45
+ def pinned?(_ctx)
47
46
  true
48
47
  end
49
48
 
@@ -55,7 +54,7 @@ module MiniKraken
55
54
 
56
55
  # Treat this object as a data value.
57
56
  # @return [AtomicTerm]
58
- def quote(_env)
57
+ def quote(_ctx)
59
58
  self
60
59
  end
61
60
 
@@ -76,6 +75,22 @@ module MiniKraken
76
75
  def eql?(other)
77
76
  (self.class == other.class) && value.eql?(other.value)
78
77
  end
78
+
79
+ # Return the list of variable (i_names) that this term depends on.
80
+ # For atomic terms, there is no such dependencies.
81
+ # @param _ctx [Core::Context]
82
+ # @return [Set] an empty set
83
+ def dependencies(_ctx)
84
+ Set.new
85
+ end
86
+
87
+ # Make a copy of self with all the variable reference being
88
+ # replaced by the corresponding value in the Hash.
89
+ # @param _substitutions [Hash {String => Term}]
90
+ # @return [Term]
91
+ def dup_cond(_substitutions)
92
+ dup
93
+ end
79
94
  end # class
80
95
  end # module
81
96
  end # module
@@ -8,10 +8,6 @@ module MiniKraken
8
8
  # in MiniKraken
9
9
  # @note As MiniKraken doesn't support integer values yet, this class is WIP.
10
10
  class KInteger < AtomicTerm
11
- # @param aValue [Integer] Ruby representation of integer value
12
- def initialize(aValue)
13
- super(aValue)
14
- end
15
11
  end # class
16
12
  end # module
17
13
  end # module
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'atomic_term'
4
+
5
+ module MiniKraken
6
+ module Atomic
7
+ # A specialized atomic term that represents a string value
8
+ # in MiniKraken.
9
+ class KString < AtomicTerm
10
+ # Returns a string representing the MiniKraken symbol.
11
+ # @return [String]
12
+ def to_s
13
+ value
14
+ end
15
+ end # class
16
+ end # module
17
+ end # module
@@ -7,12 +7,6 @@ module MiniKraken
7
7
  # A specialized atomic term that represents a symbolic value
8
8
  # in MiniKraken.
9
9
  class KSymbol < AtomicTerm
10
- # Initialize a MiniKraken symbol with a given Ruby Symbol value.
11
- # @param aValue [Symbol] Ruby representation of symbol value
12
- def initialize(aValue)
13
- super(aValue)
14
- end
15
-
16
10
  # Returns the name or string corresponding to value.
17
11
  # @return [String]
18
12
  def id2name
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'list'
4
+ require_relative 'cons_cell_visitor'
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../core/term'
4
- require_relative '../core/freshness'
5
4
 
6
5
  module MiniKraken
7
6
  # This module packages the composite term classes.
@@ -16,28 +15,13 @@ module MiniKraken
16
15
  raise NotImplementedError, 'This method must re-defined in subclass(es).'
17
16
  end
18
17
 
19
- # A composite term is fresh when all its members are nil or all non-nil members
20
- # are all fresh
21
- # A composite term is bound when it is not fresh and not ground
22
- # A composite term is a ground term when all its non-nil members are ground.
23
- # @param _env [Vocabulary]
24
- # @return [Freshness]
25
- def freshness(_env)
26
- env.freshness_composite(self)
27
- end
28
-
18
+ =begin
29
19
  # @param env [Environment]
30
20
  # @return [Boolean]
31
21
  def fresh?(env)
32
22
  env.fresh_value?(self)
33
23
  end
34
-
35
- # A composite is ground if all its children are ground
36
- def ground?(anEnv)
37
- children.all? do |child|
38
- child.nil? || child.ground?(anEnv)
39
- end
40
- end
24
+ =end
41
25
  end # class
42
26
  end # module
43
27
  end # module
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+
3
5
  require_relative 'composite_term'
6
+ require_relative 'cons_cell_visitor'
4
7
 
5
8
  module MiniKraken
6
9
  module Composite
7
10
  # 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).
11
+ # fields named car and cdr (for historical reasons).
9
12
  # Cons cells are the key ingredient for building lists in Lisp.
10
13
  # A cons cell can be depicted as a box with two parts, car and cdr each
11
14
  # containing a reference to another object.
@@ -43,6 +46,10 @@ module MiniKraken
43
46
  attr_reader :cdr
44
47
 
45
48
  # Construct a new conscell whose car and cdr are obj1 and obj2.
49
+ # In Scheme, a list is terminated by a null list.
50
+ # In MiniKraken, a list is terminated by a Ruby nil.
51
+ # Therefore, when setting the cdr to the null list, the implementation
52
+ # will silently replace the null list by a nil.
46
53
  # @param obj1 [Term, NilClass]
47
54
  # @param obj2 [Term, NilClass]
48
55
  def initialize(obj1, obj2 = nil)
@@ -55,6 +62,12 @@ module MiniKraken
55
62
  end
56
63
  end
57
64
 
65
+ # Specialized constructor for null list.
66
+ # @return [ConsCell] Null list
67
+ def self.null
68
+ new(nil, nil)
69
+ end
70
+
58
71
  def children
59
72
  [car, cdr]
60
73
  end
@@ -66,7 +79,30 @@ module MiniKraken
66
79
  car.nil? && cdr.nil?
67
80
  end
68
81
 
69
- # Return true if car and cdr fields have the same values as the other
82
+ # Is the receiver an unbound variable?
83
+ # By definition, a composite isn't a variable.
84
+ # @param _ctx [Core::Context]
85
+ # @return [FalseClass]
86
+ def unbound?(_ctx)
87
+ false
88
+ end
89
+
90
+ # Does the composite have a variable that is itself floating?
91
+ # @return [Boolean]
92
+ def floating?(ctx)
93
+ !pinned?(ctx)
94
+ end
95
+
96
+ # Does the composite have a definite value?
97
+ # @return [Boolean]
98
+ def pinned?(ctx)
99
+ @pinned_car ||= car.nil? || car.pinned?(ctx)
100
+ @pinned_cdr ||= cdr.nil? || cdr.pinned?(ctx)
101
+
102
+ @pinned_car && @pinned_cdr
103
+ end
104
+
105
+ # Return true if car and cdr fields have the same values as the other
70
106
  # ConsCell.
71
107
  # @param other [ConsCell]
72
108
  # @return [Boolean]
@@ -75,17 +111,17 @@ module MiniKraken
75
111
 
76
112
  (car == other.car) && (cdr == other.cdr)
77
113
  end
78
-
114
+
79
115
  # Test for type and data value equality.
80
116
  # @param other [ConsCell]
81
- # @return [Boolean]
117
+ # @return [Boolean]
82
118
  def eql?(other)
83
119
  (self.class == other.class) && car.eql?(other.car) && cdr.eql?(other.cdr)
84
120
  end
85
121
 
86
122
  # Return a data object that is a copy of the ConsCell
87
123
  # @param anEnv [Core::Environment]
88
- # @return [ConsCell]
124
+ # @return [ConsCell]
89
125
  def quote(anEnv)
90
126
  return self if null?
91
127
 
@@ -102,11 +138,142 @@ module MiniKraken
102
138
  "(#{pair_to_s})"
103
139
  end
104
140
 
105
- # Change the cdr of ConsCell to 'another'.
141
+ # Return the list of variable (i_names) that this term depends on.
142
+ # For a variable reference, it will return the i_names of its variable
143
+ # @param ctx [Core::Context]
144
+ # @return [Set<String>] A set of i_names
145
+ def dependencies(ctx)
146
+ deps = []
147
+ visitor = ConsCellVisitor.df_visitor(self)
148
+ skip_children = false
149
+ loop do
150
+ side, cell = visitor.resume(skip_children)
151
+ if cell.kind_of?(Core::LogVarRef)
152
+ deps << ctx.lookup(cell.name).i_name
153
+ skip_children = true
154
+ else
155
+ skip_children = false
156
+ end
157
+ break if side == :stop
158
+ end
159
+
160
+ Set.new(deps)
161
+ end
162
+
163
+ # @param ctx [Core::Context]
164
+ # @param theSubstitutions [Hash{String => Association}]
165
+ def expand(ctx, theSubstitutions)
166
+ head = curr_cell = nil
167
+ path = []
168
+
169
+ visitor = ConsCellVisitor.df_visitor(self) # Breadth-first!
170
+ skip_children = false
171
+
172
+ loop do
173
+ side, cell = visitor.resume(skip_children)
174
+ # next if cell == self
175
+ break if side == :stop
176
+
177
+ case cell
178
+ when ConsCell
179
+ new_cell = ConsCell.null
180
+ if curr_cell
181
+ curr_cell.set!(side, new_cell)
182
+ path.push(curr_cell)
183
+ end
184
+ curr_cell = new_cell
185
+ head ||= new_cell
186
+
187
+ when Core::LogVarRef
188
+ # Is this robust?
189
+ if cell.i_name
190
+ i_name = cell.i_name
191
+ else
192
+ i_name = ctx.symbol_table.lookup(cell.name).i_name
193
+ end
194
+ expanded = ctx.expand_value_of(i_name, theSubstitutions)
195
+ curr_cell.set!(side, expanded)
196
+ curr_cell = path.pop if side == :cdr
197
+
198
+ else
199
+ curr_cell.set!(side, cell)
200
+ curr_cell = path.pop if side == :cdr
201
+ end
202
+ end
203
+
204
+ head
205
+ end
206
+
207
+
208
+ # @param ctx [Core::Context]
209
+ # @param theSubstitutions [Hash{String => Association}]
210
+
211
+
212
+ # File 'lib/mini_kraken/atomic/atomic_term.rb', line 91
213
+ # Make a copy of self with all the variable reference being replaced
214
+ # by the corresponding value in the Hash.
215
+ # @param substitutions [Hash {String => Term}]
216
+ # @return [ConsCell]
217
+ def dup_cond(substitutions)
218
+ head = curr_cell = nil
219
+ path = []
220
+
221
+ visitor = ConsCellVisitor.df_visitor(self) # Breadth-first!
222
+ skip_children = false
223
+
224
+ loop do
225
+ side, cell = visitor.resume(skip_children)
226
+ # next if cell == self
227
+ break if side == :stop
228
+
229
+ if cell.kind_of?(ConsCell)
230
+ new_cell = ConsCell.null
231
+ if curr_cell
232
+ curr_cell.set!(side, new_cell)
233
+ path.push(curr_cell)
234
+ end
235
+ curr_cell = new_cell
236
+ head ||= new_cell
237
+
238
+ else
239
+ duplicate = cell.nil? ? nil : cell.dup_cond(substitutions)
240
+ curr_cell.set!(side, duplicate)
241
+ curr_cell = path.pop if side == :cdr
242
+ end
243
+ end
244
+
245
+ head
246
+ end
247
+
248
+ # Set one element of the pair
249
+ # @param member [Symbol]
250
+ # @param element [Term]
251
+ def set!(member, element)
252
+ case member
253
+ when :car
254
+ set_car!(element)
255
+ when :cdr
256
+ @pinned_cdr = nil
257
+ @cdr = element
258
+ else
259
+ raise StandardError, "Undefined cons cell member #{member}"
260
+ end
261
+ end
262
+
263
+ # Change the car of ConsCell to 'element'.
264
+ # Analogue of set-car! procedure in Scheme.
265
+ # @param element [Term]
266
+ def set_car!(element)
267
+ @pinned_car = nil # To force re-evaluation
268
+ @car = element
269
+ end
270
+
271
+ # Change the cdr of ConsCell to 'element'.
106
272
  # Analogue of set-cdr! procedure in Scheme.
107
- # @param another [Term]
108
- def set_cdr!(another)
109
- @cdr = another
273
+ # @param element [Term]
274
+ def set_cdr!(element)
275
+ @pinned_cdr = nil # To force re-evaluation
276
+ @cdr = (element.kind_of?(ConsCell) && element.null?) ? nil : element
110
277
  end
111
278
 
112
279
  protected
@@ -126,7 +293,7 @@ module MiniKraken
126
293
  end
127
294
  end # class
128
295
 
129
- # Constant representing the null (empty) list.
130
- NullList = ConsCell.new(nil, nil).freeze
296
+ # Constant set to a null (empty) list.
297
+ NullList = ConsCell.null.freeze
131
298
  end # module
132
299
  end # module