mini_kraken 0.1.07 → 0.1.12

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/README.md +11 -3
  4. data/lib/mini_kraken/core/association_walker.rb +1 -1
  5. data/lib/mini_kraken/core/base_arg.rb +10 -0
  6. data/lib/mini_kraken/core/conde.rb +143 -0
  7. data/lib/mini_kraken/core/conj2.rb +59 -53
  8. data/lib/mini_kraken/core/def_relation.rb +49 -0
  9. data/lib/mini_kraken/core/disj2.rb +53 -48
  10. data/lib/mini_kraken/core/duck_fiber.rb +1 -1
  11. data/lib/mini_kraken/core/equals.rb +134 -126
  12. data/lib/mini_kraken/core/fail.rb +18 -14
  13. data/lib/mini_kraken/core/formal_arg.rb +22 -0
  14. data/lib/mini_kraken/core/formal_ref.rb +24 -0
  15. data/lib/mini_kraken/core/goal.rb +9 -3
  16. data/lib/mini_kraken/core/goal_arg.rb +5 -3
  17. data/lib/mini_kraken/core/goal_template.rb +60 -0
  18. data/lib/mini_kraken/core/k_boolean.rb +31 -0
  19. data/lib/mini_kraken/core/outcome.rb +14 -0
  20. data/lib/mini_kraken/core/relation.rb +7 -0
  21. data/lib/mini_kraken/core/succeed.rb +17 -13
  22. data/lib/mini_kraken/core/vocabulary.rb +22 -4
  23. data/lib/mini_kraken/glue/fresh_env.rb +45 -3
  24. data/lib/mini_kraken/glue/run_star_expression.rb +43 -26
  25. data/lib/mini_kraken/version.rb +1 -1
  26. data/spec/core/conde_spec.rb +147 -0
  27. data/spec/core/conj2_spec.rb +8 -9
  28. data/spec/core/def_relation_spec.rb +96 -0
  29. data/spec/core/disj2_spec.rb +0 -45
  30. data/spec/core/duck_fiber_spec.rb +12 -1
  31. data/spec/core/equals_spec.rb +3 -3
  32. data/spec/core/goal_template_spec.rb +74 -0
  33. data/spec/core/k_boolean_spec.rb +107 -0
  34. data/spec/core/outcome_spec.rb +48 -0
  35. data/spec/core/vocabulary_spec.rb +6 -0
  36. data/spec/glue/fresh_env_spec.rb +27 -1
  37. data/spec/glue/run_star_expression_spec.rb +535 -18
  38. data/spec/mini_kraken_spec.rb +2 -0
  39. data/spec/spec_helper.rb +0 -1
  40. data/spec/support/factory_methods.rb +15 -0
  41. metadata +19 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15536181da18de42e83ec948742a97c63338f2065841849de64d3537eeeb9bc1
4
- data.tar.gz: bb25b2bfcab17da55d5dfa6845e699fa2a48e89e19956f3c63f2e755577d4ae7
3
+ metadata.gz: ca30db7b93b0232f1df5321a1e00270395d908e3f7be1d24d3d5a9c19c833914
4
+ data.tar.gz: d64051987c0b09abf752b24604854515cabc9e99ff5ee436884c8522d6a75950
5
5
  SHA512:
