mini_kraken 0.2.04 → 0.3.00

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