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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 672521fbc6627e2767bfe4d6ce38dd66243736fee451e857ec7ef45d0f8905fe
4
- data.tar.gz: 6835bb1d91bb6e867417faba0d6904734295f9a7bab612c6a78fcfbcdc41f84a
3
+ metadata.gz: ca30db7b93b0232f1df5321a1e00270395d908e3f7be1d24d3d5a9c19c833914
4
+ data.tar.gz: d64051987c0b09abf752b24604854515cabc9e99ff5ee436884c8522d6a75950
5
5
  SHA512:
6
- metadata.gz: bd5b5b2d2493b4ec8f8293c5f786be1a7739d05bee5d4733d48edb16713d1df4097be5a6e85127c2c90cf395da877b57ef07a607167b8f5bb1732dbc2a367610
7
- data.tar.gz: 6661aadbe8bd778283e91f31181651e32296150d282575697fee1f5c240ea46168c5107baf33d9b590d6a449ec89b2f41e69340b81cb9b9e3692954af155083b
6
+ metadata.gz: f4fc60b256907317972c51410e52aec821553d7575c24d59b9421f4ba8dbd0136937e70ffb2f9f21556ed205b8b8d54796af68c222df0704b45ebbcf562c3ab3
7
+ data.tar.gz: 5caf5af0c016e8dfb5de40244ad71a083eb035e0bcb911ae7bd90e67a784789458c12e46a3cc75a96ae483fd0317d86b6610a7c339cd423050b4d57e642d6cc9
@@ -1,5 +1,15 @@
1
- ## [0.1.11] - 2020-06-10
2
- - Supports `defrel`, taht is, the capability to define new relations by combining other relations.
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-10
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 |actl|
40
- raise StandardError, prefix + actl.to_s unless actl.kind_of?(GoalArg)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniKraken
4
- VERSION = '0.1.11'
4
+ VERSION = '0.1.12'
5
5
  end
@@ -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.11
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-25 00:00:00.000000000 Z
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