mini_kraken 0.1.02 → 0.1.03

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6ff930902d2d1d8967acaab13146a2f1f884b4dab5cdf5dc23b14e043baac76
4
- data.tar.gz: 702bd83538e9e94edac5e17943af72438d3c91296221c80e6172d1cbbf1df71c
3
+ metadata.gz: ba23145a960593f3910547ace544235fcf3f9c9f5ee45b593e3aa465231e37fe
4
+ data.tar.gz: f2e1e6371cb9873192fece615acc2a6bd9021c5e7f8b1865b4a108ab414d5e8e
5
5
  SHA512:
6
- metadata.gz: 4885be585b956b690762ff66365098d4ed0cd6bd141410863afa1701431f6cc4fc6749fc5a2fff4e2689cbd3f9ac7035f057458de2b364df6a34015a252deaed
7
- data.tar.gz: 6180e86da36cdb2d2d3fd0daf6e53061513064b40df79980f36c73d97418e0016c5985bb03d573a338508a8c2d1745dfa2340c62c261177716d57ec4676e5cba
6
+ metadata.gz: 0d8c08206038cb1b1d0fe339ace8f5ee4bcb9a91afb10447b8bc15624253774fcfd8fd1e1c4b07faa687fc2f5071bd4e46db0bcc2323044f2d36222db9906e80
7
+ data.tar.gz: 9f55752adc900a4cfa9aa82906aab0c513f3cf35918c7a50e170ce16201fad8dc471e2f364efaaa9f8db935f35b18bbb12e4af1ef4a74d297f03f54dfc92eda3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## [0.1.03] - 2020-05-01
2
+ Passes all frames 1:1 up to 1:47 of "Reasoned Schemer" book
3
+
1
4
  ## [0.1.02] - 2020-04-28
2
5
  Major code refactoring. Passes all frames 1:1 up to 1:36 of "Reasoned Schemer" book
3
6
 
@@ -3,8 +3,10 @@ module MiniKraken
3
3
  class AnyValue
4
4
  attr_reader :rank
5
5
 
6
- def initialize(aRank)
7
- @rank = aRank
6
+ # @param aName [String]
7
+ # @param anEnv [Vocabulary]
8
+ def initialize(aName, anEnv, alternate_names = [])
9
+ @rank = anEnv.get_rank(aName, alternate_names)
8
10
  end
9
11
 
10
12
  def ==(other)
@@ -29,14 +29,10 @@ module MiniKraken
29
29
  end
30
30
 
31
31
  def quote(anEnvironment)
32
- # raise StandardError, "class #{anEnvironment}" unless anEnvironment.kind_of?(Vocabulary)
33
- # freshness = anEnvironment.freshness_ref(self)
34
- # raise StandardError, "class #{freshness}" unless freshness.kind_of?(Freshness)
35
- # raise StandardError, "class #{freshness.associated}" if freshness.associated.kind_of?(Freshness)
36
- # freshness.fresh? ? AnyValue.new(0) : freshness.associated.quote(anEnvironment)
32
+ raise StandardError, "class #{anEnvironment}" unless anEnvironment.kind_of?(Vocabulary)
37
33
 
38
34
  val = anEnvironment.quote_ref(self)
39
- val.nil? ? AnyValue.new(0) : val
35
+ val.nil? ? AnyValue.new(name, anEnvironment) : val
40
36
  end
41
37
 
42
38
  private
@@ -21,6 +21,13 @@ module MiniKraken
21
21
  env.fresh?(self)
22
22
  end
23
23
 
24
+ # @param env [Environment]
25
+ # @return [Boolean]
26
+ def bound?(env)
27
+ freshness = env.freshness_ref(self)
28
+ freshness.degree == :bound
29
+ end
30
+
24
31
  # @param env [Environment]
25
32
  # @return [Boolean]
26
33
  def ground?(env)
@@ -46,18 +53,59 @@ module MiniKraken
46
53
  freshness = env.freshness_ref(self)
