mini_kraken 0.3.01 → 0.3.02
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +4 -4
- data/lib/mini_kraken/composite/cons_cell.rb +1 -1
- data/lib/mini_kraken/core/context.rb +0 -132
- data/lib/mini_kraken/core/succeed.rb +1 -1
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/core/context_spec.rb +26 -2
- data/spec/glue/dsl_chap2_spec.rb +81 -13
- data/spec/glue/run_star_expression_spec.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aef8d438d839320f5a38af687cfef1e4018a687d5a9b3d071f5a485fa03cab3d
|
4
|
+
data.tar.gz: a8aefa5c850913634dbe5b28bec9c605782ef2ef7e8791632a9f7b504937442d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 521fefadf00ba08606646afdd5f881a1a7bd4626221085c69adc2e822a2c2777b6fa28d71ea5f6541c614e0b8e9ca8470cab2301683d1c9f235bb1e3535f20f3
|
7
|
+
data.tar.gz: 99478f83ad99813aa29d82cef6cc5b8ad3d5df376d56819151ecc508d62f0a0fd23a62a1fb3553f8aa231e8c317ad5aa6930503b2cd8ff2951b6d94ca8b3480c
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## [0.3.02] - 2020-12-19
|
2
|
+
- `mini_kraken` now implements `conso` relation.
|
3
|
+
|
4
|
+
### CHANGED
|
5
|
+
- Method `Context#build_solution` old source code (redundant) removed.
|
6
|
+
- File `README.md` minor changes.
|
7
|
+
|
8
|
+
### FIXED
|
9
|
+
- Method `ConsCell#expand` pushed a node in the stack even if it was fully visited.
|
10
|
+
|
1
11
|
## [0.3.01] - 2020-12-17
|
2
12
|
- Minor: updated Rubocop config file `.rubocop.yml`
|
3
13
|
|
data/README.md
CHANGED
@@ -14,10 +14,10 @@ ISBN: 9780262535519, (2018), MIT Press.
|
|
14
14
|
- Pure Ruby implementation, not a port from another language
|
15
15
|
- Object-Oriented design
|
16
16
|
- No runtime dependencies
|
17
|
-
- Test suite patterned on
|
17
|
+
- Test suite patterned on examples from the reference book.
|
18
18
|
|
19
19
|
### miniKanren Features
|
20
|
-
- [X] ==
|
20
|
+
- [X] ( == ) unify
|
21
21
|
- [X] run\*
|
22
22
|
- [X] fresh
|
23
23
|
- [X] conde
|
@@ -26,13 +26,13 @@ ISBN: 9780262535519, (2018), MIT Press.
|
|
26
26
|
- [X] defrel
|
27
27
|
- [X] caro
|
28
28
|
- [X] cdro
|
29
|
+
- [X] conso
|
29
30
|
|
30
31
|
### TODO
|
31
32
|
|
32
33
|
- [ ] Occurs check
|
33
34
|
|
34
|
-
|
35
|
-
- [ ] conso
|
35
|
+
Pair-centric relations from Chapter 2
|
36
36
|
- [ ] nullo
|
37
37
|
- [ ] pairo
|
38
38
|
- [ ] singletono
|
@@ -239,138 +239,6 @@ module MiniKraken
|
|
239
239
|
# Returns a Hash with pairs of the form:
|
240
240
|
# { String => Association }, or
|
241
241
|
# { String => AnyValue }
|
242
|
-
def build_zolution
|
243
|
-
clear_ranking
|
244
|
-
calc_ranking
|
245
|
-
solution = {}
|
246
|
-
return solution if failure?
|
247
|
-
|
248
|
-
# require 'debug'
|
249
|
-
symbol_table.root.defns.each_pair do |nm, item|
|
250
|
-
next unless item.kind_of?(LogVar)
|
251
|
-
|
252
|
-
if failure?
|
253
|
-
solution[nm] = nil
|
254
|
-
next
|
255
|
-
end
|
256
|
-
i_name = item.i_name
|
257
|
-
assocs = blackboard.associations_for(i_name, true)
|
258
|
-
if assocs.nil? || assocs.empty? ||
|
259
|
-
(blackboard.fused?(i_name) && assocs.empty?)
|
260
|
-
solution[nm] = AnyValue.new(ranking[i_name])
|
261
|
-
else
|
262
|
-
my_assocs = []
|
263
|
-
assocs.each { |a| my_assocs << a if a.kind_of?(Association) }
|
264
|
-
next if my_assocs.empty?
|
265
|
-
|
266
|
-
# TODO: if multiple associations, conj2 them...
|
267
|
-
as = my_assocs.first
|
268
|
-
|
269
|
-
deps = as.dependencies(self)
|
270
|
-
if deps.any? { |i_name_dep| ranking.include? i_name_dep }
|
271
|
-
# At least one dependent variable in expression is unbound
|
272
|
-
solution[nm] = substitute(as)
|
273
|
-
else
|
274
|
-
solution[nm] = as.value
|
275
|
-
end
|
276
|
-
end
|
277
|
-
end
|
278
|
-
# Take into current scope (e.g. x).
|
279
|
-
# e.g. if q depends on other inner scope variable,
|
280
|
-
# then one should replace every occurrence of x by AnyValue
|
281
|
-
|
282
|
-
solution
|
283
|
-
end
|
284
|
-
=begin
|
285
|
-
find a solution for 1:67
|
286
|
-
Scope picture:
|
287
|
-
s_a:
|
288
|
-
q
|
289
|
-
----
|
290
|
-
s_b:
|
291
|
-
x
|
292
|
-
----
|
293
|
-
s_c:
|
294
|
-
y
|
295
|
-
|
296
|
-
Move_queue
|
297
|
-
bk(s_b)
|
298
|
-
bk(s_c)
|
299
|
-
assoc x => :split
|
300
|
-
assoc y => :pea
|
301
|
-
assoc r => '(,x ,y)
|
302
|
-
|
303
|
-
Let solution be a Hash i_name => value expression
|
304
|
-
Let substitutions be a Hash i_name => value expression
|
305
|
-
Start with root variables:
|
306
|
-
For each root variable:
|
307
|
-
Given q:
|
308
|
-
Is it unbound? N
|
309
|
-
Is it fused? N
|
310
|
-
Get its association (Assumption: one association only)
|
311
|
-
q => '( ,x ,y)
|
312
|
-
dependents:
|
313
|
-
Set { x, y}
|
314
|
-
Given x:
|
315
|
-
Is it unbound? N
|
316
|
-
Is it fused? N
|
317
|
-
Get its association
|
318
|
-
x => :split
|
319
|
-
no dependents => pinned, not a root variable:
|
320
|
-
Add x => :split in substitution
|
321
|
-
substitution = { x => :split }
|
322
|
-
Given y:
|
323
|
-
Is it unbound? N
|
324
|
-
Is it fused? N
|
325
|
-
Get its association
|
326
|
-
y => :pea
|
327
|
-
no dependents => pinned, not a root variable:
|
328
|
-
Add y => :pea in substitution
|
329
|
-
substitution = { x => :split, y => :pea }
|
330
|
-
All dependents resolved? Y
|
331
|
-
Add q => (:split :pea) to solution
|
332
|
-
Done with root variables?
|
333
|
-
Return solution: q => (split :pea)
|
334
|
-
|
335
|
-
Let solution be a Hash i_name => value expression
|
336
|
-
Let substitutions be a Hash i_name => value expression
|
337
|
-
Method add_substitution_for(q, substitutions):
|
338
|
-
Is it already present in substitutions, then return
|
339
|
-
|
340
|
-
Is it unbound? N
|
341
|
-
Is it fused? N
|
342
|
-
Get its association (Assumption: one association only)
|
343
|
-
Assume q => '( ,x ,y)
|
344
|
-
with dependents: Set { x, y}
|
345
|
-
foreach dependent variable call
|
346
|
-
add_substitution_for(q, substitutions) # Recursive call
|
347
|
-
no dependents => pinned
|
348
|
-
Add association in substitution
|
349
|
-
|
350
|
-
IDEA: placeholder in term expressions
|
351
|
-
'( ,x ,y) is translated into:
|
352
|
-
cons(placeholder_100(nil), placeholder_200(nil))
|
353
|
-
x => [placeholder_100]
|
354
|
-
y => [placeholder_200]
|
355
|
-
|
356
|
-
class Placeholder {
|
357
|
-
subj = nil
|
358
|
-
def object_id ; subj.object_id ; end
|
359
|
-
def kind_of? ; subj.kind_of? ; end
|
360
|
-
def to_s ; subj.to_s ; end
|
361
|
-
}
|
362
|
-
|
363
|
-
Where are placedholders created?
|
364
|
-
- In Association.new
|
365
|
-
|
366
|
-
Do we really need Placeholder?
|
367
|
-
Do LogVarRef not fill the bill?
|
368
|
-
IDEA: at a given moment, a LogVarRef is instructed to refer to a value
|
369
|
-
Difficulty: A variable can take multiple values
|
370
|
-
Thus the logVarRef may not embed the value but refer to a value indexed by solution.
|
371
|
-
|
372
|
-
=end
|
373
|
-
|
374
242
|
def build_solution
|
375
243
|
solution = {}
|
376
244
|
return solution if failure?
|
@@ -19,7 +19,7 @@ module MiniKraken
|
|
19
19
|
# When that object receives the message resume, it will
|
20
20
|
# return a success context.
|
21
21
|
# @param _actuals [Array] MUST be empty array for nullary relation.
|
22
|
-
# @param
|
22
|
+
# @param ctx [Core::Context] Runtime context
|
23
23
|
# @return [Core::DuckFiber]
|
24
24
|
def solver_for(_actuals, ctx)
|
25
25
|
# Important: every `solver_for` call will result in a distinct Context.
|
data/lib/mini_kraken/version.rb
CHANGED
data/spec/core/context_spec.rb
CHANGED
@@ -31,11 +31,15 @@ module MiniKraken
|
|
31
31
|
end
|
32
32
|
end # context
|
33
33
|
|
34
|
-
context '
|
34
|
+
context 'Provided services:' do
|
35
35
|
def var(aName)
|
36
36
|
LogVar.new(aName)
|
37
37
|
end
|
38
38
|
|
39
|
+
def cons(term1, term2 = nil)
|
40
|
+
Composite::ConsCell.new(term1, term2)
|
41
|
+
end
|
42
|
+
|
39
43
|
it 'should accept the addition of an entry in symbol table' do
|
40
44
|
subject.insert(var('x'))
|
41
45
|
expect(subject.symbol_table).not_to be_empty
|
@@ -189,7 +193,7 @@ module MiniKraken
|
|
189
193
|
expect(subject.send(:substitute, assoc_q).to_s).to eq('(_0 :foo . :bar)')
|
190
194
|
end
|
191
195
|
|
192
|
-
it 'should build a solution' do
|
196
|
+
it 'should build a solution (atomic terms)' do
|
193
197
|
subject.add_vars(%w[x y z])
|
194
198
|
|
195
199
|
subject.succeeded!
|
@@ -215,6 +219,26 @@ module MiniKraken
|
|
215
219
|
expect(sol['y']).to eq(bar)
|
216
220
|
expect(sol['z']).to eq(:_0)
|
217
221
|
end
|
222
|
+
|
223
|
+
it 'should build a solution (composite terms)' do
|
224
|
+
subject.add_vars(['l'])
|
225
|
+
new_scope = Scope.new
|
226
|
+
subject.enter_scope(new_scope)
|
227
|
+
a = k_symbol(:a)
|
228
|
+
b = k_symbol(:b)
|
229
|
+
c = k_symbol(:c)
|
230
|
+
subject.add_vars(['d'])
|
231
|
+
d_ref = LogVarRef.new('d')
|
232
|
+
subject.associate('l', cons(cons(a, cons(b)), d_ref))
|
233
|
+
subject.associate('d', cons(c))
|
234
|
+
|
235
|
+
subject.succeeded!
|
236
|
+
sol = subject.build_solution
|
237
|
+
expect(sol.size).to eq(1)
|
238
|
+
|
239
|
+
# TODO: fix next line. Actual is: ((a :c))
|
240
|
+
expect(sol['l'].to_s).to eq('((:a :b) :c)')
|
241
|
+
end
|
218
242
|
end
|
219
243
|
end # describe
|
220
244
|
end # module
|
data/spec/glue/dsl_chap2_spec.rb
CHANGED
@@ -231,14 +231,14 @@ module MiniKraken
|
|
231
231
|
# (cdro p d))
|
232
232
|
|
233
233
|
# As 'p' has a special meaning in Ruby, the argument has been remaned to 'r'
|
234
|
-
conso_rel = defrel('conso', %w[a r
|
234
|
+
conso_rel = defrel('conso', %w[a d r], [caro(r, a), cdro(r, d)])
|
235
235
|
|
236
236
|
expect(conso_rel).to be_kind_of(Rela::DefRelation)
|
237
237
|
expect(conso_rel.name).to eq('conso')
|
238
238
|
expect(conso_rel.arity).to eq(3)
|
239
239
|
expect(conso_rel.formals[0]).to match(/^a_[-0-9a-f]+$/)
|
240
|
-
expect(conso_rel.formals[1]).to match(/^
|
241
|
-
expect(conso_rel.formals[2]).to match(/^
|
240
|
+
expect(conso_rel.formals[1]).to match(/^d_[-0-9a-f]+$/)
|
241
|
+
expect(conso_rel.formals[2]).to match(/^r_[-0-9a-f]+$/)
|
242
242
|
g_template = conso_rel.expression
|
243
243
|
expect(g_template.relation).to be_kind_of(Rela::Conj2)
|
244
244
|
g1 = g_template.actuals[0]
|
@@ -255,7 +255,7 @@ module MiniKraken
|
|
255
255
|
end
|
256
256
|
|
257
257
|
# In Scheme:
|
258
|
-
# (defrel (conso a p
|
258
|
+
# (defrel (conso a d p)
|
259
259
|
# (caro p a)
|
260
260
|
# (cdro p d))
|
261
261
|
# In Ruby, `p`is a standard Kernel method => replace it by `r`
|
@@ -263,19 +263,87 @@ module MiniKraken
|
|
263
263
|
defrel_caro
|
264
264
|
defrel_cdro
|
265
265
|
|
266
|
-
|
266
|
+
# Definition derived from frame 2:25
|
267
|
+
# defrel('conso', %w[a d r], [caro(r, a), cdro(r, d)])
|
268
|
+
|
269
|
+
# Definition derived from frame 2:26
|
270
|
+
defrel('conso', %w[a d r], [unify(cons(a, d), r)])
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'passes frame 2:19' do
|
274
|
+
defrel_conso
|
275
|
+
|
276
|
+
# (run* l
|
277
|
+
# (conso '(a b c) '(d e) l)) ;; => (((a b c) d e))
|
278
|
+
|
279
|
+
result = run_star('l', conso(list(:a, :b, :c), list(:d, :e), l))
|
280
|
+
expect(result.to_s).to eq('(((:a :b :c) :d :e))')
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'passes frame 2:20' do
|
284
|
+
defrel_conso
|
285
|
+
|
286
|
+
# (run* x
|
287
|
+
# (conso x '(a b c) '(d a b c))) ;; => (d)
|
288
|
+
|
289
|
+
result = run_star('x', conso(x, list(:a, :b, :c), list(:d, :a, :b, :c)))
|
290
|
+
expect(result.to_s).to eq('(:d)')
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'passes frame 2:21' do
|
294
|
+
defrel_conso
|
295
|
+
|
296
|
+
# (run* r
|
297
|
+
# (fresh (x y z)
|
298
|
+
# (== '(e a d ,x) r)
|
299
|
+
# (conso y '(a ,z c) r))) ;; => ((e a d c)
|
300
|
+
|
301
|
+
expr = fresh(%w[x y z],
|
302
|
+
[unify(list(:e, :a, :d, x), r),
|
303
|
+
conso(y, list(:a, z, :c), r)])
|
304
|
+
result = run_star('r', expr)
|
305
|
+
expect(result.to_s).to eq('((:e :a :d :c))')
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'passes frame 2:22' do
|
309
|
+
defrel_conso
|
310
|
+
|
311
|
+
# (run* x
|
312
|
+
# (conso x '(a ,x c) '(d a ,x c))) ;; => (d)
|
313
|
+
|
314
|
+
result = run_star('x', conso(x, list(:a, x, :c), list(:d, :a, x, :c)))
|
315
|
+
expect(result.to_s).to eq('(:d)')
|
267
316
|
end
|
268
317
|
|
269
|
-
|
270
|
-
|
271
|
-
# defrel_conso
|
318
|
+
it 'passes frame 2:23' do
|
319
|
+
defrel_conso
|
272
320
|
|
273
|
-
#
|
274
|
-
#
|
321
|
+
# (run* l
|
322
|
+
# (fresh (x)
|
323
|
+
# (== '(d a ,x c) l)
|
324
|
+
# (conso x '(a ,x c) l))) ;; => ((d a d c)
|
325
|
+
|
326
|
+
expr = fresh(%w[x],
|
327
|
+
[unify(list(:d, :a, x, :c), l),
|
328
|
+
conso(x, list(:a, x, :c), l)])
|
329
|
+
result = run_star('l', expr)
|
330
|
+
expect(result.to_s).to eq('((:d :a :d :c))')
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'passes frame 2:24' do
|
334
|
+
defrel_conso
|
275
335
|
|
276
|
-
#
|
277
|
-
#
|
278
|
-
|
336
|
+
# (run* l
|
337
|
+
# (fresh (x)
|
338
|
+
# (conso x '(a ,x c) l)))
|
339
|
+
# (== '(d a ,x c) l) ;; => ((d a d c)
|
340
|
+
|
341
|
+
expr = fresh(%w[x],
|
342
|
+
[conso(x, list(:a, x, :c), l),
|
343
|
+
unify(list(:d, :a, x, :c), l)])
|
344
|
+
result = run_star('l', expr)
|
345
|
+
expect(result.to_s).to eq('((:d :a :d :c))')
|
346
|
+
end
|
279
347
|
end # context
|
280
348
|
end # describe
|
281
349
|
end # module
|
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.3.
|
4
|
+
version: 0.3.02
|
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-12-
|
11
|
+
date: 2020-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|