mini_kraken 0.1.07 → 0.1.12
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 +50 -0
- data/README.md +11 -3
- data/lib/mini_kraken/core/association_walker.rb +1 -1
- data/lib/mini_kraken/core/base_arg.rb +10 -0
- data/lib/mini_kraken/core/conde.rb +143 -0
- data/lib/mini_kraken/core/conj2.rb +59 -53
- data/lib/mini_kraken/core/def_relation.rb +49 -0
- data/lib/mini_kraken/core/disj2.rb +53 -48
- data/lib/mini_kraken/core/duck_fiber.rb +1 -1
- data/lib/mini_kraken/core/equals.rb +134 -126
- data/lib/mini_kraken/core/fail.rb +18 -14
- data/lib/mini_kraken/core/formal_arg.rb +22 -0
- data/lib/mini_kraken/core/formal_ref.rb +24 -0
- data/lib/mini_kraken/core/goal.rb +9 -3
- data/lib/mini_kraken/core/goal_arg.rb +5 -3
- data/lib/mini_kraken/core/goal_template.rb +60 -0
- data/lib/mini_kraken/core/k_boolean.rb +31 -0
- data/lib/mini_kraken/core/outcome.rb +14 -0
- data/lib/mini_kraken/core/relation.rb +7 -0
- data/lib/mini_kraken/core/succeed.rb +17 -13
- data/lib/mini_kraken/core/vocabulary.rb +22 -4
- data/lib/mini_kraken/glue/fresh_env.rb +45 -3
- data/lib/mini_kraken/glue/run_star_expression.rb +43 -26
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/core/conde_spec.rb +147 -0
- data/spec/core/conj2_spec.rb +8 -9
- data/spec/core/def_relation_spec.rb +96 -0
- data/spec/core/disj2_spec.rb +0 -45
- data/spec/core/duck_fiber_spec.rb +12 -1
- data/spec/core/equals_spec.rb +3 -3
- data/spec/core/goal_template_spec.rb +74 -0
- data/spec/core/k_boolean_spec.rb +107 -0
- data/spec/core/outcome_spec.rb +48 -0
- data/spec/core/vocabulary_spec.rb +6 -0
- data/spec/glue/fresh_env_spec.rb +27 -1
- data/spec/glue/run_star_expression_spec.rb +535 -18
- data/spec/mini_kraken_spec.rb +2 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/factory_methods.rb +15 -0
- metadata +19 -2
data/spec/glue/fresh_env_spec.rb
CHANGED
@@ -3,7 +3,9 @@
|
|
3
3
|
require_relative '../spec_helper' # Use the RSpec framework
|
4
4
|
require_relative '../../lib/mini_kraken/core/goal'
|
5
5
|
require_relative '../../lib/mini_kraken/core/equals'
|
6
|
+
require_relative '../../lib/mini_kraken/core/fail'
|
6
7
|
require_relative '../../lib/mini_kraken/core/k_symbol'
|
8
|
+
require_relative '../../lib/mini_kraken/core/succeed'
|
7
9
|
|
8
10
|
# Load the class under test
|
9
11
|
require_relative '../../lib/mini_kraken/glue/fresh_env'
|
@@ -17,15 +19,39 @@ module MiniKraken
|
|
17
19
|
let(:sample_goal) do
|
18
20
|
Core::Goal.new(Core::Equals.instance, [pea, pod])
|
19
21
|
end
|
22
|
+
let(:pea_goal) do
|
23
|
+
Core::Goal.new(Core::Equals.instance, [pea, pea])
|
24
|
+
end
|
25
|
+
let(:goal_succeeds) { Core::Goal.new(Core::Succeed.instance, []) }
|
26
|
+
let(:goal_fails) { Core::Goal.new(Core::Fail.instance, []) }
|
20
27
|
subject { FreshEnv.new(['q'], sample_goal) }
|
21
28
|
|
22
29
|
context 'Initialization:' do
|
23
|
-
it '
|
30
|
+
it 'could be initialized with names and a goal' do
|
24
31
|
expect { FreshEnv.new(['q'], sample_goal) }.not_to raise_error
|
25
32
|
end
|
26
33
|
|
34
|
+
it 'could be initialized with names and goals' do
|
35
|
+
expect { FreshEnv.new(%w[x y], [pea_goal, goal_succeeds]) }.not_to raise_error
|
36
|
+
end
|
37
|
+
|
27
38
|
it 'should know its variables' do
|
28
39
|
expect(subject.vars['q']).not_to be_nil
|
40
|
+
|
41
|
+
instance = FreshEnv.new(%w[x y], sample_goal)
|
42
|
+
expect(instance.vars['x']).not_to be_nil
|
43
|
+
expect(instance.vars['y']).not_to be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should know its goal' do
|
47
|
+
# Single goal at initialization
|
48
|
+
expect(subject.goal).to eq(sample_goal)
|
49
|
+
|
50
|
+
# Multiple goals at initialization
|
51
|
+
instance = FreshEnv.new(['q'], [pea_goal, goal_succeeds])
|
52
|
+
expect(instance.goal.relation.name).to eq('conj2')
|
53
|
+
expect(instance.goal.actuals[0]).to eq(pea_goal)
|
54
|
+
expect(instance.goal.actuals[1]).to eq(goal_succeeds)
|
29
55
|
end
|
30
56
|
end # context
|
31
57
|
|
@@ -2,10 +2,15 @@
|
|
2
2
|
|
3
3
|
require_relative '../spec_helper' # Use the RSpec framework
|
4
4
|
require_relative '../../lib/mini_kraken/core/goal'
|
5
|
+
require_relative '../../lib/mini_kraken/core/conde'
|
5
6
|
require_relative '../../lib/mini_kraken/core/conj2'
|
7
|
+
require_relative '../../lib/mini_kraken/core/def_relation'
|
6
8
|
require_relative '../../lib/mini_kraken/core/disj2'
|
7
9
|
require_relative '../../lib/mini_kraken/core/equals'
|
8
10
|
require_relative '../../lib/mini_kraken/core/fail'
|
11
|
+
require_relative '../../lib/mini_kraken/core/formal_arg'
|
12
|
+
require_relative '../../lib/mini_kraken/core/formal_ref'
|
13
|
+
require_relative '../../lib/mini_kraken/core/goal_template'
|
9
14
|
require_relative '../../lib/mini_kraken/core/succeed'
|
10
15
|
|
11
16
|
require_relative '../support/factory_methods'
|
@@ -21,16 +26,26 @@ module MiniKraken
|
|
21
26
|
let(:pea) { k_symbol(:pea) }
|
22
27
|
let(:pod) { k_symbol(:pod) }
|
23
28
|
let(:sample_goal) { equals_goal(pea, pod) }
|
29
|
+
let(:fails) { Core::Goal.new(Core::Fail.instance, []) }
|
30
|
+
let(:succeeds) { Core::Goal.new(Core::Succeed.instance, []) }
|
24
31
|
subject { RunStarExpression.new('q', sample_goal) }
|
25
32
|
|
26
33
|
context 'Initialization:' do
|
27
|
-
it '
|
34
|
+
it 'could be initialized with a name and a goal' do
|
28
35
|
expect { RunStarExpression.new('q', sample_goal) }.not_to raise_error
|
29
36
|
end
|
30
37
|
|
38
|
+
it 'could be initialized with multiple names and a goal' do
|
39
|
+
expect { RunStarExpression.new(%w[r x y], sample_goal) }.not_to raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'could be initialized with multiple names and goals' do
|
43
|
+
expect { RunStarExpression.new(%w[r x y], [succeeds, succeeds]) }.not_to raise_error
|
44
|
+
end
|
45
|
+
|
31
46
|
it 'should know its variables' do
|
32
47
|
expect(subject.env.vars['q']).not_to be_nil
|
33
|
-
expect(subject.
|
48
|
+
expect(subject.env.vars.values[0].name).to eq('q')
|
34
49
|
end
|
35
50
|
|
36
51
|
it 'should know its goal' do
|
@@ -39,18 +54,40 @@ module MiniKraken
|
|
39
54
|
end # context
|
40
55
|
|
41
56
|
context 'Provided services:' do
|
57
|
+
let(:k_false) { k_boolean(false) }
|
58
|
+
let(:k_true) { k_boolean(true) }
|
59
|
+
let(:bean) { k_symbol(:bean) }
|
42
60
|
let(:corn) { k_symbol(:corn) }
|
61
|
+
let(:cup) { k_symbol(:cup) }
|
62
|
+
let(:green) { k_symbol(:green) }
|
63
|
+
let(:lentil) { k_symbol(:lentil) }
|
43
64
|
let(:meal) { k_symbol(:meal) }
|
44
65
|
let(:oil) { k_symbol(:oil) }
|
45
66
|
let(:olive) { k_symbol(:olive) }
|
67
|
+
let(:red) { k_symbol(:red) }
|
68
|
+
let(:soup) { k_symbol(:soup) }
|
69
|
+
let(:split) { k_symbol(:split) }
|
70
|
+
let(:tea) { k_symbol(:tea) }
|
71
|
+
let(:virgin) { k_symbol(:virgin) }
|
46
72
|
let(:ref_q) { Core::VariableRef.new('q') }
|
73
|
+
let(:ref_r) { Core::VariableRef.new('r') }
|
47
74
|
let(:ref_x) { Core::VariableRef.new('x') }
|
48
75
|
let(:ref_y) { Core::VariableRef.new('y') }
|
76
|
+
let(:ref_z) { Core::VariableRef.new('z') }
|
49
77
|
let(:ref_s) { Core::VariableRef.new('s') }
|
50
78
|
let(:ref_t) { Core::VariableRef.new('t') }
|
51
79
|
let(:ref_u) { Core::VariableRef.new('u') }
|
52
|
-
let(:
|
53
|
-
let(:
|
80
|
+
let(:ref_z) { Core::VariableRef.new('z') }
|
81
|
+
let(:t_ref) { Core::FormalRef.new('t') }
|
82
|
+
let(:equals_tea) { Core::GoalTemplate.new(Core::Equals.instance, [tea, t_ref]) }
|
83
|
+
let(:equals_cup) { Core::GoalTemplate.new(Core::Equals.instance, [cup, t_ref]) }
|
84
|
+
let(:g_template) { Core::GoalTemplate.new(Core::Disj2.instance, [equals_tea, equals_cup]) }
|
85
|
+
let(:formal_t) { Core::FormalArg.new('t') }
|
86
|
+
|
87
|
+
# Reasoned S2, frame 1:82
|
88
|
+
# (defrel (teacupo t)
|
89
|
+
# (disj2 (== 'tea t) (== 'cup t)))
|
90
|
+
let(:teacupo_rel) { Core::DefRelation.new('teacupo', g_template, [formal_t]) }
|
54
91
|
|
55
92
|
it 'should return a null list with the fail goal' do
|
56
93
|
# Reasoned S2, frame 1:7
|
@@ -88,8 +125,7 @@ module MiniKraken
|
|
88
125
|
end
|
89
126
|
|
90
127
|
it 'should return a null list with the succeed goal' do
|
91
|
-
|
92
|
-
instance = RunStarExpression.new('q', success)
|
128
|
+
instance = RunStarExpression.new('q', succeeds)
|
93
129
|
|
94
130
|
# (display (run* q succeed)) ;; => (_0)
|
95
131
|
# Reasoned S2, frame 1:16
|
@@ -327,16 +363,16 @@ module MiniKraken
|
|
327
363
|
end
|
328
364
|
|
329
365
|
# TODO: fix erratic RSpec failure
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
366
|
+
it 'should support conjunction of one succeed and a successful goal' do
|
367
|
+
subgoal = equals_goal(corn, ref_q)
|
368
|
+
goal = conj2_goal(succeeds, subgoal)
|
369
|
+
instance = RunStarExpression.new('q', goal)
|
334
370
|
|
335
|
-
#
|
336
|
-
#
|
337
|
-
|
338
|
-
|
339
|
-
|
371
|
+
# Reasoned S2, frame 1:51
|
372
|
+
# (run* q (conj2 succeed (== 'corn q)) ;; => ('corn)
|
373
|
+
result = instance.run
|
374
|
+
expect(result.car).to eq(corn)
|
375
|
+
end
|
340
376
|
|
341
377
|
it 'should support conjunction of one fail and a successful goal' do
|
342
378
|
subgoal = equals_goal(corn, ref_q)
|
@@ -386,7 +422,7 @@ module MiniKraken
|
|
386
422
|
instance = RunStarExpression.new('q', goal)
|
387
423
|
|
388
424
|
# Reasoned S2, frame 1:56
|
389
|
-
# (run* q (disj2 (
|
425
|
+
# (run* q (disj2 (== 'olive q) fail)) ;; => ('olive)
|
390
426
|
result = instance.run
|
391
427
|
expect(result.car).to eq(olive)
|
392
428
|
end
|
@@ -397,7 +433,7 @@ module MiniKraken
|
|
397
433
|
instance = RunStarExpression.new('q', goal)
|
398
434
|
|
399
435
|
# Reasoned S2, frame 1:57
|
400
|
-
# (run* q (disj2 fail (
|
436
|
+
# (run* q (disj2 fail (== 'oil q))) ;; => (oil)
|
401
437
|
result = instance.run
|
402
438
|
expect(result.car).to eq(oil)
|
403
439
|
end
|
@@ -409,7 +445,7 @@ module MiniKraken
|
|
409
445
|
instance = RunStarExpression.new('q', goal)
|
410
446
|
|
411
447
|
# Reasoned S2, frame 1:58
|
412
|
-
# (run* q (disj2 (
|
448
|
+
# (run* q (disj2 (== 'olive q) (== 'oil q))) ;; => (olive oil)
|
413
449
|
result = instance.run
|
414
450
|
expect(result.car).to eq(olive)
|
415
451
|
expect(result.cdr.car).to eq(oil)
|
@@ -480,6 +516,487 @@ module MiniKraken
|
|
480
516
|
expect(result.car).to eq(oil)
|
481
517
|
expect(result.cdr.car).to eq(olive)
|
482
518
|
end
|
519
|
+
|
520
|
+
it 'should accept nesting of disj2 and conj2 (IV)' do
|
521
|
+
oil_goal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
|
522
|
+
disja = disj2_goal(succeeds, oil_goal)
|
523
|
+
olive_goal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
|
524
|
+
disjb = disj2_goal(olive_goal, disja)
|
525
|
+
virgin_goal = Core::Goal.new(Core::Equals.instance, [virgin, ref_x])
|
526
|
+
conjunction = conj2_goal(virgin_goal, fails)
|
527
|
+
goal = disj2_goal(conjunction, disjb)
|
528
|
+
instance = RunStarExpression.new('x', goal)
|
529
|
+
|
530
|
+
# Reasoned S2, frame 1:65
|
531
|
+
# (run* x (disj2
|
532
|
+
# (conj2(== 'virgin x) fails)
|
533
|
+
# (disj2
|
534
|
+
# (== 'olive x)
|
535
|
+
# (dis2
|
536
|
+
# succeeds
|
537
|
+
# (== 'oil x))))) ;; => (olive _0 oil)
|
538
|
+
result = instance.run
|
539
|
+
expect(result.car).to eq(olive)
|
540
|
+
expect(result.cdr.car).to eq(any_value(0))
|
541
|
+
expect(result.cdr.cdr.car).to eq(oil)
|
542
|
+
end
|
543
|
+
|
544
|
+
it 'should accept nesting fresh, disj2 and conj2 expressions (I)' do
|
545
|
+
subgoal1 = equals_goal(split, ref_x)
|
546
|
+
expr1 = equals_goal(pea, ref_y)
|
547
|
+
expr2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
|
548
|
+
subgoal2 = conj2_goal(expr1, expr2)
|
549
|
+
goal = conj2_goal(subgoal1, subgoal2)
|
550
|
+
fresh_env_y = FreshEnv.new(['y'], goal)
|
551
|
+
fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
|
552
|
+
instance = RunStarExpression.new('r', fresh_env_x)
|
553
|
+
|
554
|
+
# Reasoned S2, frame 1:67
|
555
|
+
# (run* r
|
556
|
+
# (fresh x
|
557
|
+
# (fresh y
|
558
|
+
# (conj2
|
559
|
+
# (== 'split x)
|
560
|
+
# (conj2
|
561
|
+
# (== 'pea y)
|
562
|
+
# (== '(,x ,y) r)))))) ;; => ((split pea))
|
563
|
+
result = instance.run
|
564
|
+
expect(result.car.car).to eq(split)
|
565
|
+
expect(result.car.cdr.car).to eq(pea)
|
566
|
+
end
|
567
|
+
|
568
|
+
it 'should accept nesting fresh, disj2 and conj2 expressions (II)' do
|
569
|
+
expr1 = equals_goal(split, ref_x)
|
570
|
+
expr2 = equals_goal(pea, ref_y)
|
571
|
+
subgoal1 = conj2_goal(expr1, expr2)
|
572
|
+
subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
|
573
|
+
goal = conj2_goal(subgoal1, subgoal2)
|
574
|
+
fresh_env_y = FreshEnv.new(['y'], goal)
|
575
|
+
fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
|
576
|
+
instance = RunStarExpression.new('r', fresh_env_x)
|
577
|
+
|
578
|
+
# Reasoned S2, frame 1:68
|
579
|
+
# (run* r
|
580
|
+
# (fresh x
|
581
|
+
# (fresh y
|
582
|
+
# (conj2
|
583
|
+
# (conj2
|
584
|
+
# (== 'split x)
|
585
|
+
# (== 'pea y)
|
586
|
+
# (== '(,x ,y) r)))))) ;; => ((split pea))
|
587
|
+
result = instance.run
|
588
|
+
expect(result.car.car).to eq(split)
|
589
|
+
expect(result.car.cdr.car).to eq(pea)
|
590
|
+
end
|
591
|
+
|
592
|
+
it 'should accept fresh with multiple variables' do
|
593
|
+
expr1 = equals_goal(split, ref_x)
|
594
|
+
expr2 = equals_goal(pea, ref_y)
|
595
|
+
subgoal1 = conj2_goal(expr1, expr2)
|
596
|
+
subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
|
597
|
+
goal = conj2_goal(subgoal1, subgoal2)
|
598
|
+
fresh_env = FreshEnv.new(%w[x y], goal)
|
599
|
+
instance = RunStarExpression.new('r', fresh_env)
|
600
|
+
|
601
|
+
# Reasoned S2, frame 1:70
|
602
|
+
# (run* r
|
603
|
+
# (fresh (x y)
|
604
|
+
# (conj2
|
605
|
+
# (conj2
|
606
|
+
# (== 'split x)
|
607
|
+
# (== 'pea y)
|
608
|
+
# (== '(,x ,y) r))))) ;; => ((split pea))
|
609
|
+
result = instance.run
|
610
|
+
expect(result.car.car).to eq(split)
|
611
|
+
expect(result.car.cdr.car).to eq(pea)
|
612
|
+
end
|
613
|
+
|
614
|
+
it 'should accept multiple variables' do
|
615
|
+
expr1 = equals_goal(split, ref_x)
|
616
|
+
expr2 = equals_goal(pea, ref_y)
|
617
|
+
subgoal1 = conj2_goal(expr1, expr2)
|
618
|
+
subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
|
619
|
+
goal = conj2_goal(subgoal1, subgoal2)
|
620
|
+
instance = RunStarExpression.new(%w[r x y], goal)
|
621
|
+
|
622
|
+
# Reasoned S2, frame 1:72
|
623
|
+
# (run* (r x y)
|
624
|
+
# (conj2
|
625
|
+
# (conj2
|
626
|
+
# (== 'split x)
|
627
|
+
# (== 'pea y))
|
628
|
+
# (== '(,x ,y) r))) ;; => (((split pea) split pea))
|
629
|
+
# o
|
630
|
+
# / \
|
631
|
+
# o nil
|
632
|
+
# / \
|
633
|
+
# / \
|
634
|
+
# / \
|
635
|
+
# / \
|
636
|
+
# / \
|
637
|
+
# o o
|
638
|
+
# / \ / \
|
639
|
+
# split o split o
|
640
|
+
# / \ / \
|
641
|
+
# pea nil pea nil
|
642
|
+
result = instance.run
|
643
|
+
expect(result.car.car.car).to eq(split)
|
644
|
+
expect(result.car.car.cdr.car).to eq(pea)
|
645
|
+
expect(result.car.car.cdr.cdr).to be_nil
|
646
|
+
expect(result.car.cdr.car).to eq(split)
|
647
|
+
expect(result.car.cdr.cdr.car).to eq(pea)
|
648
|
+
expect(result.car.cdr.cdr.cdr).to be_nil
|
649
|
+
end
|
650
|
+
|
651
|
+
it 'should allow simplication of expressions' do
|
652
|
+
expr1 = equals_goal(split, ref_x)
|
653
|
+
expr2 = equals_goal(pea, ref_y)
|
654
|
+
goal = conj2_goal(expr1, expr2)
|
655
|
+
instance = RunStarExpression.new(%w[x y], goal)
|
656
|
+
|
657
|
+
# Reasoned S2, frame 1:75
|
658
|
+
# (run* (x y)
|
659
|
+
# (conj2
|
660
|
+
# (== 'split x)
|
661
|
+
# (== 'pea y))) ;; => ((split pea))
|
662
|
+
result = instance.run
|
663
|
+
expect(result.car.car).to eq(split)
|
664
|
+
expect(result.car.cdr.car).to eq(pea)
|
665
|
+
end
|
666
|
+
|
667
|
+
it 'should allow simplication of expressions' do
|
668
|
+
expr1 = equals_goal(split, ref_x)
|
669
|
+
expr2 = equals_goal(pea, ref_y)
|
670
|
+
subgoal1 = conj2_goal(expr1, expr2)
|
671
|
+
expr3 = equals_goal(red, ref_x)
|
672
|
+
expr4 = equals_goal(bean, ref_y)
|
673
|
+
subgoal2 = conj2_goal(expr3, expr4)
|
674
|
+
goal = disj2_goal(subgoal1, subgoal2)
|
675
|
+
instance = RunStarExpression.new(%w[x y], goal)
|
676
|
+
|
677
|
+
# Reasoned S2, frame 1:76
|
678
|
+
# (run* (x y)
|
679
|
+
# (disj2
|
680
|
+
# (conj2 (== 'split x) (== 'pea y))
|
681
|
+
# (conj2 (== 'red x) (== 'bean y)))) ;; => ((split pea)(red bean))
|
682
|
+
result = instance.run
|
683
|
+
expect(result.car.car).to eq(split)
|
684
|
+
expect(result.car.cdr.car).to eq(pea)
|
685
|
+
expect(result.cdr.car.car).to eq(red)
|
686
|
+
expect(result.cdr.car.cdr.car).to eq(bean)
|
687
|
+
end
|
688
|
+
|
689
|
+
it 'should allow nesting a disjunction inside of conjunction' do
|
690
|
+
expr1 = equals_goal(split, ref_x)
|
691
|
+
expr2 = equals_goal(red, ref_x)
|
692
|
+
subgoal1 = disj2_goal(expr1, expr2)
|
693
|
+
subgoal2 = equals_goal(ref_x, ref_y)
|
694
|
+
goal = conj2_goal(subgoal1, subgoal2)
|
695
|
+
instance = RunStarExpression.new(%w[x y], goal)
|
696
|
+
|
697
|
+
# (display (run* (x y)
|
698
|
+
# (conj2
|
699
|
+
# (disj2
|
700
|
+
# (== 'split x)
|
701
|
+
# (== 'red x))
|
702
|
+
# (== x y)))) ;; => ((split split) (red red))
|
703
|
+
result = instance.run
|
704
|
+
expect(result.car.car).to eq(split)
|
705
|
+
expect(result.car.cdr.car).to eq(split)
|
706
|
+
expect(result.cdr.car.cdr.car).to eq(red)
|
707
|
+
end
|
708
|
+
|
709
|
+
it 'should accept fresh with multiple variables' do
|
710
|
+
expr1 = equals_goal(split, ref_x)
|
711
|
+
expr2 = equals_goal(pea, ref_y)
|
712
|
+
subgoal1 = conj2_goal(expr1, expr2)
|
713
|
+
expr3 = equals_goal(red, ref_x)
|
714
|
+
expr4 = equals_goal(bean, ref_y)
|
715
|
+
subgoal2 = conj2_goal(expr3, expr4)
|
716
|
+
subgoal3 = disj2_goal(subgoal1, subgoal2)
|
717
|
+
subgoal4 = equals_goal(cons(ref_x, cons(ref_y, cons(soup))), ref_r)
|
718
|
+
goal = conj2_goal(subgoal3, subgoal4)
|
719
|
+
fresh_env = FreshEnv.new(%w[x y], goal)
|
720
|
+
instance = RunStarExpression.new('r', fresh_env)
|
721
|
+
|
722
|
+
# Reasoned S2, frame 1:77
|
723
|
+
# (run* r
|
724
|
+
# (fresh (x y)
|
725
|
+
# (conj2
|
726
|
+
# (disj2
|
727
|
+
# (conj2 (== 'split x) (== 'pea y))
|
728
|
+
# (conj2 (== 'red x) (== 'bean y)))
|
729
|
+
# (== '(,x ,y soup) r)))) ;; => ((split pea soup) (red bean soup))
|
730
|
+
result = instance.run
|
731
|
+
expect(result.car.car).to eq(split)
|
732
|
+
expect(result.car.cdr.car).to eq(pea)
|
733
|
+
expect(result.car.cdr.cdr.car).to eq(soup)
|
734
|
+
expect(result.cdr.car.car).to eq(red)
|
735
|
+
expect(result.cdr.car.cdr.car).to eq(bean)
|
736
|
+
expect(result.cdr.car.cdr.cdr.car).to eq(soup)
|
737
|
+
end
|
738
|
+
|
739
|
+
it 'should allow fresh with multiple goals' do
|
740
|
+
expr1 = equals_goal(split, ref_x)
|
741
|
+
expr2 = equals_goal(pea, ref_y)
|
742
|
+
subgoal1 = conj2_goal(expr1, expr2)
|
743
|
+
expr3 = equals_goal(red, ref_x)
|
744
|
+
expr4 = equals_goal(bean, ref_y)
|
745
|
+
subgoal2 = conj2_goal(expr3, expr4)
|
746
|
+
goal1 = disj2_goal(subgoal1, subgoal2)
|
747
|
+
goal2 = equals_goal(cons(ref_x, cons(ref_y, cons(soup))), ref_r)
|
748
|
+
fresh_env = FreshEnv.new(%w[x y], [goal1, goal2])
|
749
|
+
instance = RunStarExpression.new('r', fresh_env)
|
750
|
+
|
751
|
+
# Reasoned S2, frame 1:78
|
752
|
+
# (run* r
|
753
|
+
# (fresh (x y)
|
754
|
+
# (disj2
|
755
|
+
# (conj2 (== 'split x) (== 'pea y))
|
756
|
+
# (conj2 (== 'red x) (== 'bean y)))
|
757
|
+
# (== '(,x ,y soup) r))) ;; => ((split pea soup) (red bean soup))
|
758
|
+
result = instance.run
|
759
|
+
expect(result.car.car).to eq(split)
|
760
|
+
expect(result.car.cdr.car).to eq(pea)
|
761
|
+
expect(result.car.cdr.cdr.car).to eq(soup)
|
762
|
+
expect(result.cdr.car.car).to eq(red)
|
763
|
+
expect(result.cdr.car.cdr.car).to eq(bean)
|
764
|
+
expect(result.cdr.car.cdr.cdr.car).to eq(soup)
|
765
|
+
end
|
766
|
+
|
767
|
+
it 'should allow run* with multiple goals' do
|
768
|
+
expr1 = equals_goal(split, ref_x)
|
769
|
+
expr2 = equals_goal(pea, ref_y)
|
770
|
+
subgoal1 = conj2_goal(expr1, expr2)
|
771
|
+
expr3 = equals_goal(red, ref_x)
|
772
|
+
expr4 = equals_goal(bean, ref_y)
|
773
|
+
subgoal2 = conj2_goal(expr3, expr4)
|
774
|
+
goal1 = disj2_goal(subgoal1, subgoal2)
|
775
|
+
goal2 = equals_goal(soup, ref_z)
|
776
|
+
instance = RunStarExpression.new(%w[x y z], [goal1, goal2])
|
777
|
+
|
778
|
+
# Reasoned S2, frame 1:80
|
779
|
+
# (run* (x y z)
|
780
|
+
# (disj2
|
781
|
+
# (conj2 (== 'split x) (== 'pea y))
|
782
|
+
# (conj2 (== 'red x) (== 'bean y)))
|
783
|
+
# (== 'soup z)) ;; => ((split pea soup) (red bean soup))
|
784
|
+
result = instance.run
|
785
|
+
expect(result.car.car).to eq(split)
|
786
|
+
expect(result.car.cdr.car).to eq(pea)
|
787
|
+
expect(result.car.cdr.cdr.car).to eq(soup)
|
788
|
+
expect(result.cdr.car.car).to eq(red)
|
789
|
+
expect(result.cdr.car.cdr.car).to eq(bean)
|
790
|
+
expect(result.cdr.car.cdr.cdr.car).to eq(soup)
|
791
|
+
end
|
792
|
+
|
793
|
+
it 'should allow simplified expressions with multiple goals' do
|
794
|
+
expr1 = equals_goal(split, ref_x)
|
795
|
+
expr2 = equals_goal(pea, ref_y)
|
796
|
+
instance = RunStarExpression.new(%w[x y], [expr1, expr2])
|
797
|
+
|
798
|
+
# Reasoned S2, frame 1:81
|
799
|
+
# (run* (x y)
|
800
|
+
# (== 'split x)
|
801
|
+
# (== 'pea y)) ;; => ((split pea))
|
802
|
+
result = instance.run
|
803
|
+
expect(result.car.car).to eq(split)
|
804
|
+
expect(result.car.cdr.car).to eq(pea)
|
805
|
+
end
|
806
|
+
|
807
|
+
it 'should solve expression with defrel' do
|
808
|
+
teacupo_goal = Core::Goal.new(teacupo_rel, [ref_x])
|
809
|
+
|
810
|
+
# Reasoned S2, frame 1:83
|
811
|
+
# (run* x
|
812
|
+
# (teacupo x)) ;; => ((tea cup))
|
813
|
+
instance = RunStarExpression.new('x', teacupo_goal)
|
814
|
+
|
815
|
+
result = instance.run
|
816
|
+
expect(result.car).to eq(tea)
|
817
|
+
expect(result.cdr.car).to eq(cup)
|
818
|
+
end
|
819
|
+
|
820
|
+
it 'should solve expression with defrel and booleans' do
|
821
|
+
teacupo_goal = Core::Goal.new(teacupo_rel, [ref_x])
|
822
|
+
expr2 = equals_goal(k_true, ref_y)
|
823
|
+
subgoal1 = conj2_goal(teacupo_goal, expr2)
|
824
|
+
expr3 = equals_goal(k_false, ref_x)
|
825
|
+
expr4 = equals_goal(k_true, ref_y)
|
826
|
+
subgoal2 = conj2_goal(expr3, expr4)
|
827
|
+
goal = disj2_goal(subgoal1, subgoal2)
|
828
|
+
# Reasoned S2, frame 1:84
|
829
|
+
# (run* (x y)
|
830
|
+
# (disj2
|
831
|
+
# (conj2 (teacupo x) (== #t y))
|
832
|
+
# (conj2 (== #f x) (== #t y))) ;; => ((#f #t)(tea #t) (cup #t))
|
833
|
+
instance = RunStarExpression.new(%w[x y], goal)
|
834
|
+
|
835
|
+
result = instance.run
|
836
|
+
# Order of solutions differs from RS book
|
837
|
+
expect(result.car).to eq(cons(tea, cons(true)))
|
838
|
+
expect(result.cdr.car).to eq(cons(cup, cons(true)))
|
839
|
+
expect(result.cdr.cdr.car).to eq(cons(false, cons(true)))
|
840
|
+
end
|
841
|
+
|
842
|
+
it 'should solve expression with two variable and defrel' do
|
843
|
+
teacupo_goal1 = Core::Goal.new(teacupo_rel, [ref_x])
|
844
|
+
teacupo_goal2 = Core::Goal.new(teacupo_rel, [ref_y])
|
845
|
+
|
846
|
+
# Reasoned S2, frame 1:85
|
847
|
+
# (run* (x y)
|
848
|
+
# (teacupo x)
|
849
|
+
# (teacupo y)) ;; => ((tea tea)(tea cup)(cup tea)(cup c))
|
850
|
+
instance = RunStarExpression.new(%w[x y], [teacupo_goal1, teacupo_goal2])
|
851
|
+
|
852
|
+
result = instance.run
|
853
|
+
expect(result.car).to eq(cons(tea, cons(tea)))
|
854
|
+
expect(result.cdr.car).to eq(cons(tea, cons(cup)))
|
855
|
+
expect(result.cdr.cdr.car).to eq(cons(cup, cons(tea)))
|
856
|
+
expect(result.cdr.cdr.cdr.car).to eq(cons(cup, cons(cup)))
|
857
|
+
end
|
858
|
+
|
859
|
+
it 'should solve expression with two variable and defrel' do
|
860
|
+
teacupo_goal1 = Core::Goal.new(teacupo_rel, [ref_x])
|
861
|
+
teacupo_goal2 = Core::Goal.new(teacupo_rel, [ref_x])
|
862
|
+
|
863
|
+
# Reasoned S2, frame 1:86
|
864
|
+
# (run* (x y)
|
865
|
+
# (teacupo x)
|
866
|
+
# (teacupo x)) ;; => ((tea _0)(cup _0))
|
867
|
+
instance = RunStarExpression.new(%w[x y], [teacupo_goal1, teacupo_goal2])
|
868
|
+
|
869
|
+
result = instance.run
|
870
|
+
expect(result.car).to eq(cons(tea, cons(any_value(0))))
|
871
|
+
expect(result.cdr.car).to eq(cons(cup, cons(any_value(0))))
|
872
|
+
end
|
873
|
+
|
874
|
+
it 'should solve expression with defrel and booleans' do
|
875
|
+
teacupo_goal1 = Core::Goal.new(teacupo_rel, [ref_x])
|
876
|
+
teacupo_goal2 = Core::Goal.new(teacupo_rel, [ref_x])
|
877
|
+
subgoal1 = conj2_goal(teacupo_goal1, teacupo_goal2)
|
878
|
+
expr3 = equals_goal(k_false, ref_x)
|
879
|
+
expr4 = Core::Goal.new(teacupo_rel, [ref_y])
|
880
|
+
subgoal2 = conj2_goal(expr3, expr4)
|
881
|
+
goal = disj2_goal(subgoal1, subgoal2)
|
882
|
+
# Reasoned S2, frame 1:87
|
883
|
+
# (run* (x y)
|
884
|
+
# (disj2
|
885
|
+
# (conj2 (teacupo x) (teacupo x))
|
886
|
+
# (conj2 (== #f x) (teacupo y)))) ;; => ((#f tea)(#f cup)(tea _0)(cup _0))
|
887
|
+
instance = RunStarExpression.new(%w[x y], goal)
|
888
|
+
|
889
|
+
result = instance.run
|
890
|
+
# Order of solutions differs from RS book
|
891
|
+
expect(result.car).to eq(cons(tea, cons(any_value(0))))
|
892
|
+
expect(result.cdr.car).to eq(cons(cup, cons(any_value(0))))
|
893
|
+
expect(result.cdr.cdr.car).to eq(cons(false, cons(tea)))
|
894
|
+
expect(result.cdr.cdr.cdr.car).to eq(cons(false, cons(cup)))
|
895
|
+
end
|
896
|
+
|
897
|
+
it 'should allow conde in the goal expression' do
|
898
|
+
teacupo_goal1 = Core::Goal.new(teacupo_rel, [ref_x])
|
899
|
+
teacupo_goal2 = Core::Goal.new(teacupo_rel, [ref_x])
|
900
|
+
expr3 = equals_goal(k_false, ref_x)
|
901
|
+
expr4 = Core::Goal.new(teacupo_rel, [ref_y])
|
902
|
+
goal = conde_goal([[teacupo_goal1, teacupo_goal2], [expr3, expr4]])
|
903
|
+
# Reasoned S2, frame 1:88
|
904
|
+
# (run* (x y)
|
905
|
+
# (conde
|
906
|
+
# ((teacupo x) (teacupo x))
|
907
|
+
# ((== #f x) (teacupo y)))) ;; => ((#f tea)(#f cup)(tea _0)(cup _0))
|
908
|
+
instance = RunStarExpression.new(%w[x y], goal)
|
909
|
+
|
910
|
+
result = instance.run
|
911
|
+
expect(result.car).to eq(cons(tea, cons(any_value(0))))
|
912
|
+
expect(result.cdr.car).to eq(cons(cup, cons(any_value(0))))
|
913
|
+
expect(result.cdr.cdr.car).to eq(cons(false, cons(tea)))
|
914
|
+
expect(result.cdr.cdr.cdr.car).to eq(cons(false, cons(cup)))
|
915
|
+
end
|
916
|
+
|
917
|
+
it 'should allow simplication of expressions (conde version)' do
|
918
|
+
expr1 = equals_goal(split, ref_x)
|
919
|
+
expr2 = equals_goal(pea, ref_y)
|
920
|
+
combo1 = [expr1, expr2]
|
921
|
+
expr3 = equals_goal(red, ref_x)
|
922
|
+
expr4 = equals_goal(bean, ref_y)
|
923
|
+
combo2 = [expr3, expr4]
|
924
|
+
goal = conde_goal([combo1, combo2])
|
925
|
+
instance = RunStarExpression.new(%w[x y], goal)
|
926
|
+
|
927
|
+
# Reasoned S2, frame 1:88 (second part, a rewrite of 1:76)
|
928
|
+
# (run* (x y)
|
929
|
+
# (conde
|
930
|
+
# ((== 'split x) (== 'pea y))
|
931
|
+
# ((== 'red x) (== 'bean y)))) ;; => ((split pea)(red bean))
|
932
|
+
result = instance.run
|
933
|
+
expect(result.car.car).to eq(split)
|
934
|
+
expect(result.car.cdr.car).to eq(pea)
|
935
|
+
expect(result.cdr.car.car).to eq(red)
|
936
|
+
expect(result.cdr.car.cdr.car).to eq(bean)
|
937
|
+
end
|
938
|
+
|
939
|
+
it 'should accept nesting of disj2 and conj2 (conde version)' do
|
940
|
+
equals_olive = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
|
941
|
+
combo = [equals_olive, fails]
|
942
|
+
equals_oil = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
|
943
|
+
goal = conde_goal([combo, equals_oil])
|
944
|
+
instance = RunStarExpression.new('x', goal)
|
945
|
+
|
946
|
+
# Reasoned S2, frame 1:89 (rewrite of 1:62)
|
947
|
+
# (run* x
|
948
|
+
# (conde
|
949
|
+
# ((== 'olive x) fail)
|
950
|
+
# ('oil x))) ;; => (oil)
|
951
|
+
result = instance.run
|
952
|
+
expect(result.car).to eq(oil)
|
953
|
+
end
|
954
|
+
|
955
|
+
it 'should accept nesting of conde inside a fresh context' do
|
956
|
+
equals_lentil = Core::Goal.new(Core::Equals.instance, [lentil, ref_z])
|
957
|
+
fresh_env = FreshEnv.new(['z'], equals_lentil)
|
958
|
+
equals_xy = Core::Goal.new(Core::Equals.instance, [ref_x, ref_y])
|
959
|
+
goal = conde_goal([fresh_env, equals_xy])
|
960
|
+
instance = RunStarExpression.new(%w[x y], goal)
|
961
|
+
fresh_env.parent = instance.env
|
962
|
+
|
963
|
+
# Reasoned S2, frame 1:90
|
964
|
+
# (run* (x y)
|
965
|
+
# (conde
|
966
|
+
# ((fresh (z)
|
967
|
+
# (== 'lentil z)))
|
968
|
+
# ((== x y)))) ;; => ((_0 _1)(_0 _0))
|
969
|
+
result = instance.run
|
970
|
+
expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
|
971
|
+
# Bug: next line fails
|
972
|
+
# expect(result.cdr.car).to eq(cons(any_value(0), cons(any_value(0))))
|
973
|
+
end
|
974
|
+
|
975
|
+
it 'accepts conde with more than two condition lines' do
|
976
|
+
expr1 = equals_goal(split, ref_x)
|
977
|
+
expr2 = equals_goal(pea, ref_y)
|
978
|
+
combo1 = [expr1, expr2]
|
979
|
+
expr3 = equals_goal(red, ref_x)
|
980
|
+
expr4 = equals_goal(bean, ref_y)
|
981
|
+
combo2 = [expr3, expr4]
|
982
|
+
expr5 = equals_goal(green, ref_x)
|
983
|
+
expr6 = equals_goal(lentil, ref_y)
|
984
|
+
combo3 = [expr5, expr6]
|
985
|
+
goal = conde_goal([combo1, combo2, combo3])
|
986
|
+
instance = RunStarExpression.new(%w[x y], goal)
|
987
|
+
|
988
|
+
# Reasoned S2, frame 1:91
|
989
|
+
# (run* (x y)
|
990
|
+
# (conde
|
991
|
+
# ((== 'split x) (== 'pea y))
|
992
|
+
# ((== 'red x) (== 'bean y))
|
993
|
+
# ((== 'green x) (== 'lentil y))))
|
994
|
+
# ;; => ((split pea)(red bean)(green lentil))
|
995
|
+
result = instance.run
|
996
|
+
expect(result.car).to eq(cons(split, cons(pea)))
|
997
|
+
expect(result.cdr.car).to eq(cons(red, cons(bean)))
|
998
|
+
expect(result.cdr.cdr.car).to eq(cons(green, cons(lentil)))
|
999
|
+
end
|
483
1000
|
end # context
|
484
1001
|
end # describe
|
485
1002
|
end # module
|