47
54
  freshness.associated
48
55
  end
49
-
56
+
50
57
  # @param env [Environment]
51
58
  # @return [Freshness]
52
59
  def freshness(env)
53
60
  freshness = env.freshness_ref(self)
54
- end
55
-
61
+ end
62
+
56
63
 
57
64
  # @param env [Environment]
58
65
  def quote(env)
59
66
  val = env.quote_ref(self)
60
- val.nil? ? AnyValue.new(0) : val
67
+ val.nil? ? AnyValue.new(var_name, env, names_fused(env)) : val
68
+ end
69
+
70
+ # param another [VariableRef]
71
+ # @param env [Environment]
72
+ # @return [Boolean]
73
+ def fused_with?(another, env)
74
+ # I should point to 'another'...
75
+ other_name = another.var_name
76
+ to_another = values(env).find do |val|
77
+ val.kind_of?(VariableRef) && val.var_name == other_name
78
+ end
79
+ return false unless to_another
80
+
81
+ # 'another' should point to me
82
+ to_me = another.values(env).find do |val|
83
+ val.kind_of?(VariableRef) && val.var_name == var_name
84
+ end
85
+ !to_me.nil?
86
+ end
87
+
88
+ def names_fused(env)
89
+ to_others = values(env).select do |val|
90
+ val.kind_of?(VariableRef)
91
+ end
92
+ return [] if to_others.empty?
93
+
94
+ # 'others' should point to me
95
+ to_me = to_others.select do |other|
96
+ other.values(env).find do |val|
97
+ val.kind_of?(VariableRef) && val.var_name == var_name
98
+ end
99
+ end
100
+
101
+ to_me.map { |other| other.var_name }
102
+ end
103
+
104
+ # param another [VariableRef]
105
+ # @param env [Environment]
106
+ # @return [Boolean]
107
+ def different_from?(another, env)
108
+ !fused_with?(another, env)
61
109
  end
62
110
 
63
111
  private
@@ -9,10 +9,14 @@ module MiniKraken
9
9
  # @return [Hash] Pairs of the kind {String => Array[Association]}
10
10
  attr_reader :associations
11
11
 
12
+ # @return [Hash] Pairs of the kind {String => Integer}
13
+ attr_reader :rankings
14
+
12
15
  # @param aParent [Environment, NilClass] Parent environment to this one.
13
16
  def init_vocabulary(aParent = nil)
14
17
  @parent = validated_parent(aParent)
15
18
  @associations = {}
19
+ @rankings = {} unless aParent
16
20
  end
17
21
 
18
22
  # Return a Fiber object that can iterate over this vocabulary and
@@ -30,6 +34,47 @@ module MiniKraken
30
34
  end
31
35
  end
32
36
 
37
+ def clear_rankings
38
+ walker = ancestor_walker
39
+ orphan = nil
40
+ loop do
41
+ orphan_temp = walker.resume
42
+ break unless orphan_temp
43
+ orphan = orphan_temp
44
+ end
45
+
46
+ orphan.rankings.clear if orphan.rankings
47
+ end
48
+
49
+ # @param aName [String]
50
+ # @param alternate_names [Array<String>]
51
+ def get_rank(aName, alternate_names = [])
52
+ walker = ancestor_walker
53
+ orphan = nil
54
+ loop do
55
+ orphan_temp = walker.resume
56
+ break unless orphan_temp
57
+ orphan = orphan_temp
58
+ end
59
+
60
+ raise StandardError unless orphan
61
+
62
+ if orphan.rankings.include?(aName)
63
+ orphan.rankings[aName]
64
+ else
65
+ other = alternate_names.find do |a_name|
66
+ orphan.rankings.include?(a_name)
67
+ end
68
+ if other
69
+ get_rank(other)
70
+ else
71
+ rank = orphan.rankings.keys.size
72
+ orphan.rankings[aName] = rank
73
+ rank
74
+ end
75
+ end
76
+ end
77
+
33
78
  # Record an association between a variable with given name and a term.
