mini_kraken 0.1.06 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +5 -1
- data/CHANGELOG.md +56 -1
- data/README.md +11 -3
- data/lib/mini_kraken/core/association_walker.rb +1 -1
- data/lib/mini_kraken/core/base_arg.rb +10 -0
- data/lib/mini_kraken/core/conj2.rb +58 -65
- data/lib/mini_kraken/core/cons_cell.rb +49 -43
- data/lib/mini_kraken/core/def_relation.rb +49 -0
- data/lib/mini_kraken/core/disj2.rb +72 -0
- data/lib/mini_kraken/core/duck_fiber.rb +1 -1
- data/lib/mini_kraken/core/environment.rb +1 -1
- data/lib/mini_kraken/core/equals.rb +134 -126
- data/lib/mini_kraken/core/fail.rb +18 -14
- data/lib/mini_kraken/core/formal_arg.rb +22 -0
- data/lib/mini_kraken/core/formal_ref.rb +24 -0
- data/lib/mini_kraken/core/goal_arg.rb +5 -3
- data/lib/mini_kraken/core/goal_relation.rb +13 -0
- data/lib/mini_kraken/core/goal_template.rb +60 -0
- data/lib/mini_kraken/core/k_boolean.rb +31 -0
- data/lib/mini_kraken/core/outcome.rb +40 -24
- data/lib/mini_kraken/core/succeed.rb +17 -13
- data/lib/mini_kraken/core/vocabulary.rb +37 -17
- data/lib/mini_kraken/glue/fresh_env.rb +45 -3
- data/lib/mini_kraken/glue/run_star_expression.rb +45 -19
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/core/conj2_spec.rb +8 -9
- data/spec/core/cons_cell_spec.rb +8 -0
- data/spec/core/def_relation_spec.rb +96 -0
- data/spec/core/disj2_spec.rb +99 -0
- data/spec/core/duck_fiber_spec.rb +12 -1
- data/spec/core/equals_spec.rb +3 -3
- data/spec/core/goal_template_spec.rb +74 -0
- data/spec/core/k_boolean_spec.rb +107 -0
- data/spec/core/outcome_spec.rb +48 -0
- data/spec/core/vocabulary_spec.rb +11 -5
- data/spec/glue/fresh_env_spec.rb +27 -1
- data/spec/glue/run_star_expression_spec.rb +538 -70
- data/spec/mini_kraken_spec.rb +2 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/factory_methods.rb +17 -1
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 672521fbc6627e2767bfe4d6ce38dd66243736fee451e857ec7ef45d0f8905fe
|
4
|
+
data.tar.gz: 6835bb1d91bb6e867417faba0d6904734295f9a7bab612c6a78fcfbcdc41f84a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd5b5b2d2493b4ec8f8293c5f786be1a7739d05bee5d4733d48edb16713d1df4097be5a6e85127c2c90cf395da877b57ef07a607167b8f5bb1732dbc2a367610
|
7
|
+
data.tar.gz: 6661aadbe8bd778283e91f31181651e32296150d282575697fee1f5c240ea46168c5107baf33d9b590d6a449ec89b2f41e69340b81cb9b9e3692954af155083b
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,60 @@
|
|
1
|
+
## [0.1.11] - 2020-06-10
|
2
|
+
- Supports `defrel`, taht is, the capability to define new relations by combining other relations.
|
3
|
+
- Covers frames from "The Reasoned Scheme" book up to frame [1:87]
|
4
|
+
|
5
|
+
### New
|
6
|
+
- Class `BaseArg` a generalization of goal or goal template argument.
|
7
|
+
- Class `DefRelation` A specialization of `Relation` class aimed for user-defined relation.
|
8
|
+
- Class `FormalArg` to represent goal template argument(s).
|
9
|
+
- Class `FormalRef` an allusion to a formal argument in a goal template.
|
10
|
+
- Class `GoalTemplate` a representation of a goal parametrized with formal arguments.
|
11
|
+
- Class `KBoolean` a MiniKraken representation of a boolean value.
|
12
|
+
|
13
|
+
### CHANGED
|
14
|
+
- File `README.md` minor change: added more TODO's.
|
15
|
+
|
16
|
+
## [0.1.10] - 2020-06-10
|
17
|
+
- Supports frames from "The Reasoned Scheme" book up to frame [1:81]
|
18
|
+
|
19
|
+
### New
|
20
|
+
- Factory methods `Outcome#failure`, `Outcome#success`
|
21
|
+
- Method `Vocabulary#inspect`
|
22
|
+
- File `outcome_spec.rb`
|
23
|
+
|
24
|
+
### FIXED
|
25
|
+
- `Conj2#conjunction` vocabulary wasn't cleared when outcome2 was nil.
|
26
|
+
|
27
|
+
## [0.1.09] - 2020-06-06
|
28
|
+
- Supports frames from "The Reasoned Scheme" book up to frame [1:76]
|
29
|
+
|
30
|
+
### CHANGED
|
31
|
+
- Method `FreshEnv#initialize`accepts an array of goals as second argument. This array is transformed into a conjunction of goals.
|
32
|
+
- Method `RunStarExpression#initialize` accepts multiple multiple variable names and goals.
|
33
|
+
- Method `RunStarExpression#run` can handle solutions with multiple variables.
|
34
|
+
|
35
|
+
## [0.1.08] - 2020-05-30
|
36
|
+
- Fix of nasty bug (object aliasing) that caused flaky failures in specs.
|
37
|
+
|
38
|
+
### FIXED
|
39
|
+
- `DuckFiber#resume` each call returns a distinct `Outcome` instance when successful.
|
40
|
+
|
41
|
+
## [0.1.07] - 2020-05-23
|
42
|
+
- Implementation of `disj2` (two arguments disjunction - or -)
|
43
|
+
|
44
|
+
### New
|
45
|
+
- Class `Disj2` as subclass of `GoalRelation` that implements the disjunction of two subgoals
|
46
|
+
|
47
|
+
### CHANGED
|
48
|
+
- Class `Disj2`: common code with `Conj2` class factored out to superclass `GoalRelation`
|
49
|
+
- File `cons_cell.rb`: prevent multiple inclusions via different requires
|
50
|
+
- Method `Vocabulary#ancestor_walker` now returns an `Enumerator` instead of a `Fiber`.
|
51
|
+
|
52
|
+
### FIXED
|
53
|
+
- Method `RunStarExpression#run` clear associations and rankings for second and consecutive solmutions
|
54
|
+
|
55
|
+
|
1
56
|
## [0.1.06] - 2020-05-20
|
2
|
-
- Implementation of `conj2` (two arguments conjunction)
|
57
|
+
- Implementation of `conj2` (two arguments conjunction - and -)
|
3
58
|
|
4
59
|
### New
|
5
60
|
- Class `CompositeGoal`
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
### What is __mini_kraken__ ?
|
7
7
|
An implemention of the [miniKanren](http://minikanren.org/) relational programming language in Ruby.
|
8
8
|
*miniKanren* is a small language for relational (logic) programming.
|
9
|
-
Based on the reference implementation, in Scheme from the "The Reasoned Schemer" book.
|
9
|
+
Based on the reference implementation, in Scheme from the "The Reasoned Schemer" book.
|
10
10
|
Daniel P. Friedman, William E. Byrd, Oleg Kiselyov, and Jason Hemann: "The Reasoned Schemer", Second Edition,
|
11
11
|
ISBN: 9780262535519, (2018), MIT Press.
|
12
12
|
|
@@ -15,13 +15,21 @@ ISBN: 9780262535519, (2018), MIT Press.
|
|
15
15
|
- [X] run\*
|
16
16
|
- [X] fresh
|
17
17
|
- [X] conj2
|
18
|
+
- [X] disj2
|
19
|
+
- [X] defrel
|
18
20
|
|
19
21
|
### TODO
|
20
|
-
- [ ] disj2
|
21
|
-
- [ ] defrel
|
22
22
|
- [ ] conde
|
23
23
|
- [ ] Occurs check
|
24
24
|
|
25
|
+
List-centric relations from Chapter 2
|
26
|
+
- [ ] caro
|
27
|
+
- [ ] cdro
|
28
|
+
- [ ] conso
|
29
|
+
- [ ] nullo
|
30
|
+
- [ ] pairo
|
31
|
+
- [ ] singletono
|
32
|
+
|
25
33
|
## Installation
|
26
34
|
|
27
35
|
Add this line to your application's Gemfile:
|
@@ -6,81 +6,74 @@ require_relative 'goal'
|
|
6
6
|
require_relative 'goal_relation'
|
7
7
|
require_relative 'outcome'
|
8
8
|
|
9
|
-
|
10
|
-
module
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
unless MiniKraken::Core.constants(false).include? :Conj2
|
10
|
+
module MiniKraken
|
11
|
+
module Core
|
12
|
+
# The conjunction is a relation that accepts only goal(s) as its two
|
13
|
+
# arguments. It succeeds if and only both its goal arguments succeeds.
|
14
|
+
class Conj2 < GoalRelation
|
15
|
+
include Singleton
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def initialize
|
18
|
+
super('conj2', nil)
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
# @param actuals [Array<Term>] A two-elements array
|
22
|
+
# @param anEnv [Vocabulary] A vocabulary object
|
23
|
+
# @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
|
24
|
+
def solver_for(actuals, anEnv)
|
25
|
+
g1, g2 = *validated_args(actuals)
|
26
|
+
Fiber.new { conjunction(g1, g2, anEnv) }
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
29
|
+
# Yields [Outcome, NilClass] result of the conjunction
|
30
|
+
# @param g1 [Goal] First goal argument
|
31
|
+
# @param g2 [Goal] Second goal argument
|
32
|
+
# @param voc [Vocabulary] A vocabulary object
|
33
|
+
def conjunction(g1, g2, voc)
|
34
|
+
# require 'debug'
|
35
|
+
if g1.relation.kind_of?(Fail) || g2.relation.kind_of?(Fail)
|
36
|
+
Fiber.yield Outcome.new(:"#u", voc)
|
37
|
+
else
|
38
|
+
outcome1 = nil
|
39
|
+
outcome2 = nil
|
40
|
+
f1 = g1.attain(voc)
|
41
|
+
loop do
|
42
|
+
outcome1 = f1.resume
|
43
|
+
break unless outcome1
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
outcome1.parent = voc unless outcome1.parent
|
46
|
+
if outcome1.successful?
|
47
|
+
f2 = g2.attain(outcome1)
|
48
|
+
loop do
|
49
|
+
outcome2 = f2.resume
|
50
|
+
break unless outcome2
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
52
|
+
outcome2.parent = voc unless outcome2.parent
|
53
|
+
if outcome2.successful?
|
54
|
+
res = Outcome.new(:"#s", voc)
|
55
|
+
res.merge(outcome1)
|
56
|
+
res.merge(outcome2)
|
57
|
+
Fiber.yield res
|
58
|
+
else
|
59
|
+
Fiber.yield outcome2
|
60
|
+
end
|
61
|
+
outcome2.clear
|
59
62
|
end
|
60
|
-
|
63
|
+
else
|
64
|
+
Fiber.yield outcome1
|
65
|
+
end
|
66
|
+
if outcome1.successful? && (outcome2&.successful? || outcome2.nil?)
|
67
|
+
voc.clear
|
61
68
|
end
|
62
|
-
else
|
63
|
-
Fiber.yield outcome1
|
64
69
|
end
|
65
|
-
voc.clear if outcome1&.successful? && outcome2&.successful?
|
66
70
|
end
|
67
|
-
end
|
68
71
|
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def validated_args(actuals)
|
75
|
-
actuals.each do |arg|
|
76
|
-
unless arg.kind_of?(Goal)
|
77
|
-
prefix = 'conj2 expects goal as argument, found a '
|
78
|
-
raise StandardError, prefix + "'#{arg.class}'"
|
79
|
-
end
|
72
|
+
Fiber.yield nil
|
80
73
|
end
|
74
|
+
end # class
|
81
75
|
|
82
|
-
|
83
|
-
|
84
|
-
end # class
|
76
|
+
Conj2.instance.freeze
|
77
|
+
end # module
|
85
78
|
end # module
|
86
|
-
end #
|
79
|
+
end # unless
|
@@ -2,47 +2,53 @@
|
|
2
2
|
|
3
3
|
require_relative 'composite_term'
|
4
4
|
|
5
|
-
|
6
|
-
module
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
5
|
+
unless MiniKraken::Core.constants(false).include? :ConsCell
|
6
|
+
module MiniKraken
|
7
|
+
module Core
|
8
|
+
class ConsCell < CompositeTerm
|
9
|
+
attr_reader :car
|
10
|
+
attr_reader :cdr
|
11
|
+
|
12
|
+
def initialize(obj1, obj2 = nil)
|
13
|
+
@car = obj1
|
14
|
+
@cdr = obj2
|
15
|
+
end
|
16
|
+
|
17
|
+
def children
|
18
|
+
[car, cdr]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return true if it is an empty list, otherwise false.
|
22
|
+
# A list is empty, when both car and cdr fields are nil.
|
23
|
+
def null?
|
24
|
+
car.nil? && cdr.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(other)
|
28
|
+
return false unless other.respond_to?(:car)
|
29
|
+
|
30
|
+
(car == other.car) && (cdr == other.cdr)
|
31
|
+
end
|
32
|
+
|
33
|
+
def eql?(other)
|
34
|
+
(self.class == other.class) && car.eql?(other.car) && cdr.eql?(other.cdr)
|
35
|
+
end
|
36
|
+
|
37
|
+
def quote(anEnv)
|
38
|
+
return self if null?
|
39
|
+
|
40
|
+
new_car = car.nil? ? nil : car.quote(anEnv)
|
41
|
+
new_cdr = cdr.nil? ? nil : cdr.quote(anEnv)
|
42
|
+
ConsCell.new(new_car, new_cdr)
|
43
|
+
end
|
44
|
+
|
45
|
+
def append(another)
|
46
|
+
@cdr = another
|
47
|
+
end
|
48
|
+
end # class
|
49
|
+
|
50
|
+
# Constant representing the null (empty) list.
|
51
|
+
NullList = ConsCell.new(nil, nil).freeze
|
52
|
+
end # module
|
47
53
|
end # module
|
48
|
-
end #
|
54
|
+
end # defined
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'relation'
|
4
|
+
|
5
|
+
module MiniKraken
|
6
|
+
module Core
|
7
|
+
# A relation that is parametrized with generic formal arguments
|
8
|
+
# and a goal template expression.
|
9
|
+
class DefRelation < Relation
|
10
|
+
# @return [Array<FormalArg>] formal arguments of this DefRelation
|
11
|
+
attr_reader :formals
|
12
|
+
|
13
|
+
# @return [GoalTemplate] goal template
|
14
|
+
attr_reader :goal_template
|
15
|
+
|
16
|
+
# @param aName [String] name of def relation
|
17
|
+
# @param aGoalTemplate [GoalTemplate]
|
18
|
+
def initialize(aName, aGoalTemplate, theFormals, alternateName = nil)
|
19
|
+
super(aName, alternateName)
|
20
|
+
@formals = validated_formals(theFormals)
|
21
|
+
@goal_template = validated_goal_template(aGoalTemplate)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Number of arguments for the relation.
|
25
|
+
# @return [Integer]
|
26
|
+
def arity
|
27
|
+
formals.size
|
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(-like) instance that yields Outcomes
|
33
|
+
def solver_for(actuals, anEnv)
|
34
|
+
goal = goal_template.instantiate(formals, actuals)
|
35
|
+
goal.attain(anEnv)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def validated_formals(theFormals)
|
41
|
+
theFormals
|
42
|
+
end
|
43
|
+
|
44
|
+
def validated_goal_template(aGoalTemplate)
|
45
|
+
aGoalTemplate
|
46
|
+
end
|
47
|
+
end # class
|
48
|
+
end # module
|
49
|
+
end # module
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require_relative 'duck_fiber'
|
5
|
+
require_relative 'fail'
|
6
|
+
require_relative 'goal'
|
7
|
+
require_relative 'goal_relation'
|
8
|
+
require_relative 'outcome'
|
9
|
+
|
10
|
+
unless MiniKraken::Core.constants(false).include? :Disj2
|
11
|
+
module MiniKraken
|
12
|
+
module Core
|
13
|
+
# The disjunction is a relation that accepts only goal(s) as its two
|
14
|
+
# arguments. It succeeds if at least one of its goal arguments succeeds.
|
15
|
+
class Disj2 < GoalRelation
|
16
|
+
include Singleton
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
super('disj2', nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param actuals [Array<Term>] A two-elements array
|
23
|
+
# @param anEnv [Vocabulary] A vocabulary object
|
24
|
+
# @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
|
25
|
+
def solver_for(actuals, anEnv)
|
26
|
+
g1, g2 = *validated_args(actuals)
|
27
|
+
Fiber.new { disjunction(g1, g2, anEnv) }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Yields [Outcome, NilClass] result of the disjunction
|
31
|
+
# @param g1 [Goal] First goal argument
|
32
|
+
# @param g2 [Goal] Second goal argument
|
33
|
+
# @param voc [Vocabulary] A vocabulary object
|
34
|
+
def disjunction(g1, g2, voc)
|
35
|
+
# require 'debug'
|
36
|
+
outcome1 = nil
|
37
|
+
outcome2 = nil
|
38
|
+
if g1.relation.kind_of?(Fail) && g2.relation.kind_of?(Fail)
|
39
|
+
Fiber.yield Outcome.new(:"#u", voc)
|
40
|
+
else
|
41
|
+
f1 = g1.attain(voc)
|
42
|
+
loop do
|
43
|
+
outcome1 = f1.resume
|
44
|
+
break unless outcome1
|
45
|
+
|
46
|
+
outcome1.parent = voc unless outcome1.parent
|
47
|
+
if outcome1.successful?
|
48
|
+
Fiber.yield outcome1
|
49
|
+
outcome1.clear
|
50
|
+
end
|
51
|
+
end
|
52
|
+
f2 = g2.attain(voc)
|
53
|
+
loop do
|
54
|
+
outcome2 = f2.resume
|
55
|
+
break unless outcome2
|
56
|
+
|
57
|
+
outcome2.parent = voc unless outcome2.parent
|
58
|
+
if outcome2.successful?
|
59
|
+
Fiber.yield outcome2
|
60
|
+
outcome2.clear
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
Fiber.yield nil
|
66
|
+
end
|
67
|
+
end # class
|
68
|
+
|
69
|
+
Disj2.instance.freeze
|
70
|
+
end # module
|
71
|
+
end # module
|
72
|
+
end # unless
|