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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/README.md +11 -3
  4. data/lib/mini_kraken/core/association_walker.rb +1 -1
  5. data/lib/mini_kraken/core/base_arg.rb +10 -0
  6. data/lib/mini_kraken/core/conde.rb +143 -0
  7. data/lib/mini_kraken/core/conj2.rb +59 -53
  8. data/lib/mini_kraken/core/def_relation.rb +49 -0
  9. data/lib/mini_kraken/core/disj2.rb +53 -48
  10. data/lib/mini_kraken/core/duck_fiber.rb +1 -1
  11. data/lib/mini_kraken/core/equals.rb +134 -126
  12. data/lib/mini_kraken/core/fail.rb +18 -14
  13. data/lib/mini_kraken/core/formal_arg.rb +22 -0
  14. data/lib/mini_kraken/core/formal_ref.rb +24 -0
  15. data/lib/mini_kraken/core/goal.rb +9 -3
  16. data/lib/mini_kraken/core/goal_arg.rb +5 -3
  17. data/lib/mini_kraken/core/goal_template.rb +60 -0
  18. data/lib/mini_kraken/core/k_boolean.rb +31 -0
  19. data/lib/mini_kraken/core/outcome.rb +14 -0
  20. data/lib/mini_kraken/core/relation.rb +7 -0
  21. data/lib/mini_kraken/core/succeed.rb +17 -13
  22. data/lib/mini_kraken/core/vocabulary.rb +22 -4
  23. data/lib/mini_kraken/glue/fresh_env.rb +45 -3
  24. data/lib/mini_kraken/glue/run_star_expression.rb +43 -26
  25. data/lib/mini_kraken/version.rb +1 -1
  26. data/spec/core/conde_spec.rb +147 -0
  27. data/spec/core/conj2_spec.rb +8 -9
  28. data/spec/core/def_relation_spec.rb +96 -0
  29. data/spec/core/disj2_spec.rb +0 -45
  30. data/spec/core/duck_fiber_spec.rb +12 -1
  31. data/spec/core/equals_spec.rb +3 -3
  32. data/spec/core/goal_template_spec.rb +74 -0
  33. data/spec/core/k_boolean_spec.rb +107 -0
  34. data/spec/core/outcome_spec.rb +48 -0
  35. data/spec/core/vocabulary_spec.rb +6 -0
  36. data/spec/glue/fresh_env_spec.rb +27 -1
  37. data/spec/glue/run_star_expression_spec.rb +535 -18
  38. data/spec/mini_kraken_spec.rb +2 -0
  39. data/spec/spec_helper.rb +0 -1
  40. data/spec/support/factory_methods.rb +15 -0
  41. metadata +19 -2
@@ -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 'should be initialized with an array of names' do
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 'should be initialized with a name and a goal' do
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.var.name).to eq('q')
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(:fails) { Core::Goal.new(Core::Fail.instance, []) }
53
- let(:succeeds) { Core::Goal.new(Core::Succeed.instance, []) }
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
- success = Core::Goal.new(Core::Succeed.instance, [])
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
- # it 'should support conjunction of one succeed and a successful goal' do
331
- # subgoal = equals_goal(corn, ref_q)
332
- # goal = conj2_goal(succeeds, subgoal)
333
- # instance = RunStarExpression.new('q', goal)
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
- # # Reasoned S2, frame 1:51
336
- # # (run* q (conj2 succeed (== 'corn q)) ;; => ('corn)
337
- # result = instance.run
338
- # expect(result.car).to eq(corn)
339
- # end
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 (equals 'olive q) fail)) ;; => ('olive)
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 (equals 'oil q)) ;; => (oil)
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 (equals 'olive q) (equals 'oil q)) ;; => (olive oil)
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