34
79
  # @param anAssociation [Association]
35
80
  def add_assoc(anAssociation)
@@ -89,13 +134,13 @@ module MiniKraken
89
134
  ground_term = walker.walk_value(val, self)
90
135
  ground_term.nil? ? true : false
91
136
  end
92
-
137
+
93
138
  # A composite term is fresh when all its members are nil or all non-nil members
94
139
  # are all fresh
95
- # A composite term is bound when it is not fresh and not ground
140
+ # A composite term is bound when it is not fresh and not ground
96
141
  # A composite term is a ground term when all its non-nil members are ground.
97
142
  # @param aComposite [CompositeTerm]
98
- # @return [Freshness]
143
+ # @return [Freshness]
99
144
  def freshness_composite(aComposite)
100
145
  walker = AssociationWalker.new
101
146
  walker.freshness_composite(aComposite)
@@ -110,10 +155,10 @@ module MiniKraken
110
155
  end
111
156
 
112
157
  # @param aVariableRef [VariableRef]
113
- # @return [Term, NilClass]
158
+ # @return [Term, NilClass]
114
159
  def quote_ref(aVariableRef)
115
160
  walker = AssociationWalker.new
116
- walker.quote_term(aVariableRef, self)
161
+ walker.quote_term(aVariableRef, self)
117
162
  end
118
163
 
119
164
  # @param aName [String]
@@ -29,6 +29,7 @@ module MiniKraken
29
29
  else
30
30
  if outcome.successful?
31
31
  env.propagate(outcome)
32
+ # require 'debug'
32
33
  result = Core::ConsCell.new(var.quote(outcome))
33
34
  else
34
35
  result = Core::NullList
@@ -1,3 +1,3 @@
1
1
  module MiniKraken
2
- VERSION = '0.1.02'.freeze
2
+ VERSION = '0.1.03'.freeze
3
3
  end
@@ -44,7 +44,7 @@ module MiniKraken
44
44
 
45
45
  let(:pea) { KSymbol.new(:pea) }
46
46
  let(:pod) { KSymbol.new(:pod) }
47
- let(:sample_cons) { ConsCell.new(pea, nil) }
47
+ let(:sample_cons) { ConsCell.new(pea) }
48
48
  let(:a_composite) { ConsCell.new(pod) }
49
49
  let(:env) { Environment.new }
50
50
  let(:var_q) { build_var('q') }
@@ -94,6 +94,8 @@ module MiniKraken
94
94
  end
95
95
 
96
96
  it 'should fail for one right literal and one composite arguments' do
97
+ # Reasoned S2, frame 1:44
98
+ # (== '(pea) 'pea) ;; => #u
97
99
  result = solve_for(sample_cons, pea)
98
100
 
99
101
  expect(result.resultant).to eq(:"#u")
@@ -283,6 +285,19 @@ module MiniKraken
283
285
  expect(ref_x.fresh?(env)).to be_falsey
284
286
  expect(ref_q.fresh?(env)).to be_falsey
285
287
  end
288
+
289
+ it 'should fail for one right literal and one composite arguments' do
290
+ # Reasoned S2, frame 1:46
291
+ # x associated to ('pea 'pod)
292
+ # (== '(,x) x) ;; => #u
293
+ expr1 = cons(pea, cons(pod))
294
+ ref_x.associate(expr1, env)
295
+ expr2 = cons(ref_x)
296
+ result = solve_for(expr2, ref_x)
297
+
298
+ expect(result.resultant).to eq(:"#u")
299
+ expect(result.associations).to be_empty
300
+ end
286
301
  end # context
287
302
  end # describe
288
303
  end # module
@@ -182,6 +182,25 @@ module MiniKraken
182
182
  subject.add_assoc(Association.new('x', pea))
183
183
  expect(subject.fresh?(ref_q)).to be_falsey
