mini_kraken 0.2.00 → 0.2.01

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: 4bbfe3d58d4c7271a5c8a54486a743c51d6fddcb3b068095cad44cacad575c83
4
- data.tar.gz: 012b412a95752592b5be70859e00d94b379e2c4c564d37d932a300a03cbc9716
3
+ metadata.gz: f51855ac3179a111a4ace328340402fa167818773086076abf90ed845c31d248
4
+ data.tar.gz: f6bde0d3d7b872461d7cb0b087eaab821240f14317445e95f4d832d09af51e2f
5
5
  SHA512:
6
- metadata.gz: 952d93b01de392c7168e7cfb4a3cc911e4d1b53be11a4a10d67012bb4ab4a8f69aa42f41bf13a756f89a6d09eb775a5594174d73334c57c2230039b7991e89fd
7
- data.tar.gz: a60adbd5da627495badab63de355336f131a5e599c40226931366a7c1b4a349b2542ccb55cf30ef8e60990341c1f5d8547804b1552fbbbc504bd1bed40a36d9e
6
+ metadata.gz: ae6319164a2f6212bdbdea35dd5b5befa86648b685063dea79d220b70e5852c3e8b343d2d37ebfe6584aa3966b44174e91df2866d63e03a219a0008a5b382d66
7
+ data.tar.gz: 70eb204ab842ea45527bd5ca8661dcd98081422cae4d6848d220bc70e080da94b3b7078f3602f1143535821a9894309b9bec39ec5f4d6c60edcfd21cfa5c8f4f
@@ -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
@@ -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 provided variable(s)
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. In that case, the `run_star` return an empty list represented as `()` in the output.
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. A `disj2` succeeds if any of its arguments succeeds.
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
@@ -13,6 +13,7 @@ module MiniKraken
13
13
 
14
14
  # @param aValue [Object] Ruby representation of MiniKraken data value
15
15
  def initialize(aValue)
16
+ super()
16
17
  @value = aValue
17
18
  @value.freeze
18
19
  end
@@ -10,6 +10,7 @@ unless MiniKraken::Core.constants(false).include? :ConsCell
10
10
  attr_reader :cdr
11
11
 
12
12
  def initialize(obj1, obj2 = nil)
13
+ super()
13
14
  @car = obj1
14
15
  if obj2.kind_of?(ConsCell) && obj2.null?
15
16
  @cdr = nil
@@ -19,6 +19,7 @@ module MiniKraken
19
19
  super(aName, alternateName)
20
20
  @formals = validated_formals(theFormals)
21
21
  @goal_template = validated_goal_template(aGoalTemplate)
22
+ freeze
22
23
  end
23
24
 
24
25
  # Number of arguments for the relation.
@@ -60,7 +60,7 @@ module MiniKraken
60
60
  return unless descendent.successful?
61
61
 
62
62
  vars.each_key do |var_name|
63
- assocs = descendent[var_name]
63
+ # assocs = descendent[var_name]
64
64
  move_assocs(var_name, descendent)
65
65
  end
66
66
  end
@@ -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
- result = do_unification(new_arg1, new_arg2, anEnv)
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
- associations = subresults.reduce(memo) do |sub_total, outcome|
141
+ subresults.reduce(memo) do |sub_total, outcome|
145
142
  sub_total.merge(outcome)
146
143
  sub_total
147
144
  end
@@ -11,6 +11,7 @@ module MiniKraken
11
11
  attr_reader :name
12
12
 
13
13
  def initialize(aName)
14
+ super()
14
15
  @name = validated_name(aName)
15
16
  end
16
17
 
@@ -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 = "Invalid goal argument '"
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 + actual_display + "'"
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>}] Arguments of goal template.
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
@@ -20,7 +20,7 @@ module MiniKraken
20
20
 
21
21
  # Returns a string representing the MiniKraken symbol.
22
22
  def to_s
23
- ':' + id2name
23
+ ":#{id2name}"
24
24
  end
25
25
  end # class
26
26
  end # module
@@ -13,6 +13,7 @@ module MiniKraken
13
13
 
14
14
  # @param aName [String] The name of the variable
15
15
  def initialize(aName)
16
+ super()
16
17
  init_designation(aName)
17
18
  end
18
19
 
@@ -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
- Core::Goal.new(Core::Conj2.instance, [convert(arg1), convert(arg2)])
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
- Core::ConsCell.new(convert(car_item), convert(cdr_item))
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
- Core::Goal.new(Core::Disj2.instance, [convert(arg1), convert(arg2)])
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
- Core::Goal.new(Core::Fail.instance, [])
76
+ goal_class.new(Core::Fail.instance, [])
39
77
  end
40
78
 
41
79
  def equals(arg1, arg2)
42
- Core::Goal.new(Core::Equals.instance, [convert(arg1), convert(arg2)])
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
- elsif
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
- Core::Goal.new(Core::Succeed.instance, [])
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
- result = Core::VariableRef.new(mth.id2name)
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
- sol = env.vars.values.map do |var|
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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniKraken
4
- VERSION = '0.2.00'
4
+ VERSION = '0.2.01'
5
5
  end
@@ -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 = %q{Implementation of Minikanren language in Ruby. WIP}
42
- spec.description = %q{Implementation of Minikanren language in Ruby. WIP}
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)
@@ -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
@@ -52,7 +52,7 @@ module MiniKraken
52
52
  outcome1 = instance1.resume
53
53
 
54
54
  instance2 = DuckFiber.new(:success)
55
- outcome2 = instance1.resume
55
+ outcome2 = instance2.resume
56
56
 
57
57
  expect(outcome1).not_to be_equal(outcome2)
58
58
  end
@@ -65,7 +65,7 @@ module MiniKraken
65
65
  # Default Ruby representation, different value
66
66
  expect(subject == :pod).to be_falsy
67
67
  end
68
-
68
+
69
69
  it 'should provide a string representation of itself' do
70
70
  expect(subject.to_s).to eq(':pea')
71
71
  end
@@ -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' do
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' do
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' do
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
- disj2(equals(:olive, x), disj2(succeed, equals(:oil, x)))))
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), conj2(
321
- equals(:pea, y), equals(cons(x, cons(y)), r))))))
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.car).to eq(:split)
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.car).to eq(:split)
414
- expect(result.car.cdr.car).to eq(:pea)
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.car).to eq(:split)
436
- expect(result.car.cdr.car).to eq(:pea)
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
- conj2(equals(:split, x), equals(:pea, y)),
455
- conj2(equals(:red, x), equals(:bean, y))),
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.car).to eq(:split)
458
- expect(result.car.cdr.car).to eq(:pea)
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
- conj2(equals(:split, x), equals(:pea, y)),
475
- conj2(equals(:red, x), equals(:bean, y))),
476
- equals(:soup, z)])
477
- expect(result.car.car).to eq(:split)
478
- expect(result.car.cdr.car).to eq(:pea)
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.car).to eq(:split)
493
- expect(result.car.cdr.car).to eq(:pea)
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.00
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-12 00:00:00.000000000 Z
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: '0'
153
+ version: '2.4'
152
154
  required_rubygems_version: !ruby/object:Gem::Requirement
153
155
  requirements:
154
156
  - - ">="