mini_kraken 0.1.11 → 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 +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
|