mini_kraken 0.1.11 → 0.1.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -3
- data/lib/mini_kraken/core/conde.rb +143 -0
- data/lib/mini_kraken/core/goal.rb +9 -3
- data/lib/mini_kraken/core/relation.rb +7 -0
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/core/conde_spec.rb +147 -0
- data/spec/glue/run_star_expression_spec.rb +108 -0
- data/spec/support/factory_methods.rb +7 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca30db7b93b0232f1df5321a1e00270395d908e3f7be1d24d3d5a9c19c833914
|
4
|
+
data.tar.gz: d64051987c0b09abf752b24604854515cabc9e99ff5ee436884c8522d6a75950
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4fc60b256907317972c51410e52aec821553d7575c24d59b9421f4ba8dbd0136937e70ffb2f9f21556ed205b8b8d54796af68c222df0704b45ebbcf562c3ab3
|
7
|
+
data.tar.gz: 5caf5af0c016e8dfb5de40244ad71a083eb035e0bcb911ae7bd90e67a784789458c12e46a3cc75a96ae483fd0317d86b6610a7c339cd423050b4d57e642d6cc9
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
|
-
## [0.1.
|
2
|
-
- Supports `
|
1
|
+
## [0.1.12] - 2020-06-29
|
2
|
+
- Supports `conde`, that is, a relation that can take an arbitrary number of arguments.
|
3
|
+
- Cover all frames but one from Chapter One of "Reasoned Scheme" book.
|
4
|
+
|
5
|
+
### New
|
6
|
+
- Class `Conde` a relation that succeeds for each of its successful arguments.
|
7
|
+
|
8
|
+
### CHANGED
|
9
|
+
- Method `Goal#validated_actuals` add into account polyadic relations (= relations with arbitrary number of arguments)
|
10
|
+
|
11
|
+
## [0.1.11] - 2020-06-25
|
12
|
+
- Supports `defrel`, that is, the capability to define new relations by combining other relations.
|
3
13
|
- Covers frames from "The Reasoned Scheme" book up to frame [1:87]
|
4
14
|
|
5
15
|
### New
|
@@ -13,7 +23,7 @@
|
|
13
23
|
### CHANGED
|
14
24
|
- File `README.md` minor change: added more TODO's.
|
15
25
|
|
16
|
-
## [0.1.10] - 2020-06-
|
26
|
+
## [0.1.10] - 2020-06-13
|
17
27
|
- Supports frames from "The Reasoned Scheme" book up to frame [1:81]
|
18
28
|
|
19
29
|
### New
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require_relative 'conj2'
|
5
|
+
require_relative 'duck_fiber'
|
6
|
+
require_relative 'fail'
|
7
|
+
require_relative 'goal'
|
8
|
+
require_relative 'goal_relation'
|
9
|
+
require_relative 'outcome'
|
10
|
+
|
11
|
+
unless MiniKraken::Core.constants(false).include? :Conde
|
12
|
+
module MiniKraken
|
13
|
+
module Core
|
14
|
+
# A polyadic relation (i.e. it can takes an arbitrary number of argumentt)
|
15
|
+
# that behaves as the disjunction of its arguments.
|
16
|
+
# It succeeds if at least one of its goal arguments succeeds.
|
17
|
+
class Conde < GoalRelation
|
18
|
+
include Singleton
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
super('conde', nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
# A relation is polyadic when it accepts an arbitrary number of arguments.
|
25
|
+
# @return [TrueClass]
|
26
|
+
def polyadic?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param actuals [Array<Term>] A two-elements array
|
31
|
+
# @param anEnv [Vocabulary] A vocabulary object
|
32
|
+
# @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
|
33
|
+
def solver_for(actuals, anEnv)
|
34
|
+
args = *validated_args(actuals)
|
35
|
+
Fiber.new { cond(args, anEnv) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Yields [Outcome, NilClass] result of the disjunction
|
39
|
+
# @param goals [Array<Goal>] Array of goals
|
40
|
+
# @param voc [Vocabulary] A vocabulary object
|
41
|
+
def cond(goals, voc)
|
42
|
+
# require 'debug'
|
43
|
+
success = false
|
44
|
+
|
45
|
+
goals.each do |g|
|
46
|
+
fiber = nil
|
47
|
+
|
48
|
+
case g
|
49
|
+
when Core::Goal
|
50
|
+
fiber = g.attain(voc)
|
51
|
+
when Core::Environment
|
52
|
+
fiber = g.attain(voc)
|
53
|
+
when Array
|
54
|
+
conjunct = conjunction(g)
|
55
|
+
fiber = conjunct.attain(voc)
|
56
|
+
when Core::ConsCell
|
57
|
+
goal_array = to_goal_array(g)
|
58
|
+
conjunct = conjunction(goal_array)
|
59
|
+
fiber = conjunct.attain(voc)
|
60
|
+
end
|
61
|
+
loop do
|
62
|
+
outcome = fiber.resume
|
63
|
+
break unless outcome
|
64
|
+
|
65
|
+
outcome.parent = voc unless outcome.parent
|
66
|
+
if outcome.successful?
|
67
|
+
success = true
|
68
|
+
Fiber.yield outcome
|
69
|
+
outcome.clear
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
Fiber.yield Outcome.new(:"#u", voc) unless success
|
75
|
+
Fiber.yield nil
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def validated_args(actuals)
|
81
|
+
result = []
|
82
|
+
|
83
|
+
actuals.each do |arg|
|
84
|
+
case arg
|
85
|
+
when Core::Goal
|
86
|
+
result << arg
|
87
|
+
|
88
|
+
when Core::Environment
|
89
|
+
result << arg
|
90
|
+
|
91
|
+
when Array
|
92
|
+
result << validated_args(arg)
|
93
|
+
|
94
|
+
else
|
95
|
+
prefix = "#{name} expects goal as argument, found a "
|
96
|
+
raise StandardError, prefix + "'#{arg.class}'"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
result
|
101
|
+
end
|
102
|
+
|
103
|
+
def conjunction(goal_array)
|
104
|
+
result = nil
|
105
|
+
|
106
|
+
loop do
|
107
|
+
conjunctions = []
|
108
|
+
goal_array.each_slice(2) do |uno_duo|
|
109
|
+
if uno_duo.size == 2
|
110
|
+
conjunctions << Core::Goal.new(Core::Conj2.instance, uno_duo)
|
111
|
+
else
|
112
|
+
conjunctions << uno_duo[0]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
if conjunctions.size == 1
|
116
|
+
result = conjunctions[0]
|
117
|
+
break
|
118
|
+
end
|
119
|
+
goal_array = conjunctions
|
120
|
+
end
|
121
|
+
|
122
|
+
result
|
123
|
+
end
|
124
|
+
|
125
|
+
def to_goal_array(aCons)
|
126
|
+
array = []
|
127
|
+
curr_node = aCons
|
128
|
+
loop do
|
129
|
+
array << curr_node.car if curr_node.car.kind_of?(Core::Goal)
|
130
|
+
break unless curr_node.cdr
|
131
|
+
break unless curr_node.car.kind_of?(Core::Goal)
|
132
|
+
|
133
|
+
curr_node = curr_node.cdr
|
134
|
+
end
|
135
|
+
|
136
|
+
array
|
137
|
+
end
|
138
|
+
end # class
|
139
|
+
|
140
|
+
Conde.instance.freeze
|
141
|
+
end # module
|
142
|
+
end # module
|
143
|
+
end # unless
|
@@ -30,14 +30,20 @@ module MiniKraken
|
|
30
30
|
private
|
31
31
|
|
32
32
|
def validated_actuals(args)
|
33
|
-
if args.size != relation.arity
|
33
|
+
if !relation.polyadic? && (args.size != relation.arity)
|
34
34
|
err_msg = "Goal has #{args.size} arguments, expected #{relation.arity}"
|
35
35
|
raise StandardError, err_msg
|
36
36
|
end
|
37
37
|
|
38
38
|
prefix = 'Invalid goal argument '
|
39
|
-
args.each do |
|
40
|
-
|
39
|
+
args.each do |actual|
|
40
|
+
if actual.kind_of?(GoalArg) || actual.kind_of?(Environment)
|
41
|
+
next
|
42
|
+
elsif actual.kind_of?(Array)
|
43
|
+
validated_actuals(actual)
|
44
|
+
else
|
45
|
+
raise StandardError, prefix + actual.to_s
|
46
|
+
end
|
41
47
|
end
|
42
48
|
|
43
49
|
args.dup
|
@@ -16,6 +16,13 @@ module MiniKraken
|
|
16
16
|
@alt_name = alternateName
|
17
17
|
end
|
18
18
|
|
19
|
+
# A relation is polyadic when it accepts an arbitrary number of arguments.
|
20
|
+
# Most built-in relation takes a fixed number of arguments (= arity).
|
21
|
+
# @return [Boolean]
|
22
|
+
def polyadic?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
19
26
|
# Number of arguments for the relation.
|
20
27
|
# @return [Integer]
|
21
28
|
def arity
|
data/lib/mini_kraken/version.rb
CHANGED
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../spec_helper' # Use the RSpec framework
|
4
|
+
require_relative '../../lib/mini_kraken/core/k_symbol'
|
5
|
+
require_relative '../../lib/mini_kraken/core/fail'
|
6
|
+
require_relative '../../lib/mini_kraken/core/succeed'
|
7
|
+
require_relative '../../lib/mini_kraken/core/equals'
|
8
|
+
require_relative '../../lib/mini_kraken/core/environment'
|
9
|
+
require_relative '../../lib/mini_kraken/core/variable'
|
10
|
+
require_relative '../../lib/mini_kraken/core/variable_ref'
|
11
|
+
|
12
|
+
# Load the class under test
|
13
|
+
require_relative '../../lib/mini_kraken/core/conde'
|
14
|
+
|
15
|
+
module MiniKraken
|
16
|
+
module Core
|
17
|
+
describe Conde do
|
18
|
+
subject { Conde.instance }
|
19
|
+
|
20
|
+
context 'Initialization:' do
|
21
|
+
it 'should be initialized without argument' do
|
22
|
+
expect { Conde.instance }.not_to raise_error
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should know its name' do
|
26
|
+
expect(subject.name).to eq('conde')
|
27
|
+
end
|
28
|
+
end # context
|
29
|
+
|
30
|
+
context 'Provided services:' do
|
31
|
+
let(:bean) { KSymbol.new(:bean) }
|
32
|
+
let(:corn) { KSymbol.new(:corn) }
|
33
|
+
let(:meal) { KSymbol.new(:meal) }
|
34
|
+
let(:oil) { KSymbol.new(:oil) }
|
35
|
+
let(:olive) { KSymbol.new(:olive) }
|
36
|
+
let(:pea) { KSymbol.new(:pea) }
|
37
|
+
let(:red) { KSymbol.new(:red) }
|
38
|
+
let(:split) { KSymbol.new(:split) }
|
39
|
+
let(:fails) { Goal.new(Fail.instance, []) }
|
40
|
+
let(:succeeds) { Goal.new(Succeed.instance, []) }
|
41
|
+
let(:var_q) { Variable.new('q') }
|
42
|
+
let(:var_x) { Variable.new('x') }
|
43
|
+
let(:var_y) { Variable.new('y') }
|
44
|
+
let(:ref_q) { VariableRef.new('q') }
|
45
|
+
let(:ref_x) { VariableRef.new('x') }
|
46
|
+
let(:ref_y) { VariableRef.new('y') }
|
47
|
+
let(:env) do
|
48
|
+
e = Environment.new
|
49
|
+
e.add_var(var_q)
|
50
|
+
e.add_var(var_x)
|
51
|
+
e.add_var(var_y)
|
52
|
+
e
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should complain when one of its argument is not a goal' do
|
56
|
+
err = StandardError
|
57
|
+
expect { subject.solver_for([succeeds, pea], env) }.to raise_error(err)
|
58
|
+
expect { subject.solver_for([pea, succeeds], env) }.to raise_error(err)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should fails if when all goals fail' do
|
62
|
+
solver = subject.solver_for([fails, fails, fails], env)
|
63
|
+
expect(solver.resume).not_to be_successful
|
64
|
+
expect(solver.resume).to be_nil
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'yield success if first argument succeeds' do
|
68
|
+
subgoal = Goal.new(Equals.instance, [olive, ref_q])
|
69
|
+
solver = subject.solver_for([subgoal, fails, fails], env)
|
70
|
+
outcome = solver.resume
|
71
|
+
expect(outcome).to be_successful
|
72
|
+
expect(outcome.associations['q'].first.value).to eq(olive)
|
73
|
+
expect(solver.resume).to be_nil
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'yield success if second argument succeeds' do
|
77
|
+
subgoal = Goal.new(Equals.instance, [oil, ref_q])
|
78
|
+
solver = subject.solver_for([fails, subgoal, fails], env)
|
79
|
+
outcome = solver.resume
|
80
|
+
expect(outcome).to be_successful
|
81
|
+
expect(outcome.associations['q'].first.value).to eq(oil)
|
82
|
+
expect(solver.resume).to be_nil
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'yield success if third argument succeeds' do
|
86
|
+
subgoal = Goal.new(Equals.instance, [oil, ref_q])
|
87
|
+
solver = subject.solver_for([fails, fails, subgoal], env)
|
88
|
+
outcome = solver.resume
|
89
|
+
expect(outcome).to be_successful
|
90
|
+
expect(outcome.associations['q'].first.value).to eq(oil)
|
91
|
+
expect(solver.resume).to be_nil
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'yields three solutions if three goals succeed' do
|
95
|
+
# Covers frame 1:58
|
96
|
+
subgoal1 = Goal.new(Equals.instance, [olive, ref_q])
|
97
|
+
subgoal2 = Goal.new(Equals.instance, [oil, ref_q])
|
98
|
+
subgoal3 = Goal.new(Equals.instance, [pea, ref_q])
|
99
|
+
solver = subject.solver_for([subgoal1, subgoal2, subgoal3, fails], env)
|
100
|
+
|
101
|
+
# First solution
|
102
|
+
outcome1 = solver.resume
|
103
|
+
expect(outcome1).to be_successful
|
104
|
+
expect(outcome1.associations['q'].first.value).to eq(olive)
|
105
|
+
|
106
|
+
# Second solution
|
107
|
+
outcome2 = solver.resume
|
108
|
+
expect(outcome2).to be_successful
|
109
|
+
expect(outcome2.associations['q'].first.value).to eq(oil)
|
110
|
+
|
111
|
+
# Third solution
|
112
|
+
outcome3 = solver.resume
|
113
|
+
expect(outcome3).to be_successful
|
114
|
+
expect(outcome3.associations['q'].first.value).to eq(pea)
|
115
|
+
|
116
|
+
expect(solver.resume).to be_nil
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'also use conjunctions for nested goals' do
|
120
|
+
# Covers frame 1:88
|
121
|
+
subgoal1 = Goal.new(Equals.instance, [split, ref_x])
|
122
|
+
subgoal2 = Goal.new(Equals.instance, [pea, ref_y])
|
123
|
+
combo1 = [subgoal1, subgoal2]
|
124
|
+
|
125
|
+
subgoal3 = Goal.new(Equals.instance, [red, ref_x])
|
126
|
+
subgoal4 = Goal.new(Equals.instance, [bean, ref_y])
|
127
|
+
combo2 = [subgoal3, subgoal4]
|
128
|
+
solver = subject.solver_for([combo1, combo2], env)
|
129
|
+
|
130
|
+
# First solution
|
131
|
+
outcome1 = solver.resume
|
132
|
+
expect(outcome1).to be_successful
|
133
|
+
expect(outcome1.associations['x'].first.value).to eq(split)
|
134
|
+
expect(outcome1.associations['y'].first.value).to eq(pea)
|
135
|
+
|
136
|
+
# Second solution
|
137
|
+
outcome2 = solver.resume
|
138
|
+
expect(outcome2).to be_successful
|
139
|
+
expect(outcome2.associations['x'].first.value).to eq(red)
|
140
|
+
expect(outcome2.associations['y'].first.value).to eq(bean)
|
141
|
+
|
142
|
+
expect(solver.resume).to be_nil
|
143
|
+
end
|
144
|
+
end # context
|
145
|
+
end # describe
|
146
|
+
end # module
|
147
|
+
end # module
|
@@ -2,6 +2,7 @@
|
|
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'
|
6
7
|
require_relative '../../lib/mini_kraken/core/def_relation'
|
7
8
|
require_relative '../../lib/mini_kraken/core/disj2'
|
@@ -58,6 +59,8 @@ module MiniKraken
|
|
58
59
|
let(:bean) { k_symbol(:bean) }
|
59
60
|
let(:corn) { k_symbol(:corn) }
|
60
61
|
let(:cup) { k_symbol(:cup) }
|
62
|
+
let(:green) { k_symbol(:green) }
|
63
|
+
let(:lentil) { k_symbol(:lentil) }
|
61
64
|
let(:meal) { k_symbol(:meal) }
|
62
65
|
let(:oil) { k_symbol(:oil) }
|
63
66
|
let(:olive) { k_symbol(:olive) }
|
@@ -70,6 +73,7 @@ module MiniKraken
|
|
70
73
|
let(:ref_r) { Core::VariableRef.new('r') }
|
71
74
|
let(:ref_x) { Core::VariableRef.new('x') }
|
72
75
|
let(:ref_y) { Core::VariableRef.new('y') }
|
76
|
+
let(:ref_z) { Core::VariableRef.new('z') }
|
73
77
|
let(:ref_s) { Core::VariableRef.new('s') }
|
74
78
|
let(:ref_t) { Core::VariableRef.new('t') }
|
75
79
|
let(:ref_u) { Core::VariableRef.new('u') }
|
@@ -889,6 +893,110 @@ module MiniKraken
|
|
889
893
|
expect(result.cdr.cdr.car).to eq(cons(false, cons(tea)))
|
890
894
|
expect(result.cdr.cdr.cdr.car).to eq(cons(false, cons(cup)))
|
891
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
|
892
1000
|
end # context
|
893
1001
|
end # describe
|
894
1002
|
end # module
|
@@ -27,6 +27,13 @@ module MiniKraken
|
|
27
27
|
Core::ConsCell.new(obj1, obj2)
|
28
28
|
end
|
29
29
|
|
30
|
+
# Factory method for constructing a goal using the Equals relation.
|
31
|
+
# @param args [Array<Core::Goal>]
|
32
|
+
# @return [Core::Goal]
|
33
|
+
def conde_goal(args)
|
34
|
+
Core::Goal.new(Core::Conde.instance, args)
|
35
|
+
end
|
36
|
+
|
30
37
|
# Factory method for constructing a goal using the Equals relation.
|
31
38
|
# @param arg1 [Term]
|
32
39
|
# @param arg2 [Term]
|
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.
|
4
|
+
version: 0.1.12
|
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-06-
|
11
|
+
date: 2020-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -76,6 +76,7 @@ files:
|
|
76
76
|
- lib/mini_kraken/core/binary_relation.rb
|
77
77
|
- lib/mini_kraken/core/composite_goal.rb
|
78
78
|
- lib/mini_kraken/core/composite_term.rb
|
79
|
+
- lib/mini_kraken/core/conde.rb
|
79
80
|
- lib/mini_kraken/core/conj2.rb
|
80
81
|
- lib/mini_kraken/core/cons_cell.rb
|
81
82
|
- lib/mini_kraken/core/def_relation.rb
|
@@ -109,6 +110,7 @@ files:
|
|
109
110
|
- mini_kraken.gemspec
|
110
111
|
- spec/core/association_spec.rb
|
111
112
|
- spec/core/association_walker_spec.rb
|
113
|
+
- spec/core/conde_spec.rb
|
112
114
|
- spec/core/conj2_spec.rb
|
113
115
|
- spec/core/cons_cell_spec.rb
|
114
116
|
- spec/core/def_relation_spec.rb
|
@@ -158,6 +160,7 @@ summary: Implementation of Minikanren language in Ruby. WIP
|
|
158
160
|
test_files:
|
159
161
|
- spec/core/association_spec.rb
|
160
162
|
- spec/core/association_walker_spec.rb
|
163
|
+
- spec/core/conde_spec.rb
|
161
164
|
- spec/core/conj2_spec.rb
|
162
165
|
- spec/core/cons_cell_spec.rb
|
163
166
|
- spec/core/def_relation_spec.rb
|