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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -1
  3. data/CHANGELOG.md +56 -1
  4. data/README.md +11 -3
  5. data/lib/mini_kraken/core/association_walker.rb +1 -1
  6. data/lib/mini_kraken/core/base_arg.rb +10 -0
  7. data/lib/mini_kraken/core/conj2.rb +58 -65
  8. data/lib/mini_kraken/core/cons_cell.rb +49 -43
  9. data/lib/mini_kraken/core/def_relation.rb +49 -0
  10. data/lib/mini_kraken/core/disj2.rb +72 -0
  11. data/lib/mini_kraken/core/duck_fiber.rb +1 -1
  12. data/lib/mini_kraken/core/environment.rb +1 -1
  13. data/lib/mini_kraken/core/equals.rb +134 -126
  14. data/lib/mini_kraken/core/fail.rb +18 -14
  15. data/lib/mini_kraken/core/formal_arg.rb +22 -0
  16. data/lib/mini_kraken/core/formal_ref.rb +24 -0
  17. data/lib/mini_kraken/core/goal_arg.rb +5 -3
  18. data/lib/mini_kraken/core/goal_relation.rb +13 -0
  19. data/lib/mini_kraken/core/goal_template.rb +60 -0
  20. data/lib/mini_kraken/core/k_boolean.rb +31 -0
  21. data/lib/mini_kraken/core/outcome.rb +40 -24
  22. data/lib/mini_kraken/core/succeed.rb +17 -13
  23. data/lib/mini_kraken/core/vocabulary.rb +37 -17
  24. data/lib/mini_kraken/glue/fresh_env.rb +45 -3
  25. data/lib/mini_kraken/glue/run_star_expression.rb +45 -19
  26. data/lib/mini_kraken/version.rb +1 -1
  27. data/spec/core/conj2_spec.rb +8 -9
  28. data/spec/core/cons_cell_spec.rb +8 -0
  29. data/spec/core/def_relation_spec.rb +96 -0
  30. data/spec/core/disj2_spec.rb +99 -0
  31. data/spec/core/duck_fiber_spec.rb +12 -1
  32. data/spec/core/equals_spec.rb +3 -3
  33. data/spec/core/goal_template_spec.rb +74 -0
  34. data/spec/core/k_boolean_spec.rb +107 -0
  35. data/spec/core/outcome_spec.rb +48 -0
  36. data/spec/core/vocabulary_spec.rb +11 -5
  37. data/spec/glue/fresh_env_spec.rb +27 -1
  38. data/spec/glue/run_star_expression_spec.rb +538 -70
  39. data/spec/mini_kraken_spec.rb +2 -0
  40. data/spec/spec_helper.rb +0 -1
  41. data/spec/support/factory_methods.rb +17 -1
  42. metadata +19 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 800d58c5efac32c005931c5a019f4440060728d1061c381bd10f95d554a525b1
4
- data.tar.gz: 217448cd67ca952439e81da5c89393ddd57d061d7ca9d5ed310119d1aab32cbf
3
+ metadata.gz: 672521fbc6627e2767bfe4d6ce38dd66243736fee451e857ec7ef45d0f8905fe
4
+ data.tar.gz: 6835bb1d91bb6e867417faba0d6904734295f9a7bab612c6a78fcfbcdc41f84a
5
5
  SHA512:
6
- metadata.gz: e52d5bdc7a34e1b303f2423fd8463766852bd61dc14d77e7bce43bcf653fd43d2331eb8f4bbc1beee13958e4621c9729b728d0cf5bbb5336bd5e277bf859a473
7
- data.tar.gz: 75b7a091579f667b41949905861ec314eb6a269ca5316b67df80cbf9f02d6d86ecd1c0ff74d61de3018c802e7c48ff7a2e4719bcb75f0ef45cca0092ebd3cb25
6
+ metadata.gz: bd5b5b2d2493b4ec8f8293c5f786be1a7739d05bee5d4733d48edb16713d1df4097be5a6e85127c2c90cf395da877b57ef07a607167b8f5bb1732dbc2a367610
7
+ data.tar.gz: 6661aadbe8bd778283e91f31181651e32296150d282575697fee1f5c240ea46168c5107baf33d9b590d6a449ec89b2f41e69340b81cb9b9e3692954af155083b
@@ -3,5 +3,9 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.6.3
6
+ - 2.7.1
7
+ - 2.6.6
8
+ - 2.5.8
9
+ - 2.4.10
10
+ - jruby-head
7
11
  before_install: gem install bundler -v 2.0.2
