mini_kraken 0.2.00 → 0.2.01
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +334 -0
- data/CHANGELOG.md +9 -0
- data/README.md +13 -11
- data/lib/mini_kraken/core/atomic_term.rb +1 -0
- data/lib/mini_kraken/core/cons_cell.rb +1 -0
- data/lib/mini_kraken/core/def_relation.rb +1 -0
- data/lib/mini_kraken/core/environment.rb +1 -1
- data/lib/mini_kraken/core/equals.rb +2 -5
- data/lib/mini_kraken/core/formal_ref.rb +1 -0
- data/lib/mini_kraken/core/goal.rb +3 -2
- data/lib/mini_kraken/core/goal_template.rb +3 -1
- data/lib/mini_kraken/core/k_symbol.rb +1 -1
- data/lib/mini_kraken/core/variable_ref.rb +1 -0
- data/lib/mini_kraken/glue/dsl.rb +115 -9
- data/lib/mini_kraken/glue/run_star_expression.rb +1 -3
- data/lib/mini_kraken/version.rb +1 -1
- data/mini_kraken.gemspec +6 -3
- data/spec/.rubocop.yml +13 -0
- data/spec/core/duck_fiber_spec.rb +1 -1
- data/spec/core/k_symbol_spec.rb +1 -1
- data/spec/glue/dsl_chap1_spec.rb +133 -37
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f51855ac3179a111a4ace328340402fa167818773086076abf90ed845c31d248
|
4
|
+
data.tar.gz: f6bde0d3d7b872461d7cb0b087eaab821240f14317445e95f4d832d09af51e2f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae6319164a2f6212bdbdea35dd5b5befa86648b685063dea79d220b70e5852c3e8b343d2d37ebfe6584aa3966b44174e91df2866d63e03a219a0008a5b382d66
|
7
|
+
data.tar.gz: 70eb204ab842ea45527bd5ca8661dcd98081422cae4d6848d220bc70e080da94b3b7078f3602f1143535821a9894309b9bec39ec5f4d6c60edcfd21cfa5c8f4f
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,334 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- 'exp/**/*'
|
4
|
+
|
5
|
+
Layout/ArgumentAlignment:
|
6
|
+
Enabled: false
|
7
|
+
|
8
|
+
Layout/ArrayAlignment:
|
9
|
+
Enabled: true
|
10
|
+
EnforcedStyle: with_fixed_indentation
|
11
|
+
|
12
|
+
Layout/CaseIndentation:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Layout/ClosingHeredocIndentation:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Layout/CommentIndentation:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Layout/ElseAlignment:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Layout/EmptyLines:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Layout/EndAlignment:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Layout/EndOfLine:
|
31
|
+
Enabled: true
|
32
|
+
EnforcedStyle: lf
|
33
|
+
|
34
|
+
Layout/FirstArgumentIndentation:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Layout/IndentationWidth:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Layout/IndentationConsistency:
|
41
|
+
Enabled: true
|
42
|
+
|
43
|
+
Layout/HeredocIndentation:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Layout/MultilineHashBraceLayout:
|
47
|
+
Enabled: true
|
48
|
+
|
49
|
+
Layout/MultilineMethodCallBraceLayout:
|
50
|
+
Enabled: true
|
51
|
+
EnforcedStyle: same_line
|
52
|
+
|
53
|
+
Layout/SpaceAroundOperators:
|
54
|
+
Enabled: true
|
55
|
+
|
56
|
+
Layout/SpaceInsideParens:
|
57
|
+
Enabled: true
|
58
|
+
|
59
|
+
Layout/IndentationStyle:
|
60
|
+
Enabled: true
|
61
|
+
|
62
|
+
Layout/SpaceAroundMethodCallOperator:
|
63
|
+
Enabled: true
|
64
|
+
|
65
|
+
Layout/TrailingEmptyLines:
|
66
|
+
Enabled: true
|
67
|
+
|
68
|
+
Layout/TrailingWhitespace:
|
69
|
+
Enabled: true
|
70
|
+
|
71
|
+
Lint/Loop:
|
72
|
+
Enabled: true
|
73
|
+
|
74
|
+
Lint/RaiseException:
|
75
|
+
Enabled: true
|
76
|
+
|
77
|
+
Lint/RescueException:
|
78
|
+
Enabled: true
|
79
|
+
|
80
|
+
Lint/StructNewOverride:
|
81
|
+
Enabled: true
|
82
|
+
|
83
|
+
Lint/UnusedMethodArgument:
|
84
|
+
Enabled: true
|
85
|
+
|
86
|
+
Lint/UselessAccessModifier:
|
87
|
+
Enabled: true
|
88
|
+
|
89
|
+
Lint/Void:
|
90
|
+
Enabled: false
|
91
|
+
|
92
|
+
Lint/UselessAssignment:
|
93
|
+
Enabled: true
|
94
|
+
|
95
|
+
Metrics/AbcSize:
|
96
|
+
Enabled: false
|
97
|
+
|
98
|
+
Metrics/BlockLength:
|
99
|
+
Enabled: true
|
100
|
+
Max: 350
|
101
|
+
|
102
|
+
Metrics/BlockNesting:
|
103
|
+
Enabled: true
|
104
|
+
Max: 5
|
105
|
+
|
106
|
+
Metrics/ClassLength:
|
107
|
+
Enabled: true
|
108
|
+
Max: 350
|
109
|
+
|
110
|
+
Metrics/CyclomaticComplexity:
|
111
|
+
Enabled: false
|
112
|
+
|
113
|
+
Layout/LineLength:
|
114
|
+
Enabled: false
|
115
|
+
Max: 90
|
116
|
+
|
117
|
+
Metrics/MethodLength:
|
118
|
+
Enabled: true
|
119
|
+
Max: 50
|
120
|
+
|
121
|
+
Metrics/ModuleLength:
|
122
|
+
Enabled: true
|
123
|
+
Max: 500
|
124
|
+
|
125
|
+
Metrics/PerceivedComplexity:
|
126
|
+
Enabled: false
|
127
|
+
|
128
|
+
Naming/ConstantName:
|
129
|
+
Enabled: false
|
130
|
+
|
131
|
+
Naming/ClassAndModuleCamelCase:
|
132
|
+
Enabled: false
|
133
|
+
|
134
|
+
Naming/BlockParameterName:
|
135
|
+
Enabled: true
|
136
|
+
|
137
|
+
Naming/MethodParameterName:
|
138
|
+
Enabled: false
|
139
|
+
|
140
|
+
Naming/VariableName:
|
141
|
+
Enabled: false
|
142
|
+
|
143
|
+
Style/Alias:
|
144
|
+
Enabled: true
|
145
|
+
|
146
|
+
Layout/HashAlignment:
|
147
|
+
Enabled: false
|
148
|
+
|
149
|
+
Style/AsciiComments:
|
150
|
+
Enabled: false
|
151
|
+
|
152
|
+
Style/BarePercentLiterals:
|
153
|
+
Enabled: false
|
154
|
+
|
155
|
+
Style/BlockComments:
|
156
|
+
Enabled: false
|
157
|
+
|
158
|
+
Style/CharacterLiteral:
|
159
|
+
Enabled: false
|
160
|
+
|
161
|
+
Style/ClassCheck:
|
162
|
+
Enabled: false
|
163
|
+
|
164
|
+
Style/ClassVars:
|
165
|
+
Enabled: false
|
166
|
+
|
167
|
+
Style/ColonMethodCall:
|
168
|
+
Enabled: false
|
169
|
+
|
170
|
+
Style/CommentAnnotation:
|
171
|
+
Enabled: false
|
172
|
+
|
173
|
+
Style/CommentedKeyword:
|
174
|
+
Enabled: false
|
175
|
+
|
176
|
+
Style/ConditionalAssignment:
|
177
|
+
Enabled: false
|
178
|
+
|
179
|
+
Style/DefWithParentheses:
|
180
|
+
Enabled: true
|
181
|
+
|
182
|
+
Style/Documentation:
|
183
|
+
Enabled: false
|
184
|
+
|
185
|
+
Style/ExpandPathArguments:
|
186
|
+
Enabled: false
|
187
|
+
|
188
|
+
Style/ExponentialNotation:
|
189
|
+
Enabled: true
|
190
|
+
|
191
|
+
Style/GuardClause:
|
192
|
+
Enabled: false
|
193
|
+
|
194
|
+
Style/HashEachMethods:
|
195
|
+
Enabled: true
|
196
|
+
|
197
|
+
Style/HashTransformKeys:
|
198
|
+
Enabled: true
|
199
|
+
|
200
|
+
Style/HashTransformValues:
|
201
|
+
Enabled: true
|
202
|
+
|
203
|
+
Style/IfUnlessModifier:
|
204
|
+
Enabled: false
|
205
|
+
|
206
|
+
Style/InverseMethods:
|
207
|
+
Enabled: true
|
208
|
+
|
209
|
+
Style/MissingRespondToMissing:
|
210
|
+
Enabled: false
|
211
|
+
|
212
|
+
Style/Next:
|
213
|
+
Enabled: false
|
214
|
+
|
215
|
+
Style/NumericLiterals:
|
216
|
+
Enabled: false
|
217
|
+
|
218
|
+
Style/RaiseArgs:
|
219
|
+
Enabled: true
|
220
|
+
|
221
|
+
Style/RedundantReturn:
|
222
|
+
Enabled: false
|
223
|
+
|
224
|
+
Style/RedundantSelf:
|
225
|
+
Enabled: true
|
226
|
+
|
227
|
+
Style/RegexpLiteral:
|
228
|
+
Enabled: false
|
229
|
+
|
230
|
+
Style/PercentLiteralDelimiters:
|
231
|
+
Enabled: false
|
232
|
+
|
233
|
+
Style/StderrPuts:
|
234
|
+
Enabled: false
|
235
|
+
|
236
|
+
Style/StringLiterals:
|
237
|
+
Enabled: true
|
238
|
+
|
239
|
+
Style/TernaryParentheses:
|
240
|
+
Enabled: false
|
241
|
+
|
242
|
+
Style/UnlessElse:
|
243
|
+
Enabled: false
|
244
|
+
|
245
|
+
# Rubocop complains when it doesn't find an explicit setting for the following cops:
|
246
|
+
Layout/EmptyLinesAroundAttributeAccessor:
|
247
|
+
Enabled: true
|
248
|
+
|
249
|
+
Lint/BinaryOperatorWithIdenticalOperands:
|
250
|
+
Enabled: true
|
251
|
+
|
252
|
+
Lint/DeprecatedOpenSSLConstant:
|
253
|
+
Enabled: true
|
254
|
+
|
255
|
+
Lint/DuplicateElsifCondition:
|
256
|
+
Enabled: true
|
257
|
+
|
258
|
+
Lint/DuplicateRescueException:
|
259
|
+
Enabled: true
|
260
|
+
|
261
|
+
Lint/EmptyConditionalBody:
|
262
|
+
Enabled: true
|
263
|
+
|
264
|
+
Lint/FloatComparison:
|
265
|
+
Enabled: true
|
266
|
+
|
267
|
+
Lint/MissingSuper:
|
268
|
+
Enabled: true
|
269
|
+
|
270
|
+
Lint/MixedRegexpCaptureTypes:
|
271
|
+
Enabled: true
|
272
|
+
|
273
|
+
Lint/OutOfRangeRegexpRef:
|
274
|
+
Enabled: true
|
275
|
+
|
276
|
+
Lint/SelfAssignment:
|
277
|
+
Enabled: true
|
278
|
+
|
279
|
+
Lint/TopLevelReturnWithArgument:
|
280
|
+
Enabled: true
|
281
|
+
|
282
|
+
Lint/UnreachableLoop:
|
283
|
+
Enabled: true
|
284
|
+
|
285
|
+
Style/AccessorGrouping:
|
286
|
+
Enabled: true
|
287
|
+
|
288
|
+
Style/ArrayCoercion:
|
289
|
+
Enabled: true
|
290
|
+
|
291
|
+
Style/BisectedAttrAccessor:
|
292
|
+
Enabled: true
|
293
|
+
|
294
|
+
Style/CaseLikeIf:
|
295
|
+
Enabled: true
|
296
|
+
|
297
|
+
Style/ExplicitBlockArgument:
|
298
|
+
Enabled: true
|
299
|
+
|
300
|
+
Style/GlobalStdStream:
|
301
|
+
Enabled: true
|
302
|
+
|
303
|
+
Style/HashAsLastArrayItem:
|
304
|
+
Enabled: true
|
305
|
+
|
306
|
+
Style/HashLikeCase:
|
307
|
+
Enabled: true
|
308
|
+
|
309
|
+
Style/OptionalBooleanParameter:
|
310
|
+
Enabled: true
|
311
|
+
|
312
|
+
Style/RedundantAssignment:
|
313
|
+
Enabled: true
|
314
|
+
|
315
|
+
Style/RedundantFetchBlock:
|
316
|
+
Enabled: true
|
317
|
+
|
318
|
+
Style/RedundantFileExtensionInRequire:
|
319
|
+
Enabled: true
|
320
|
+
|
321
|
+
Style/RedundantRegexpCharacterClass:
|
322
|
+
Enabled: true
|
323
|
+
|
324
|
+
Style/RedundantRegexpEscape:
|
325
|
+
Enabled: true
|
326
|
+
|
327
|
+
Style/SingleArgumentDig:
|
328
|
+
Enabled: true
|
329
|
+
|
330
|
+
Style/SlicingWithRange:
|
331
|
+
Enabled: true
|
332
|
+
|
333
|
+
Style/StringConcatenation:
|
334
|
+
Enabled: true
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## [0.2.01] - 2020-08-07
|
2
|
+
- The DSL (Domain Specific Language) now supports `defrel` and boolean literals.
|
3
|
+
|
4
|
+
### CHANGED
|
5
|
+
- Constructor `DefRelation#initialize` now freezes any new class instance.
|
6
|
+
- Constructor `GoalTemplate#initialize` now freezes any new class instance.
|
7
|
+
- Mixin module `Core::DSL` new method `defrel` to build custom relations.
|
8
|
+
- File `.rubocop.yml` to please Rubocop 0.89
|
9
|
+
|
1
10
|
## [0.2.00] - 2020-07-12
|
2
11
|
- First release of DSL (Domain Specific Language)
|
3
12
|
- Fix defect for fused variables that remain fresh
|
data/README.md
CHANGED
@@ -30,12 +30,12 @@ ISBN: 9780262535519, (2018), MIT Press.
|
|
30
30
|
- [ ] Occurs check
|
31
31
|
|
32
32
|
List-centric relations from Chapter 2
|
33
|
-
- [ ] caro
|
34
|
-
- [ ] cdro
|
35
|
-
- [ ] conso
|
36
|
-
- [ ] nullo
|
37
|
-
- [ ] pairo
|
38
|
-
- [ ] singletono
|
33
|
+
- [ ] caro
|
34
|
+
- [ ] cdro
|
35
|
+
- [ ] conso
|
36
|
+
- [ ] nullo
|
37
|
+
- [ ] pairo
|
38
|
+
- [ ] singletono
|
39
39
|
|
40
40
|
## Installation
|
41
41
|
|
@@ -73,9 +73,9 @@ The two first lines in the above code snippet are pretty standard:
|
|
73
73
|
- The first line loads the `mini_kraken` library.
|
74
74
|
- The second line add the DSL methods to the current object.
|
75
75
|
|
76
|
-
The next line constitutes a trivial `miniKanren` program.
|
77
|
-
The aim of a `miniKanren` program is to find one or more solutions involving
|
78
|
-
and satisfying one or more goals.
|
76
|
+
The next line constitutes a trivial `miniKanren` program.
|
77
|
+
The aim of a `miniKanren` program is to find one or more solutions involving the given logical variable(s)
|
78
|
+
and satisfying one or more goals to the `run_star method.
|
79
79
|
In our example, the `run_star` method instructs `MiniKraken` to find all solutions,
|
80
80
|
knowing that each successful solution:
|
81
81
|
- binds a value to the provided variable `q` and
|
@@ -101,7 +101,8 @@ So the above program succeeds and the only found solution is obtained by binding
|
|
101
101
|
puts result # => ()
|
102
102
|
```
|
103
103
|
In this example, we learn that `run_star` can take multiple goals placed in an array.
|
104
|
-
The program fails to find a solution since it is not possible to satisfy the two `equals` goals simultaneously.
|
104
|
+
The program fails to find a solution since it is not possible to satisfy the two `equals` goals simultaneously.
|
105
|
+
In case of failure, the `run_star` returns an empty list represented as `()` in the output.
|
105
106
|
|
106
107
|
|
107
108
|
### Example 3
|
@@ -127,7 +128,8 @@ This time, `run_star` takes two logical variables -`x` and `y`- and successfully
|
|
127
128
|
puts result # => ((:blue :sea) (:blue :mountain) (:red :sea) (:red :mountain))
|
128
129
|
```
|
129
130
|
|
130
|
-
Here, `run_star` takes two logical variables and two `disj2` goals.
|
131
|
+
Here, `run_star` takes two logical variables and two `disj2` goals.
|
132
|
+
A `disj2` succeeds if any of its arguments succeeds.
|
131
133
|
This program finds four distinct solutions for x, y pairs.
|
132
134
|
|
133
135
|
## Development
|
@@ -38,10 +38,7 @@ unless MiniKraken::Core.constants(false).include? :Equals
|
|
38
38
|
return result
|
39
39
|
end
|
40
40
|
new_arg1, new_arg2 = commute_cond(arg1, arg2, anEnv)
|
41
|
-
|
42
|
-
# anEnv.merge(result) if result.successful? && !result.association.empty?
|
43
|
-
|
44
|
-
result
|
41
|
+
do_unification(new_arg1, new_arg2, anEnv)
|
45
42
|
end
|
46
43
|
|
47
44
|
private
|
@@ -141,7 +138,7 @@ unless MiniKraken::Core.constants(false).include? :Equals
|
|
141
138
|
total_success = subresults.all?(&:successful?)
|
142
139
|
if total_success
|
143
140
|
memo = Outcome.success(anEnv)
|
144
|
-
|
141
|
+
subresults.reduce(memo) do |sub_total, outcome|
|
145
142
|
sub_total.merge(outcome)
|
146
143
|
sub_total
|
147
144
|
end
|
@@ -16,6 +16,7 @@ module MiniKraken
|
|
16
16
|
# @param aRelation [Relation] The relation corresponding to this goal
|
17
17
|
# @param args [Array<Term>] The actual aguments of the goal
|
18
18
|
def initialize(aRelation, args)
|
19
|
+
super()
|
19
20
|
@relation = aRelation
|
20
21
|
@actuals = validated_actuals(args)
|
21
22
|
end
|
@@ -35,7 +36,7 @@ module MiniKraken
|
|
35
36
|
raise StandardError, err_msg
|
36
37
|
end
|
37
38
|
|
38
|
-
prefix =
|
39
|
+
prefix = 'Invalid goal argument'
|
39
40
|
args.each do |actual|
|
40
41
|
if actual.kind_of?(GoalArg) || actual.kind_of?(Environment)
|
41
42
|
next
|
@@ -43,7 +44,7 @@ module MiniKraken
|
|
43
44
|
validated_actuals(actual)
|
44
45
|
else
|
45
46
|
actual_display = actual.nil? ? 'nil' : actual.to_s
|
46
|
-
raise StandardError, prefix
|
47
|
+
raise StandardError, "#{prefix} '#{actual_display}'"
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
@@ -8,15 +8,17 @@ module MiniKraken
|
|
8
8
|
# The individual goals are instantiated when the formal arguments
|
9
9
|
# are bound to goal arguments
|
10
10
|
class GoalTemplate < BaseArg
|
11
|
-
# @return [Array<BaseArg>
|
11
|
+
# @return [Array<BaseArg>] Arguments of goal template.
|
12
12
|
attr_reader :args
|
13
13
|
|
14
14
|
# @return [Relation] Main relation for the goal template
|
15
15
|
attr_reader :relation
|
16
16
|
|
17
17
|
def initialize(aRelation, theArgs)
|
18
|
+
super()
|
18
19
|
@relation = validated_relation(aRelation)
|
19
20
|
@args = validated_args(theArgs)
|
21
|
+
freeze
|
20
22
|
end
|
21
23
|
|
22
24
|
# @param formals [Array<FormalArg>] Array of formal arguments
|
data/lib/mini_kraken/glue/dsl.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'set'
|
3
4
|
require_relative '../core/any_value'
|
4
5
|
require_relative '../core/conj2'
|
5
6
|
require_relative '../core/cons_cell'
|
7
|
+
require_relative '../core/def_relation'
|
6
8
|
require_relative '../core/disj2'
|
7
9
|
require_relative '../core/equals'
|
8
10
|
require_relative '../core/fail'
|
11
|
+
require_relative '../core/formal_arg'
|
12
|
+
require_relative '../core/formal_ref'
|
13
|
+
require_relative '../core/goal_template'
|
14
|
+
require_relative '../core/k_boolean'
|
9
15
|
require_relative '../core/k_symbol'
|
10
16
|
require_relative '../core/succeed'
|
11
17
|
require_relative '../core/variable_ref'
|
@@ -15,31 +21,64 @@ require_relative 'run_star_expression'
|
|
15
21
|
|
16
22
|
module MiniKraken
|
17
23
|
module Glue
|
24
|
+
# The mixin module that implements the methods for the DSL
|
25
|
+
# (DSL = Domain Specific Langague) that allows MiniKraken
|
26
|
+
# users to embed Minikanren in their Ruby code.
|
18
27
|
module DSL
|
28
|
+
# A run* expression tries to find all the solutions
|
29
|
+
# that meet the given goal.
|
19
30
|
# @return [Core::ConsCell] A list of solutions
|
20
31
|
def run_star(var_names, goal)
|
21
32
|
program = RunStarExpression.new(var_names, goal)
|
22
33
|
program.run
|
23
34
|
end
|
24
35
|
|
36
|
+
# conj2 stands for conjunction of two arguments.
|
37
|
+
# Returns a goal linked to the Core::Conj2 relation.
|
38
|
+
# The rule of that relation succeeds when both arguments succeed.
|
39
|
+
# @param arg1 [Core::Goal]
|
40
|
+
# @param arg2 [Core::Goal]
|
41
|
+
# @return [Core::Failure|Core::Success]
|
25
42
|
def conj2(arg1, arg2)
|
26
|
-
|
43
|
+
goal_class.new(Core::Conj2.instance, [convert(arg1), convert(arg2)])
|
27
44
|
end
|
28
45
|
|
29
46
|
def cons(car_item, cdr_item = nil)
|
30
|
-
|
47
|
+
tail = cdr_item.nil? ? cdr_item : convert(cdr_item)
|
48
|
+
Core::ConsCell.new(convert(car_item), tail)
|
49
|
+
end
|
50
|
+
|
51
|
+
def defrel(relationName, theFormals, &aGoalTemplateExpr)
|
52
|
+
start_defrel
|
53
|
+
|
54
|
+
case theFormals
|
55
|
+
when String
|
56
|
+
@defrel_formals << theFormals
|
57
|
+
when Array
|
58
|
+
@defrel_formals.merge(theFormals)
|
59
|
+
end
|
60
|
+
|
61
|
+
formals = @defrel_formals.map { |name| Core::FormalArg.new(name) }
|
62
|
+
g_template = aGoalTemplateExpr.call
|
63
|
+
result = Core::DefRelation.new(relationName, g_template, formals)
|
64
|
+
add_defrel(result)
|
65
|
+
|
66
|
+
end_defrel
|
67
|
+
result
|
31
68
|
end
|
32
69
|
|
33
70
|
def disj2(arg1, arg2)
|
34
|
-
|
71
|
+
goal_class.new(Core::Disj2.instance, [convert(arg1), convert(arg2)])
|
35
72
|
end
|
36
73
|
|
74
|
+
# @return [Core::Fail] A goal that unconditionally fails.
|
37
75
|
def _fail
|
38
|
-
|
76
|
+
goal_class.new(Core::Fail.instance, [])
|
39
77
|
end
|
40
78
|
|
41
79
|
def equals(arg1, arg2)
|
42
|
-
|
80
|
+
# require 'debug'
|
81
|
+
goal_class.new(Core::Equals.instance, [convert(arg1), convert(arg2)])
|
43
82
|
end
|
44
83
|
|
45
84
|
def fresh(var_names, goal)
|
@@ -47,19 +86,30 @@ module MiniKraken
|
|
47
86
|
|
48
87
|
if var_names.kind_of?(String) || var_names.kind_of?(Core::VariableRef)
|
49
88
|
vars = [var_names]
|
50
|
-
|
51
|
-
|
89
|
+
else
|
52
90
|
vars = var_names
|
53
91
|
end
|
92
|
+
|
54
93
|
FreshEnv.new(vars, goal)
|
55
94
|
end
|
56
95
|
|
96
|
+
def list(*members)
|
97
|
+
return null if members.empty?
|
98
|
+
|
99
|
+
head = nil
|
100
|
+
members.reverse_each { |elem| head = Core::ConsCell.new(convert(elem), head) }
|
101
|
+
|
102
|
+
head
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return [ConsCell] Returns an empty list, that is, a pair whose members are nil.
|
57
106
|
def null
|
58
107
|
Core::ConsCell.new(nil, nil)
|
59
108
|
end
|
60
109
|
|
110
|
+
# @return [Core::Succeed] A goal that unconditionally succeeds.
|
61
111
|
def succeed
|
62
|
-
|
112
|
+
goal_class.new(Core::Succeed.instance, [])
|
63
113
|
end
|
64
114
|
|
65
115
|
private
|
@@ -74,27 +124,83 @@ module MiniKraken
|
|
74
124
|
any_val = Core::AnyValue.allocate
|
75
125
|
any_val.instance_variable_set(:@rank, rank)
|
76
126
|
converted = any_val
|
127
|
+
elsif anArgument.id2name =~ /^"#[ft]"$/
|
128
|
+
converted = Core::KBoolean.new(anArgument)
|
77
129
|
else
|
78
130
|
converted = Core::KSymbol.new(anArgument)
|
79
131
|
end
|
132
|
+
when String
|
133
|
+
if anArgument =~ /^#[ft]$/
|
134
|
+
converted = Core::KBoolean.new(anArgument)
|
135
|
+
else
|
136
|
+
msg = "Internal error: undefined conversion for #{anArgument.class}"
|
137
|
+
raise StandardError, msg
|
138
|
+
end
|
139
|
+
when false, true
|
140
|
+
converted = Core::KBoolean.new(anArgument)
|
141
|
+
when Core::KBoolean
|
142
|
+
converted = anArgument
|
143
|
+
when Core::FormalRef
|
144
|
+
converted = anArgument
|
80
145
|
when Core::Goal
|
81
146
|
converted = anArgument
|
147
|
+
when Core::GoalTemplate
|
148
|
+
converted = anArgument
|
82
149
|
when Core::VariableRef
|
83
150
|
converted = anArgument
|
84
151
|
when Core::ConsCell
|
85
152
|
converted = anArgument
|
153
|
+
else
|
154
|
+
msg = "Internal error: undefined conversion for #{anArgument.class}"
|
155
|
+
raise StandardError, msg
|
86
156
|
end
|
87
157
|
|
88
158
|
converted
|
89
159
|
end
|
90
160
|
|
161
|
+
def default_mode
|
162
|
+
@dsl_mode = :default
|
163
|
+
@defrel_formals = nil
|
164
|
+
end
|
165
|
+
|
166
|
+
def goal_class
|
167
|
+
default_mode unless instance_variable_defined?(:@dsl_mode)
|
168
|
+
@dsl_mode == :default ? Core::Goal : Core::GoalTemplate
|
169
|
+
end
|
170
|
+
|
171
|
+
def start_defrel
|
172
|
+
@dsl_mode = :defrel
|
173
|
+
@defrel_formals = Set.new
|
174
|
+
end
|
175
|
+
|
176
|
+
def end_defrel
|
177
|
+
default_mode
|
178
|
+
end
|
179
|
+
|
180
|
+
def add_defrel(aDefRelation)
|
181
|
+
@defrels = {} unless instance_variable_defined?(:@defrels)
|
182
|
+
@defrels[aDefRelation.name] = aDefRelation
|
183
|
+
end
|
184
|
+
|
91
185
|
def method_missing(mth, *args)
|
92
186
|
result = nil
|
93
187
|
|
94
188
|
begin
|
95
189
|
result = super(mth, *args)
|
96
190
|
rescue NameError
|
97
|
-
|
191
|
+
name = mth.id2name
|
192
|
+
@defrels = {} unless instance_variable_defined?(:@defrels)
|
193
|
+
if @defrels.include?(name)
|
194
|
+
def_relation = @defrels[name]
|
195
|
+
result = Core::Goal.new(def_relation, args.map { |el| convert(el) })
|
196
|
+
else
|
197
|
+
default_mode unless instance_variable_defined?(:@dsl_mode)
|
198
|
+
if @dsl_mode == :defrel && @defrel_formals.include?(name)
|
199
|
+
result = Core::FormalRef.new(name)
|
200
|
+
else
|
201
|
+
result = Core::VariableRef.new(name)
|
202
|
+
end
|
203
|
+
end
|
98
204
|
end
|
99
205
|
|
100
206
|
result
|
@@ -38,11 +38,9 @@ module MiniKraken
|
|
38
38
|
|
39
39
|
# @return [Array] A vector of assignment for each variable
|
40
40
|
def build_solution(outcome)
|
41
|
-
|
41
|
+
env.vars.values.map do |var|
|
42
42
|
outcome.successful? ? var.quote(outcome) : nil
|
43
43
|
end
|
44
|
-
|
45
|
-
sol
|
46
44
|
end
|
47
45
|
|
48
46
|
# Transform the solutions into sequence of conscells.
|
data/lib/mini_kraken/version.rb
CHANGED
data/mini_kraken.gemspec
CHANGED
@@ -9,6 +9,7 @@ module PkgExtending
|
|
9
9
|
def self.pkg_files(aPackage)
|
10
10
|
file_list = Dir[
|
11
11
|
'.rspec',
|
12
|
+
'.rubocop.yml',
|
12
13
|
'.travis.yml',
|
13
14
|
'Gemfile',
|
14
15
|
'Rakefile',
|
@@ -19,7 +20,8 @@ module PkgExtending
|
|
19
20
|
'bin/*.rb',
|
20
21
|
'lib/*.*',
|
21
22
|
'lib/**/*.rb',
|
22
|
-
'spec/**/*.rb'
|
23
|
+
'spec/**/*.rb',
|
24
|
+
'spec/.rubocop.yml'
|
23
25
|
]
|
24
26
|
aPackage.files = file_list
|
25
27
|
aPackage.test_files = Dir['spec/**/*_spec.rb']
|
@@ -38,8 +40,8 @@ Gem::Specification.new do |spec|
|
|
38
40
|
spec.authors = ['Dimitri Geshef']
|
39
41
|
spec.email = ['famished.tiger@yahoo.com']
|
40
42
|
|
41
|
-
spec.summary =
|
42
|
-
spec.description =
|
43
|
+
spec.summary = 'Implementation of Minikanren language in Ruby. WIP'
|
44
|
+
spec.description = 'Implementation of Minikanren language in Ruby. WIP'
|
43
45
|
spec.homepage = 'https://github.com/famished-tiger/mini_kraken'
|
44
46
|
spec.license = 'MIT'
|
45
47
|
|
@@ -47,6 +49,7 @@ Gem::Specification.new do |spec|
|
|
47
49
|
spec.bindir = 'exe'
|
48
50
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
49
51
|
spec.require_paths = ['lib']
|
52
|
+
spec.required_ruby_version = '~> 2.4'
|
50
53
|
|
51
54
|
PkgExtending.pkg_files(spec)
|
52
55
|
PkgExtending.pkg_documentation(spec)
|
data/spec/.rubocop.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
inherit_from: ../.rubocop.yml
|
2
|
+
|
3
|
+
# RSpec expectation lines can be very lenghty
|
4
|
+
Layout/LineLength:
|
5
|
+
Max: 99
|
6
|
+
|
7
|
+
# RSpec contexts can be very lenghty
|
8
|
+
Metrics/BlockLength:
|
9
|
+
Max: 1000
|
10
|
+
|
11
|
+
# RSpec modules can be very lenghty
|
12
|
+
Metrics/ModuleLength:
|
13
|
+
Max: 1000
|
data/spec/core/k_symbol_spec.rb
CHANGED
data/spec/glue/dsl_chap1_spec.rb
CHANGED
@@ -68,7 +68,7 @@ module MiniKraken
|
|
68
68
|
expect(result.car).to eq(:_0)
|
69
69
|
end
|
70
70
|
|
71
|
-
it 'passes frame 1:21
|
71
|
+
it "supports 'fresh' and passes frame 1:21" do
|
72
72
|
# Reasoned S2, frame 1:21
|
73
73
|
# (run* q (fresh (x) (== 'pea q))) ;; => (pea)
|
74
74
|
|
@@ -176,7 +176,7 @@ module MiniKraken
|
|
176
176
|
expect(result.car).to eq(cons(:_0, cons(:_1, cons(:_0))))
|
177
177
|
end
|
178
178
|
|
179
|
-
it 'passes frame 1:50
|
179
|
+
it "supports 'conj2' relation and passes frame 1:50" do
|
180
180
|
# Reasoned S2, frame 1:50
|
181
181
|
# (run* q (conj2 succeed succeed)) ;; => (_0)
|
182
182
|
|
@@ -216,7 +216,7 @@ module MiniKraken
|
|
216
216
|
expect(result.car).to eq(:corn)
|
217
217
|
end
|
218
218
|
|
219
|
-
it 'passes frame 1:55
|
219
|
+
it "supports 'disj2' and passes frame 1:55" do
|
220
220
|
# Reasoned S2, frame 1:55
|
221
221
|
# (run* q (disj2 fail fail)) ;; => ()
|
222
222
|
|
@@ -301,7 +301,7 @@ module MiniKraken
|
|
301
301
|
# (== 'oil x))))) ;; => (olive _0 oil)
|
302
302
|
|
303
303
|
result = run_star('x', disj2(conj2(equals(:virgin, x), _fail),
|
304
|
-
|
304
|
+
disj2(equals(:olive, x), disj2(succeed, equals(:oil, x)))))
|
305
305
|
expect(result).to eq(cons(:olive, cons(:_0, cons(:oil))))
|
306
306
|
end
|
307
307
|
|
@@ -317,8 +317,8 @@ module MiniKraken
|
|
317
317
|
# (== '(,x ,y) r)))))) ;; => ((split pea))
|
318
318
|
|
319
319
|
result = run_star('r', fresh('x', fresh('y',
|
320
|
-
conj2(equals(:split, x),
|
321
|
-
|
320
|
+
conj2(equals(:split, x),
|
321
|
+
conj2(equals(:pea, y), equals(cons(x, cons(y)), r))))))
|
322
322
|
expect(result).to eq(cons(cons(:split, cons(:pea))))
|
323
323
|
end
|
324
324
|
|
@@ -396,8 +396,7 @@ module MiniKraken
|
|
396
396
|
# (== 'pea y))) ;; => ((split pea))
|
397
397
|
|
398
398
|
result = run_star(%w[x y], conj2(equals(:split, x), equals(:pea, y)))
|
399
|
-
expect(result.car
|
400
|
-
expect(result.car.cdr.car).to eq(:pea)
|
399
|
+
expect(result.car).to eq(list(:split, :pea))
|
401
400
|
end
|
402
401
|
|
403
402
|
it 'passes frame 1:76' do
|
@@ -410,10 +409,8 @@ module MiniKraken
|
|
410
409
|
result = run_star(%w[x y], disj2(
|
411
410
|
conj2(equals(:split, x), equals(:pea, y)),
|
412
411
|
conj2(equals(:red, x), equals(:bean, y))))
|
413
|
-
expect(result.car
|
414
|
-
expect(result.
|
415
|
-
expect(result.cdr.car.car).to eq(:red)
|
416
|
-
expect(result.cdr.car.cdr.car).to eq(:bean)
|
412
|
+
expect(result.car).to eq(list(:split, :pea))
|
413
|
+
expect(result.cdr.car).to eq(list(:red, :bean))
|
417
414
|
end
|
418
415
|
|
419
416
|
it 'passes frame 1:77' do
|
@@ -432,12 +429,8 @@ module MiniKraken
|
|
432
429
|
conj2(equals(:split, x), equals(:pea, y)),
|
433
430
|
conj2(equals(:red, x), equals(:bean, y))),
|
434
431
|
equals(cons(x, cons(y, cons(:soup))), r))))
|
435
|
-
expect(result.car
|
436
|
-
expect(result.
|
437
|
-
expect(result.car.cdr.cdr.car).to eq(:soup)
|
438
|
-
expect(result.cdr.car.car).to eq(:red)
|
439
|
-
expect(result.cdr.car.cdr.car).to eq(:bean)
|
440
|
-
expect(result.cdr.car.cdr.cdr.car).to eq(:soup)
|
432
|
+
expect(result.car).to eq(list(:split, :pea, :soup))
|
433
|
+
expect(result.cdr.car).to eq(list(:red, :bean, :soup))
|
441
434
|
end
|
442
435
|
|
443
436
|
it 'passes frame 1:78' do
|
@@ -451,15 +444,11 @@ module MiniKraken
|
|
451
444
|
|
452
445
|
result = run_star('r',
|
453
446
|
fresh(%w[x y], [disj2(
|
454
|
-
|
455
|
-
|
447
|
+
conj2(equals(:split, x), equals(:pea, y)),
|
448
|
+
conj2(equals(:red, x), equals(:bean, y))),
|
456
449
|
equals(cons(x, cons(y, cons(:soup))), r)]))
|
457
|
-
expect(result.car
|
458
|
-
expect(result.
|
459
|
-
expect(result.car.cdr.cdr.car).to eq(:soup)
|
460
|
-
expect(result.cdr.car.car).to eq(:red)
|
461
|
-
expect(result.cdr.car.cdr.car).to eq(:bean)
|
462
|
-
expect(result.cdr.car.cdr.cdr.car).to eq(:soup)
|
450
|
+
expect(result.car).to eq(list(:split, :pea, :soup))
|
451
|
+
expect(result.cdr.car).to eq(list(:red, :bean, :soup))
|
463
452
|
end
|
464
453
|
|
465
454
|
it 'passes frame 1:80' do
|
@@ -471,15 +460,11 @@ module MiniKraken
|
|
471
460
|
# (== 'soup z)) ;; => ((split pea soup) (red bean soup))
|
472
461
|
|
473
462
|
result = run_star(%w[x y z], [disj2(
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
expect(result.car
|
478
|
-
expect(result.
|
479
|
-
expect(result.car.cdr.cdr.car).to eq(:soup)
|
480
|
-
expect(result.cdr.car.car).to eq(:red)
|
481
|
-
expect(result.cdr.car.cdr.car).to eq(:bean)
|
482
|
-
expect(result.cdr.car.cdr.cdr.car).to eq(:soup)
|
463
|
+
conj2(equals(:split, x), equals(:pea, y)),
|
464
|
+
conj2(equals(:red, x), equals(:bean, y))),
|
465
|
+
equals(:soup, z)])
|
466
|
+
expect(result.car).to eq(list(:split, :pea, :soup))
|
467
|
+
expect(result.cdr.car).to eq(list(:red, :bean, :soup))
|
483
468
|
end
|
484
469
|
|
485
470
|
it 'passes frame 1:81' do
|
@@ -489,8 +474,119 @@ module MiniKraken
|
|
489
474
|
# (== 'pea y)) ;; => ((split pea))
|
490
475
|
|
491
476
|
result = run_star(%w[x y], [equals(:split, x), equals(:pea, y)])
|
492
|
-
expect(result.car
|
493
|
-
|
477
|
+
expect(result.car).to eq(list(:split, :pea))
|
478
|
+
end
|
479
|
+
|
480
|
+
it "supports 'defrel' and passes frame 1:82" do
|
481
|
+
# Reasoned S2, frame 1:82
|
482
|
+
# (defrel (teacupo t)
|
483
|
+
# (disj2 (== 'tea t) (== 'cup t)))
|
484
|
+
|
485
|
+
result = defrel('teacupo', 't') do
|
486
|
+
disj2(equals(:tea, t), equals(:cup, t))
|
487
|
+
end
|
488
|
+
|
489
|
+
expect(result).to be_kind_of(Core::DefRelation)
|
490
|
+
expect(result.name).to eq('teacupo')
|
491
|
+
expect(result.formals.size).to eq(1)
|
492
|
+
expect(result.formals[0].name).to eq('t')
|
493
|
+
g_template = result.goal_template
|
494
|
+
expect(g_template).to be_kind_of(Core::GoalTemplate)
|
495
|
+
expect(g_template.relation).to eq(Core::Disj2.instance)
|
496
|
+
|
497
|
+
first_arg = g_template.args[0]
|
498
|
+
expect(first_arg).to be_kind_of(Core::GoalTemplate)
|
499
|
+
expect(first_arg.relation).to eq(Core::Equals.instance)
|
500
|
+
expect(first_arg.args[0]).to eq(:tea)
|
501
|
+
expect(first_arg.args[1]).to be_kind_of(Core::FormalRef)
|
502
|
+
expect(first_arg.args[1].name).to eq('t')
|
503
|
+
second_arg = g_template.args[1]
|
504
|
+
expect(second_arg).to be_kind_of(Core::GoalTemplate)
|
505
|
+
expect(second_arg.relation).to eq(Core::Equals.instance)
|
506
|
+
expect(second_arg.args[0]).to eq(:cup)
|
507
|
+
expect(second_arg.args[1]).to be_kind_of(Core::FormalRef)
|
508
|
+
expect(second_arg.args[1].name).to eq('t')
|
509
|
+
end
|
510
|
+
|
511
|
+
def defrel_teacupo
|
512
|
+
defrel('teacupo', 't') { disj2(equals(:tea, t), equals(:cup, t)) }
|
513
|
+
end
|
514
|
+
|
515
|
+
it "supports the invokation of a 'defrel' and passes frame 1:83" do
|
516
|
+
# Reasoned S2, frame 1:83
|
517
|
+
# (run* x
|
518
|
+
# (teacupo x)) ;; => ((tea cup))
|
519
|
+
|
520
|
+
defrel_teacupo
|
521
|
+
result = run_star('x', teacupo(x))
|
522
|
+
|
523
|
+
expect(result).to eq(cons(:tea, cons(:cup)))
|
524
|
+
end
|
525
|
+
|
526
|
+
it 'supports booleans and passes frame 1:84' do
|
527
|
+
# Reasoned S2, frame 1:84
|
528
|
+
# (run* (x y)
|
529
|
+
# (disj2
|
530
|
+
# (conj2 (teacupo x) (== #t y))
|
531
|
+
# (conj2 (== #f x) (== #t y))) ;; => ((#f #t)(tea #t) (cup #t))
|
532
|
+
|
533
|
+
defrel_teacupo
|
534
|
+
result = run_star(%w[x y],
|
535
|
+
disj2(
|
536
|
+
conj2(teacupo(x), equals('#t', y)),
|
537
|
+
conj2(equals('#f', x), equals('#t', y))))
|
538
|
+
|
539
|
+
# Order of solutions differs from RS book
|
540
|
+
expect(result.car).to eq(cons(:tea, cons(true)))
|
541
|
+
expect(result.cdr.car).to eq(cons(:cup, cons(true)))
|
542
|
+
expect(result.cdr.cdr.car).to eq(cons(false, cons(true)))
|
543
|
+
end
|
544
|
+
|
545
|
+
it 'passes frame 1:85' do
|
546
|
+
# Reasoned S2, frame 1:85
|
547
|
+
# (run* (x y)
|
548
|
+
# (teacupo x)
|
549
|
+
# (teacupo y)) ;; => ((tea tea)(tea cup)(cup tea)(cup c))
|
550
|
+
|
551
|
+
defrel_teacupo
|
552
|
+
result = run_star(%w[x y], [teacupo(x), teacupo(y)])
|
553
|
+
|
554
|
+
expect(result.car).to eq(cons(:tea, cons(:tea)))
|
555
|
+
expect(result.cdr.car).to eq(cons(:tea, cons(:cup)))
|
556
|
+
expect(result.cdr.cdr.car).to eq(cons(:cup, cons(:tea)))
|
557
|
+
expect(result.cdr.cdr.cdr.car).to eq(cons(:cup, cons(:cup)))
|
558
|
+
end
|
559
|
+
|
560
|
+
it 'passes frame 1:86' do
|
561
|
+
# Reasoned S2, frame 1:86
|
562
|
+
# (run* (x y)
|
563
|
+
# (teacupo x)
|
564
|
+
# (teacupo x)) ;; => ((tea _0)(cup _0))
|
565
|
+
|
566
|
+
defrel_teacupo
|
567
|
+
result = run_star(%w[x y], [teacupo(x), teacupo(x)])
|
568
|
+
|
569
|
+
expect(result.car).to eq(cons(:tea, cons(:_0)))
|
570
|
+
expect(result.cdr.car).to eq(cons(:cup, cons(:_0)))
|
571
|
+
end
|
572
|
+
|
573
|
+
it 'passes frame 1:87' do
|
574
|
+
# Reasoned S2, frame 1:87
|
575
|
+
# (run* (x y)
|
576
|
+
# (disj2
|
577
|
+
# (conj2 (teacupo x) (teacupo x))
|
578
|
+
# (conj2 (== #f x) (teacupo y)))) ;; => ((#f tea)(#f cup)(tea _0)(cup _0))
|
579
|
+
|
580
|
+
defrel_teacupo
|
581
|
+
result = run_star(%w[x y], disj2(
|
582
|
+
conj2(teacupo(x), teacupo(x)),
|
583
|
+
conj2(equals('#f', x), teacupo(y))))
|
584
|
+
|
585
|
+
# Order of solutions differs from RS book
|
586
|
+
expect(result.car).to eq(cons(:tea, cons(:_0)))
|
587
|
+
expect(result.cdr.car).to eq(cons(:cup, cons(:_0)))
|
588
|
+
expect(result.cdr.cdr.car).to eq(cons(false, cons(:tea)))
|
589
|
+
expect(result.cdr.cdr.cdr.car).to eq(cons(false, cons(:cup)))
|
494
590
|
end
|
495
591
|
end # context
|
496
592
|
end # describe
|
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.2.
|
4
|
+
version: 0.2.01
|
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-07
|
11
|
+
date: 2020-08-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -61,6 +61,7 @@ extra_rdoc_files:
|
|
61
61
|
- README.md
|
62
62
|
files:
|
63
63
|
- ".rspec"
|
64
|
+
- ".rubocop.yml"
|
64
65
|
- ".travis.yml"
|
65
66
|
- CHANGELOG.md
|
66
67
|
- Gemfile
|
@@ -109,6 +110,7 @@ files:
|
|
109
110
|
- lib/mini_kraken/glue/run_star_expression.rb
|
110
111
|
- lib/mini_kraken/version.rb
|
111
112
|
- mini_kraken.gemspec
|
113
|
+
- spec/.rubocop.yml
|
112
114
|
- spec/core/association_spec.rb
|
113
115
|
- spec/core/association_walker_spec.rb
|
114
116
|
- spec/core/conde_spec.rb
|
@@ -146,9 +148,9 @@ require_paths:
|
|
146
148
|
- lib
|
147
149
|
required_ruby_version: !ruby/object:Gem::Requirement
|
148
150
|
requirements:
|
149
|
-
- - "
|
151
|
+
- - "~>"
|
150
152
|
- !ruby/object:Gem::Version
|
151
|
-
version: '
|
153
|
+
version: '2.4'
|
152
154
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
155
|
requirements:
|
154
156
|
- - ">="
|