6
- metadata.gz: a964e66c182fa2d834007c2003fa7b16b0e91d8cc80f8525de9c95df89d81523a9a2e93c7e57c2a96605ce6af5244324d5a7bf80c877d46a8a647726a3ac1e49
7
- data.tar.gz: 6608b250a6949ce6d7f3fd4c548c7525595fc20c808b5fa327b2cdc1b48ac5b37dd05ee92ae4a8ea2e9a5f76477c24afd5b20e6d1851061afca43952f8d75949
6
+ metadata.gz: f4fc60b256907317972c51410e52aec821553d7575c24d59b9421f4ba8dbd0136937e70ffb2f9f21556ed205b8b8d54796af68c222df0704b45ebbcf562c3ab3
7
+ data.tar.gz: 5caf5af0c016e8dfb5de40244ad71a083eb035e0bcb911ae7bd90e67a784789458c12e46a3cc75a96ae483fd0317d86b6610a7c339cd423050b4d57e642d6cc9
@@ -1,3 +1,53 @@
1
+ ## [0.1.12] - 2020-06-29
2
+ - Supports `conde`, that is, a relation that can take an arbitrary number of arguments.
3
+ - Cover all frames but one from Chapter One of "Reasoned Scheme" book.
4
+
5
+ ### New
6
+ - Class `Conde` a relation that succeeds for each of its successful arguments.
7
+
8
+ ### CHANGED
9
+ - Method `Goal#validated_actuals` add into account polyadic relations (= relations with arbitrary number of arguments)
10
+
11
+ ## [0.1.11] - 2020-06-25
12
+ - Supports `defrel`, that is, the capability to define new relations by combining other relations.
13
+ - Covers frames from "The Reasoned Scheme" book up to frame [1:87]
14
+
15
+ ### New
16
+ - Class `BaseArg` a generalization of goal or goal template argument.
17
+ - Class `DefRelation` A specialization of `Relation` class aimed for user-defined relation.
18
+ - Class `FormalArg` to represent goal template argument(s).
19
+ - Class `FormalRef` an allusion to a formal argument in a goal template.
20
+ - Class `GoalTemplate` a representation of a goal parametrized with formal arguments.
21
+ - Class `KBoolean` a MiniKraken representation of a boolean value.
22
+
23
+ ### CHANGED
24
+ - File `README.md` minor change: added more TODO's.
25
+
26
+ ## [0.1.10] - 2020-06-13
27
+ - Supports frames from "The Reasoned Scheme" book up to frame [1:81]
28
+
29
+ ### New
30
+ - Factory methods `Outcome#failure`, `Outcome#success`
31
+ - Method `Vocabulary#inspect`
32
+ - File `outcome_spec.rb`
33
+
34
+ ### FIXED
35
+ - `Conj2#conjunction` vocabulary wasn't cleared when outcome2 was nil.
36
+
37
+ ## [0.1.09] - 2020-06-06
38
+ - Supports frames from "The Reasoned Scheme" book up to frame [1:76]
39
+
40
+ ### CHANGED
41
+ - Method `FreshEnv#initialize`accepts an array of goals as second argument. This array is transformed into a conjunction of goals.
42
+ - Method `RunStarExpression#initialize` accepts multiple multiple variable names and goals.
43
+ - Method `RunStarExpression#run` can handle solutions with multiple variables.
44
+
45
+ ## [0.1.08] - 2020-05-30
46
+ - Fix of nasty bug (object aliasing) that caused flaky failures in specs.
47
+
48
+ ### FIXED
49
+ - `DuckFiber#resume` each call returns a distinct `Outcome` instance when successful.
50
+
1
51
  ## [0.1.07] - 2020-05-23
2
52
  - Implementation of `disj2` (two arguments disjunction - or -)
3
53
 
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
18
+ - [X] disj2
19
+ - [X] defrel
19
20
 
20
21
  ### TODO
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
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative 'conj2'
5
+ require_relative 'duck_fiber'
6
+ require_relative 'fail'
7
+ require_relative 'goal'
8
+ require_relative 'goal_relation'
9
+ require_relative 'outcome'
10
+
11
+ unless MiniKraken::Core.constants(false).include? :Conde
12
+ module MiniKraken
13
+ module Core
14
+ # A polyadic relation (i.e. it can takes an arbitrary number of argumentt)
15
+ # that behaves as the disjunction of its arguments.
16
+ # It succeeds if at least one of its goal arguments succeeds.
17
+ class Conde < GoalRelation
18
+ include Singleton
19
+
20
+ def initialize
21
+ super('conde', nil)
22
+ end
23
+
24
+ # A relation is polyadic when it accepts an arbitrary number of arguments.
25
+ # @return [TrueClass]
26
+ def polyadic?
27
+ true
28
+ end
29
+
30
+ # @param actuals [Array<Term>] A two-elements array
31
+ # @param anEnv [Vocabulary] A vocabulary object
32
+ # @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
33
+ def solver_for(actuals, anEnv)
34
+ args = *validated_args(actuals)
35
+ Fiber.new { cond(args, anEnv) }
36
+ end
37
+
38
+ # Yields [Outcome, NilClass] result of the disjunction
39
+ # @param goals [Array<Goal>] Array of goals
40
+ # @param voc [Vocabulary] A vocabulary object
41
+ def cond(goals, voc)
42
+ # require 'debug'
43
+ success = false
44
+
45
+ goals.each do |g|
46
+ fiber = nil
47
+
48
+ case g
49
+ when Core::Goal
50
+ fiber = g.attain(voc)
51
+ when Core::Environment
52
+ fiber = g.attain(voc)
53
+ when Array
54
+ conjunct = conjunction(g)
55
+ fiber = conjunct.attain(voc)
56
+ when Core::ConsCell
57
+ goal_array = to_goal_array(g)
58
+ conjunct = conjunction(goal_array)
59
+ fiber = conjunct.attain(voc)
60
+ end
61
+ loop do
62
+ outcome = fiber.resume
63
+ break unless outcome
64
+
65
+ outcome.parent = voc unless outcome.parent
66
+ if outcome.successful?
67
+ success = true
68
+ Fiber.yield outcome
69
+ outcome.clear
70
+ end
71
+ end
72
+ end
73
+
74
+ Fiber.yield Outcome.new(:"#u", voc) unless success
75
+ Fiber.yield nil
76
+ end
77
+
78
+ private
79
+
80
+ def validated_args(actuals)
81
+ result = []
82
+
83
+ actuals.each do |arg|
84
+ case arg
85
+ when Core::Goal
86
+ result << arg
87
+
88
+ when Core::Environment
89
+ result << arg
90
+
91
+ when Array
92
+ result << validated_args(arg)
93
+
94
+ else
95
+ prefix = "#{name} expects goal as argument, found a "
96
+ raise StandardError, prefix + "'#{arg.class}'"
97
+ end
98
+ end
99
+
100
+ result
101
+ end
102
+
103
+ def conjunction(goal_array)
104
+ result = nil
105
+
106
+ loop do
107
+ conjunctions = []
108
+ goal_array.each_slice(2) do |uno_duo|
109
+ if uno_duo.size == 2
110
+ conjunctions << Core::Goal.new(Core::Conj2.instance, uno_duo)
111
+ else
112
+ conjunctions << uno_duo[0]
113
+ end
114
+ end
115
+ if conjunctions.size == 1
116
+ result = conjunctions[0]
117
+ break
118
+ end
119
+ goal_array = conjunctions
120
+ end
121
+
122
+ result
123
+ end
124
+
125
+ def to_goal_array(aCons)
126
+ array = []
127
+ curr_node = aCons
128
+ loop do
129
+ array << curr_node.car if curr_node.car.kind_of?(Core::Goal)
130
+ break unless curr_node.cdr
131
+ break unless curr_node.car.kind_of?(Core::Goal)
132
+
133
+ curr_node = curr_node.cdr
134
+ end
135
+
136
+ array
137
+ end
138
+ end # class
139
+
140
+ Conde.instance.freeze
141
+ end # module
142
+ end # module
143
+ end # unless
@@ -6,68 +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
71
+
72
+ Fiber.yield nil
67
73
  end