184
184
  end
185
+
186
+ it 'should rank names sequentially' do
187
+ 2.times do
188
+ expect(subject.get_rank('a')).to eq(0)
189
+ expect(subject.get_rank('z')).to eq(1)
190
+ expect(subject.get_rank('c')).to eq(2)
191
+ end
192
+ end
193
+
194
+ it 'should clear the rankings' do
195
+ expect(subject.get_rank('a')).to eq(0)
196
+ expect(subject.get_rank('z')).to eq(1)
197
+
198
+ subject.clear_rankings
199
+ expect(grandma.rankings).to be_empty
200
+
201
+ expect(subject.get_rank('z')).to eq(0)
202
+ expect(subject.get_rank('a')).to eq(1)
203
+ end
185
204
  end # context
186
205
  end # describe
187
206
  end # module
@@ -39,6 +39,10 @@ module MiniKraken
39
39
  context 'Provided services:' do
40
40
  let(:ref_q) { Core::VariableRef.new('q') }
41
41
  let(:ref_x) { Core::VariableRef.new('x') }
42
+ let(:ref_y) { Core::VariableRef.new('y') }
43
+ let(:ref_s) { Core::VariableRef.new('s') }
44
+ let(:ref_t) { Core::VariableRef.new('t') }
45
+ let(:ref_u) { Core::VariableRef.new('u') }
42
46
 
43
47
  it "should return a null list with the fail goal" do
44
48
  # Reasoned S2, frame 1:7
@@ -127,6 +131,9 @@ module MiniKraken
127
131
  result = instance.run
128
132
  expect(ref_q.fresh?(instance.env)).to be_falsey
129
133
  expect(ref_x.fresh?(fresh_env)).to be_truthy
134
+
135
+ # Reasoned S2, frame 1:40
136
+ expect(ref_q.different_from?(ref_x, fresh_env)).to be_truthy
130
137
  expect(result.car).to eq(pea)
131
138
  end
132
139
 
@@ -166,6 +173,11 @@ module MiniKraken
166
173
  result = instance.run
167
174
  expect(ref_q.fresh?(instance.env)).to be_truthy
168
175
  expect(ref_x.fresh?(fresh_env)).to be_truthy
176
+
177
+ # q should be fused with x...
178
+ expect(ref_q.fused_with?(ref_x, fresh_env)).to be_truthy
179
+ expect(ref_q.names_fused(fresh_env)).to eq(['x'])
180
+ expect(ref_x.names_fused(fresh_env)).to eq(['q'])
169
181
  expect(result.car).to eq(any_value(0))
170
182
  end
171
183
 
@@ -225,8 +237,8 @@ module MiniKraken
225
237
  end
226
238
 
227
239
  it 'should unify complex equality expressions (II)' do
228
- # # Reasoned S2, frame 1:36
229
- # # (run* q (fresh (x) (== '(((,q)) (,x)) `(((,x)) pod)))) ;; => ('pod)
240
+ # Reasoned S2, frame 1:36
241
+ # (run* q (fresh (x) (== '(((,q)) (,x)) `(((,x)) pod)))) ;; => ('pod)
230
242
  expr1 = cons(cons(cons(ref_q)), ref_x)
231
243
  expr2 = cons(cons(cons(ref_x)), pod)
232
244
  goal = equals_goal(expr1, expr2)
@@ -241,6 +253,104 @@ module MiniKraken
241
253
  expect(ref_x.fresh?(fresh_env)).to be_falsey
242
254
  expect(result.car).to eq(pod)
243
255
  end
