mini_kraken 0.1.05 → 0.1.10
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 -0
- data/README.md +5 -3
- data/lib/mini_kraken/core/composite_goal.rb +46 -0
- data/lib/mini_kraken/core/composite_term.rb +2 -0
- data/lib/mini_kraken/core/conj2.rb +79 -0
- data/lib/mini_kraken/core/cons_cell.rb +49 -43
- data/lib/mini_kraken/core/designation.rb +55 -0
- data/lib/mini_kraken/core/disj2.rb +71 -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 -132
- data/lib/mini_kraken/core/fail.rb +18 -14
- data/lib/mini_kraken/core/goal.rb +4 -2
- data/lib/mini_kraken/core/goal_arg.rb +10 -0
- data/lib/mini_kraken/core/goal_relation.rb +28 -0
- data/lib/mini_kraken/core/outcome.rb +40 -24
- data/lib/mini_kraken/core/succeed.rb +17 -13
- data/lib/mini_kraken/core/term.rb +5 -2
- data/lib/mini_kraken/core/variable.rb +3 -27
- data/lib/mini_kraken/core/variable_ref.rb +3 -28
- data/lib/mini_kraken/core/vocabulary.rb +39 -19
- data/lib/mini_kraken/glue/fresh_env.rb +45 -3
- data/lib/mini_kraken/glue/run_star_expression.rb +44 -19
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/core/conj2_spec.rb +114 -0
- data/spec/core/cons_cell_spec.rb +8 -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/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 +478 -53
- data/spec/mini_kraken_spec.rb +2 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/factory_methods.rb +16 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec7b984a9f0c857f74c8a8989c5da2072398775573b7ca6679d99f96ef13eac1
|
4
|
+
data.tar.gz: cdebdebae82c424b9ae701f959765f0905322798a371e2aec42ed9a229e35a4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ce3553527adeebef7adc69ec5a861fe65f9f451e54a81e23f00769923ced6b6a6011846ae3e9a673c8b9094a7dcce6ea6f8d263d55c8f543ef0de309e34c11c
|
7
|
+
data.tar.gz: 90b45061cde1119ad648359063682f411f9e5705a294340976bd027561b1d94da7afbfc9d0d27dce67942e1407d4d4ea40df85cf3fe67c9fca998d22052b6187
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,59 @@
|
|
1
|
+
## [0.1.10] - 2020-06-10
|
2
|
+
- Supports frames from "The Reasoned Scheme" book up to frame [1:81]
|
3
|
+
|
4
|
+
### New
|
5
|
+
- Factory methods `Outcome#failure`, `Outcome#success`
|
6
|
+
- Method `Vocabulary#inspect`
|
7
|
+
- File `outcome_spec.rb`
|
8
|
+
|
9
|
+
### FIXED
|
10
|
+
- `Conj2#conjunction` vocabulary wasn't cleared when outcome2 was nil.
|
11
|
+
|
12
|
+
## [0.1.09] - 2020-06-06
|
13
|
+
- Supports frames from "The Reasoned Scheme" book up to frame [1:76]
|
14
|
+
|
15
|
+
### CHANGED
|
16
|
+
- Method `FreshEnv#initialize`accepts an array of goals as second argument. This array is transformed into a conjunction of goals.
|
17
|
+
- Method `RunStarExpression#initialize` accepts multiple multiple variable names and goals.
|
18
|
+
- Method `RunStarExpression#run` can handle solutions with multiple variables.
|
19
|
+
|
20
|
+
## [0.1.08] - 2020-05-30
|
21
|
+
- Fix of nasty bug (object aliasing) that caused flaky failures in specs.
|
22
|
+
|
23
|
+
### FIXED
|
24
|
+
- `DuckFiber#resume` each call returns a distinct `Outcome` instance when successful.
|
25
|
+
|
26
|
+
## [0.1.07] - 2020-05-23
|
27
|
+
- Implementation of `disj2` (two arguments disjunction - or -)
|
28
|
+
|
29
|
+
### New
|
30
|
+
- Class `Disj2` as subclass of `GoalRelation` that implements the disjunction of two subgoals
|
31
|
+
|
32
|
+
### CHANGED
|
33
|
+
- Class `Disj2`: common code with `Conj2` class factored out to superclass `GoalRelation`
|
34
|
+
- File `cons_cell.rb`: prevent multiple inclusions via different requires
|
35
|
+
- Method `Vocabulary#ancestor_walker` now returns an `Enumerator` instead of a `Fiber`.
|
36
|
+
|
37
|
+
### FIXED
|
38
|
+
- Method `RunStarExpression#run` clear associations and rankings for second and consecutive solmutions
|
39
|
+
|
40
|
+
|
41
|
+
## [0.1.06] - 2020-05-20
|
42
|
+
- Implementation of `conj2` (two arguments conjunction - and -)
|
43
|
+
|
44
|
+
### New
|
45
|
+
- Class `CompositeGoal`
|
46
|
+
- Class `Conj2` as subclass of `GoalRelation` that implements the conjunction of two subgoals
|
47
|
+
- Mixin module `Designation` to factor out the common methods in `Variable` and `VariableRef` classes
|
48
|
+
- Class `GoalArg` abstract class, that is a generalization for anything that be be argument of a goal.
|
49
|
+
- Class `GoalRelation` as subclass of `Relation`. A goal that is linked to a such relation may have goals as its arguments only.
|
50
|
+
|
51
|
+
### Changed
|
52
|
+
- Class `Goal` is new subclass of class `GoalArg`. Therefore a goal can be an argument to another goal.
|
53
|
+
- Class `Term` is new subclass of class `GoalArg`. Therefore a term can be an argument of a goal.
|
54
|
+
- Classes `Variable`, `VariableRef` now include mix-in module `Designation`
|
55
|
+
- File `cd_implementation.txt` Updated with changes of class relationship
|
56
|
+
|
1
57
|
## [0.1.05] - 2020-05-09
|
2
58
|
- Changed implementation of fused variables
|
3
59
|
- Magic comments for frozen string literal
|
data/README.md
CHANGED
@@ -13,12 +13,14 @@ ISBN: 9780262535519, (2018), MIT Press.
|
|
13
13
|
### Features
|
14
14
|
- [X] ==
|
15
15
|
- [X] run\*
|
16
|
-
- [X] fresh
|
16
|
+
- [X] fresh
|
17
|
+
- [X] conj2
|
18
|
+
- [X] disj2
|
17
19
|
|
18
20
|
### TODO
|
19
|
-
- [ ]
|
20
|
-
- [ ] conj2
|
21
|
+
- [ ] defrel
|
21
22
|
- [ ] conde
|
23
|
+
- [ ] Occurs check
|
22
24
|
|
23
25
|
## Installation
|
24
26
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'environment'
|
4
|
+
|
5
|
+
module MiniKraken
|
6
|
+
module Core
|
7
|
+
class CompositeGoal
|
8
|
+
# @return [Operator] The operator corresponding to this goal
|
9
|
+
attr_reader :operator
|
10
|
+
|
11
|
+
# @return [Array<Goal>] The child goals (sub-goals)
|
12
|
+
attr_reader :children
|
13
|
+
|
14
|
+
# @param anOperator [Operator] The operator corresponding to this goal
|
15
|
+
# @param theChildren [Array<Goal>] The child goals (sub-goals)
|
16
|
+
def initialize(anOperator, theChildren)
|
17
|
+
@operator = anOperator
|
18
|
+
@children = validated_children(theChildren)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Attempt to achieve the goal for a given context (environment)
|
22
|
+
# @param anEnv [Environment] The context in which the goal take place.
|
23
|
+
# @return [Fiber<Outcome>] A Fiber object that will generate the results.
|
24
|
+
def attain(anEnv)
|
25
|
+
operator.solver_for(children, anEnv)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def validated_children(theChildren)
|
31
|
+
my_arity = operator.arity
|
32
|
+
if args.size != my_arity
|
33
|
+
err_msg = "Goal has #{theChildren.size} arguments, expected #{my_arity}"
|
34
|
+
raise StandardError, err_msg
|
35
|
+
end
|
36
|
+
|
37
|
+
prefix = 'Invalid goal argument '
|
38
|
+
theChildren.each do |subg|
|
39
|
+
raise StandardError, prefix + subg.to_s unless subg.kind_of?(Goal)
|
40
|
+
end
|
41
|
+
|
42
|
+
theChildren.dup
|
43
|
+
end
|
44
|
+
end # class
|
45
|
+
end # module
|
46
|
+
end # module
|
@@ -8,6 +8,8 @@ module MiniKraken
|
|
8
8
|
# An composite term is an Minikraken term that can be
|
9
9
|
# decomposed into simpler MiniKraken data value(s).
|
10
10
|
class CompositeTerm < Term
|
11
|
+
# Abstract method (to override). Return the child terms.
|
12
|
+
# @return [Array<Term>]
|
11
13
|
def children
|
12
14
|
raise NotImplementedError, 'This method must re-defined in subclass(es).'
|
13
15
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require_relative 'duck_fiber'
|
5
|
+
require_relative 'goal'
|
6
|
+
require_relative 'goal_relation'
|
7
|
+
require_relative 'outcome'
|
8
|
+
|
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
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
super('conj2', nil)
|
19
|
+
end
|
20
|
+
|
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
|
28
|
+
|
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
|
44
|
+
|
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
|
51
|
+
|
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
|
62
|
+
end
|
63
|
+
else
|
64
|
+
Fiber.yield outcome1
|
65
|
+
end
|
66
|
+
if outcome1.successful? && (outcome2&.successful? || outcome2.nil?)
|
67
|
+
voc.clear
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
Fiber.yield nil
|
73
|
+
end
|
74
|
+
end # class
|
75
|
+
|
76
|
+
Conj2.instance.freeze
|
77
|
+
end # module
|
78
|
+
end # module
|
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,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniKraken
|
4
|
+
module Core
|
5
|
+
# Wordnet definition: Identifying word or words by which someone or
|
6
|
+
# something is called and classified or distinguished from others.
|
7
|
+
# Mix-in module that contains factored code for managing named entries
|
8
|
+
# in a vocabulary such as variables and variable references.
|
9
|
+
module Designation
|
10
|
+
# @return [String] User-defined name of the variable
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
def init_designation(aName)
|
14
|
+
@name = valid_name(aName)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param voc [Vocabulary]
|
18
|
+
# @return [Freshness]
|
19
|
+
def freshness(voc)
|
20
|
+
voc.freshness_ref(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param voc [Vocabulary]
|
24
|
+
# @return [Boolean]
|
25
|
+
def fresh?(voc)
|
26
|
+
frsh = freshness(voc)
|
27
|
+
frsh.degree == :fresh || frsh.degree == :bound
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param voc [Vocabulary]
|
31
|
+
# @return [Boolean]
|
32
|
+
def bound?(voc)
|
33
|
+
frsh = freshness(voc)
|
34
|
+
frsh.degree == :bound
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param voc [Vocabulary]
|
38
|
+
# @return [Boolean]
|
39
|
+
def ground?(voc)
|
40
|
+
frsh = freshness(voc)
|
41
|
+
frsh.degree == :bound
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def valid_name(aName)
|
47
|
+
if aName.empty?
|
48
|
+
raise StandardError, 'Variable name may not be empty.'
|
49
|
+
end
|
50
|
+
|
51
|
+
aName
|
52
|
+
end
|
53
|
+
end # class
|
54
|
+
end # module
|
55
|
+
end # module
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require_relative 'duck_fiber'
|
5
|
+
require_relative 'goal'
|
6
|
+
require_relative 'goal_relation'
|
7
|
+
require_relative 'outcome'
|
8
|
+
|
9
|
+
unless MiniKraken::Core.constants(false).include? :Disj2
|
10
|
+
module MiniKraken
|
11
|
+
module Core
|
12
|
+
# The disjunction is a relation that accepts only goal(s) as its two
|
13
|
+
# arguments. It succeeds if at least one of its goal arguments succeeds.
|
14
|
+
class Disj2 < GoalRelation
|
15
|
+
include Singleton
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
super('disj2', nil)
|
19
|
+
end
|
20
|
+
|
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 { disjunction(g1, g2, anEnv) }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Yields [Outcome, NilClass] result of the disjunction
|
30
|
+
# @param g1 [Goal] First goal argument
|
31
|
+
# @param g2 [Goal] Second goal argument
|
32
|
+
# @param voc [Vocabulary] A vocabulary object
|
33
|
+
def disjunction(g1, g2, voc)
|
34
|
+
# require 'debug'
|
35
|
+
outcome1 = nil
|
36
|
+
outcome2 = nil
|
37
|
+
if g1.relation.kind_of?(Fail) && g2.relation.kind_of?(Fail)
|
38
|
+
Fiber.yield Outcome.new(:"#u", voc)
|
39
|
+
else
|
40
|
+
f1 = g1.attain(voc)
|
41
|
+
loop do
|
42
|
+
outcome1 = f1.resume
|
43
|
+
break unless outcome1
|
44
|
+
|
45
|
+
outcome1.parent = voc unless outcome1.parent
|
46
|
+
if outcome1.successful?
|
47
|
+
Fiber.yield outcome1
|
48
|
+
outcome1.clear
|
49
|
+
end
|
50
|
+
end
|
51
|
+
f2 = g2.attain(voc)
|
52
|
+
loop do
|
53
|
+
outcome2 = f2.resume
|
54
|
+
break unless outcome2
|
55
|
+
|
56
|
+
outcome2.parent = voc unless outcome2.parent
|
57
|
+
if outcome2.successful?
|
58
|
+
Fiber.yield outcome2
|
59
|
+
outcome2.clear
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Fiber.yield nil
|
65
|
+
end
|
66
|
+
end # class
|
67
|
+
|
68
|
+
Disj2.instance.freeze
|
69
|
+
end # module
|
70
|
+
end # module
|
71
|
+
end # unless
|