74
+ end # class
68
75
 
69
- Fiber.yield nil
70
- end
71
- end # class
76
+ Conj2.instance.freeze
77
+ end # module
72
78
  end # module
73
- end # module
79
+ end # unless
@@ -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
@@ -2,66 +2,71 @@
2
2
 
3
3
  require 'singleton'
4
4
  require_relative 'duck_fiber'
5
+ require_relative 'fail'
5
6
  require_relative 'goal'
6
7
  require_relative 'goal_relation'
7
8
  require_relative 'outcome'
8
9
 
9
- module MiniKraken
10
- module Core
11
- # The disjunction is a relation that accepts only goal(s) as its two
12
- # arguments. It succeeds if at least one of its goal arguments succeeds.
13
- class Disj2 < GoalRelation
14
- include Singleton
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
15
17
 
16
- def initialize
17
- super('disj2', nil)
18
- end
18
+ def initialize
19
+ super('disj2', nil)
20
+ end
19
21
 
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 { disjunction(g1, g2, anEnv) }
26
- end
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
27
29
 
28
- # Yields [Outcome, NilClass] result of the disjunction
29
- # @param g1 [Goal] First goal argument
30
- # @param g2 [Goal] Second goal argument
31
- # @param voc [Vocabulary] A vocabulary object
32
- def disjunction(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
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
43
45
 
44
- outcome1.parent = voc unless outcome1.parent
45
- if outcome1.successful?
46
- Fiber.yield outcome1
47
- outcome1.clear
46
+ outcome1.parent = voc unless outcome1.parent
47
+ if outcome1.successful?
48
+ Fiber.yield outcome1
49
+ outcome1.clear
50
+ end
48
51
  end
49
- end
50
- f2 = g2.attain(voc)
51
- loop do
52
- outcome2 = f2.resume
53
- break unless outcome2
52
+ f2 = g2.attain(voc)
53
+ loop do
54
+ outcome2 = f2.resume
55
+ break unless outcome2
54
56
 
55
- outcome2.parent = voc unless outcome2.parent
56
- if outcome2.successful?
57
- Fiber.yield outcome2
58
- outcome2.clear
57
+ outcome2.parent = voc unless outcome2.parent
58
+ if outcome2.successful?
59
+ Fiber.yield outcome2
60
+ outcome2.clear
61
+ end
59
62
  end
60
63
  end
64
+
65
+ Fiber.yield nil
61
66
  end
67
+ end # class
62
68
 
63
- Fiber.yield nil
64
- end
65
- end # class
69
+ Disj2.instance.freeze
70
+ end # module
66
71
  end # module
67
- end # module
72
+ end # unless