256
+
257
+ it 'should unify with repeated fresh variable' do
258
+ # Reasoned S2, frame 1:37
259
+ # (run* q (fresh (x) (== '( ,x ,x) q))) ;; => (_0 _0)
260
+ expr1 = cons(ref_x, cons(ref_x))
261
+ goal = equals_goal(expr1, ref_q)
262
+ fresh_env = FreshEnv.new(['x'], goal)
263
+ instance = RunStarExpression.new('q', fresh_env)
264
+
265
+ result = instance.run
266
+ expect(ref_q.fresh?(instance.env)).to be_truthy # x isn't defined here
267
+ expect(ref_q.fresh?(fresh_env)).to be_truthy
268
+ expect(ref_x.fresh?(fresh_env)).to be_truthy
269
+ expect(result.car).to eq(cons(any_value(0), cons(any_value(0))))
270
+ end
271
+
272
+ it 'should unify multiple times' do
273
+ # Reasoned S2, frame 1:38
274
+ # (run* q (fresh (x) (fresh (y) (== '( ,q ,y) '((,x ,y) ,x))))) ;; => (_0 _0)
275
+ expr1 = cons(ref_q, cons(ref_y))
276
+ expr2 = cons(cons(ref_x, cons(ref_y)), cons(ref_x))
277
+ goal = equals_goal(expr1, expr2)
278
+ fresh_env_y = FreshEnv.new(['y'], goal)
279
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
280
+ instance = RunStarExpression.new('q', fresh_env_x)
281
+
282
+ result = instance.run
283
+ expect(ref_q.fresh?(fresh_env_y)).to be_truthy
284
+ expect(ref_q.bound?(fresh_env_y)).to be_truthy
285
+ expect(ref_x.fresh?(fresh_env_y)).to be_truthy
286
+ expect(ref_x.bound?(fresh_env_y)).to be_truthy
287
+ expect(ref_y.fresh?(fresh_env_y)).to be_truthy
288
+ expect(ref_y.bound?(fresh_env_y)).to be_truthy
289
+
290
+ # y should be fused with x...
291
+ expect(ref_y.fused_with?(ref_x, fresh_env_y)).to be_truthy
292
+ expect(ref_x.names_fused(fresh_env_y)).to eq(['y'])
293
+ expect(ref_y.names_fused(fresh_env_y)).to eq(['x'])
294
+ expect(result.car).to eq(cons(any_value(0), cons(any_value(0))))
295
+ end
296
+
297
+ it 'should support multiple fresh variables' do
298
+ # Reasoned S2, frame 1:41
299
+ # (run* q (fresh (x) (fresh (y) (== '( ,x ,y) q)))) ;; => (_0 _1)
300
+ expr1 = cons(ref_x, cons(ref_y))
301
+ goal = equals_goal(expr1, ref_q)
302
+ fresh_env_y = FreshEnv.new(['y'], goal)
303
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
304
+ instance = RunStarExpression.new('q', fresh_env_x)
305
+
306
+ result = instance.run
307
+ expect(ref_q.fresh?(fresh_env_y)).to be_truthy
308
+ # q should be bound to '(,x ,y)
309
+ expect(ref_q.bound?(fresh_env_y)).to be_truthy
310
+ expect(ref_x.fresh?(fresh_env_y)).to be_truthy
311
+ expect(ref_x.bound?(fresh_env_y)).to be_falsey
312
+ expect(ref_y.fresh?(fresh_env_y)).to be_truthy
313
+ expect(ref_y.bound?(fresh_env_y)).to be_falsey
314
+ expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
315
+ end
316
+
317
+ it 'should work with variable names' do
318
+ # Reasoned S2, frame 1:42
319
+ # (run* s (fresh (t) (fresh (u) (== '( ,t ,u) s)))) ;; => (_0 _1)
320
+ expr1 = cons(ref_t, cons(ref_u))
321
+ goal = equals_goal(expr1, ref_s)
322
+ fresh_env_u = FreshEnv.new(['u'], goal)
323
+ fresh_env_t = FreshEnv.new(['t'], fresh_env_u)
324
+ instance = RunStarExpression.new('s', fresh_env_t)
325
+
326
+ result = instance.run
327
+ expect(ref_s.fresh?(fresh_env_u)).to be_truthy
328
+ # s should be bound to '(,t ,u)
329
+ expect(ref_s.bound?(fresh_env_u)).to be_truthy
330
+ expect(ref_t.fresh?(fresh_env_u)).to be_truthy
331
+ expect(ref_t.bound?(fresh_env_u)).to be_falsey
332
+ expect(ref_u.fresh?(fresh_env_u)).to be_truthy
333
+ expect(ref_u.bound?(fresh_env_u)).to be_falsey
334
+ expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
335
+ end
336
+
337
+ it 'should support repeated variables' do
338
+ # Reasoned S2, frame 1:43
339
+ # (run* q (fresh (x) (fresh (y) (== '( ,x ,y ,x) q)))) ;; => (_0 _1 _0)
340
+ expr1 = cons(ref_x, cons(ref_y, cons(ref_x)))
341
+ goal = equals_goal(expr1, ref_q)
342
+ fresh_env_y = FreshEnv.new(['y'], goal)
343
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
344
+ instance = RunStarExpression.new('q', fresh_env_x)
345
+
346
+ result = instance.run
347
+ expect(ref_q.fresh?(fresh_env_y)).to be_truthy
348
+ # q should be bound to '(,x ,y, ,x)
349
+ expect(ref_q.bound?(fresh_env_y)).to be_truthy
350
+ expect(ref_x.fresh?(fresh_env_y)).to be_truthy
351
+ expect(ref_y.fresh?(fresh_env_y)).to be_truthy
352
+ expect(result.car).to eq(cons(any_value(0), cons(any_value(1), cons(any_value(0)))))
353
+ end
244
354
  end # context
