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 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