@@ -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:
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'set'
4
4
  require_relative 'atomic_term'
5
- require_relative 'composite_term'
5
+ require_relative 'cons_cell'
6
6
 
7
7
  module MiniKraken
8
8
  module Core
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniKraken
4
+ module Core
5
+ # Abstract class that is a generalization for goal actual arguments or
6
+ # for arguments of goal template.
7
+ class BaseArg
8
+ end # class
9
+ end # module
10
+ end # module
@@ -6,81 +6,74 @@ require_relative 'goal'
6
6
  require_relative 'goal_relation'
7
7
  require_relative 'outcome'
8
8
 
9
- module MiniKraken
10
- module Core
11
- # The conjunction is a relation that accepts only goal(s) as its two
12
- # arguments. It succeeds if and only both its goal arguments succeeds.
13
- class Conj2 < GoalRelation
14
- include Singleton
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
- def initialize
17
- super('conj2', nil)
18
- end
17
+ def initialize
18
+ super('conj2', nil)
19
+ end
19
20
 
20
- # @param actuals [Array<Term>] A two-elements array
21
- # @param anEnv [Vocabulary] A vocabulary object
22
- # @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
23
- def solver_for(actuals, anEnv)
24
- g1, g2 = *validated_args(actuals)
25
- Fiber.new { conjunction(g1, g2, anEnv) }
26
- end
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
- # Yields [Outcome, NilClass] result of the conjunction
29
- # @param g1 [Goal] First goal argument
30
- # @param g2 [Goal] Second goal argument
31
- # @param voc [Vocabulary] A vocabulary object
32
- def conjunction(g1, g2, voc)
33
- # require 'debug'
34
- outcome1 = nil
35
- outcome2 = nil
36
- if g1.relation.kind_of?(Fail) || g2.relation.kind_of?(Fail)
37
- Fiber.yield Outcome.new(:"#u", voc)
38
- else
39
- f1 = g1.attain(voc)
40
- loop do
41
- outcome1 = f1.resume
42
- break unless outcome1
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
- outcome1.parent = voc unless outcome1.parent
45
- if outcome1.successful?
46
- f2 = g2.attain(outcome1)
47
- loop do
48
- outcome2 = f2.resume
49
- break unless outcome2
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
- outcome2.parent = voc unless outcome2.parent
52
- if outcome2.successful?
53
- res = Outcome.new(:"#s", voc)
54
- res.merge(outcome1)
55
- res.merge(outcome2)
56
- Fiber.yield res
57
- else
58
- Fiber.yield outcome2
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
- outcome2.clear
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
- Fiber.yield nil
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
- actuals
83
- end
84
- end # class
76
+ Conj2.instance.freeze
77
+ end # module
85
78
  end # module
86
- end # module
79
+ end # unless
@@ -2,47 +2,53 @@
2
2
 
3
3
  require_relative 'composite_term'
4
4
 
5
- module MiniKraken
6
- module Core
7
- class ConsCell < CompositeTerm
8
- attr_reader :car
9
- attr_reader :cdr
10
-
11
- def initialize(obj1, obj2 = nil)
12
- @car = obj1
13
- @cdr = obj2
14
- end
15
-
16
- def children
17
- [car, cdr]
18
- end
19
-
20
- # Return true if it is an empty list, otherwise false.
21
- # A list is empty, when both car and cdr fields are nil.
22
- def null?
23
- car.nil? && cdr.nil?
24
- end
25
-
26
- def ==(other)
27
- return false unless other.respond_to?(:car)
28
-
29
- (car == other.car) && (cdr == other.cdr)
30
- end
31
-
32
- def eql?(other)
33
- (self.class == other.class) && car.eql?(other.car) && cdr.eql?(other.cdr)
34
- end
35
-
36
- def quote(anEnv)
37
- return self if null?
38
-
39
- new_car = car.nil? ? nil : car.quote(anEnv)
40
- new_cdr = cdr.nil? ? nil : cdr.quote(anEnv)
41
- ConsCell.new(new_car, new_cdr)
42
- end
43
- end # class
44
-
45
- # Constant representing the null (empty) list.
46
- NullList = ConsCell.new(nil, nil).freeze
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 # module
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