245
355
  end # describe
246
356
  end # module
@@ -11,42 +11,44 @@ module MiniKraken
11
11
  # @param rank [Integer]
12
12
  # @return [Core::AnyValue]
13
13
  def any_value(rank)
14
- Core::AnyValue.new(rank)
14
+ any_val = Core::AnyValue.allocate
15
+ any_val.instance_variable_set(:@rank, rank)
16
+ any_val
15
17
  end
16
18
 
17
19
  # Factory method for constructing a ConsCell
18
20
  # @param obj1 [Term]
19
- # @param obj2 [Term]
20
- # @return [Core::ConsCell]
21
+ # @param obj2 [Term]
22
+ # @return [Core::ConsCell]
21
23
  def cons(obj1, obj2 = nil)
22
24
  Core::ConsCell.new(obj1, obj2)
23
25
  end
24
26
 
25
27
  # Factory method for constructing a goal using the Equals relation.
26
28
  # @param arg1 [Term]
27
- # @param arg2 [Term]
28
- # @return [Core::Goal]
29
+ # @param arg2 [Term]
30
+ # @return [Core::Goal]
29
31
  def equals_goal(arg1, arg2)
30
32
  Core::Goal.new(Core::Equals.instance, [arg1, arg2])
31
33
  end
32
34
 
33
35
  # Factory method for constructing a KSymbol instance
34
36
  # @param aSymbol [Symbol]
35
- # @return [Core::KSymbol]
37
+ # @return [Core::KSymbol]
36
38
  def k_symbol(aSymbol)
37
39
  Core::KSymbol.new(aSymbol)
38
40
  end
39
-
41
+
40
42
  # Factory method for constructing a Variable
41
43
  # @param var_name [String]
42
- # @return [Core::Variable]
44
+ # @return [Core::Variable]
43
45
  def var_ref(var_name)
44
46
  Core::Variable.new(var_name)
45
47
  end
46
-
48
+
47
49
  # Factory method for constructing a VariableRef
48
50
  # @param var_name [String]
49
- # @return [Core::VariableRef]
51
+ # @return [Core::VariableRef]
50
52
  def var_ref(var_name)
51
53
  Core::VariableRef.new(var_name)
52
54
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_kraken
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.02
4
+ version: 0.1.03
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-28 00:00:00.000000000 Z
11
+ date: 2020-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler