mini_kraken 0.1.04 → 0.1.09
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 +54 -0
- data/Gemfile +3 -1
- data/README.md +8 -6
- data/Rakefile +5 -3
- data/lib/mini_kraken.rb +3 -1
- data/lib/mini_kraken/core/any_value.rb +9 -7
- data/lib/mini_kraken/core/association.rb +20 -7
- data/lib/mini_kraken/core/association_walker.rb +5 -1
- data/lib/mini_kraken/core/atomic_term.rb +5 -3
- data/lib/mini_kraken/core/binary_relation.rb +8 -6
- data/lib/mini_kraken/core/composite_goal.rb +46 -0
- data/lib/mini_kraken/core/composite_term.rb +7 -20
- data/lib/mini_kraken/core/conj2.rb +76 -0
- data/lib/mini_kraken/core/cons_cell.rb +51 -41
- 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 +4 -2
- data/lib/mini_kraken/core/environment.rb +25 -11
- data/lib/mini_kraken/core/equals.rb +127 -188
- data/lib/mini_kraken/core/fail.rb +20 -14
- data/lib/mini_kraken/core/freshness.rb +11 -8
- data/lib/mini_kraken/core/goal.rb +8 -4
- 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/k_integer.rb +4 -3
- data/lib/mini_kraken/core/k_symbol.rb +4 -3
- data/lib/mini_kraken/core/nullary_relation.rb +3 -1
- data/lib/mini_kraken/core/outcome.rb +29 -25
- data/lib/mini_kraken/core/relation.rb +4 -18
- data/lib/mini_kraken/core/succeed.rb +20 -14
- data/lib/mini_kraken/core/term.rb +7 -2
- data/lib/mini_kraken/core/variable.rb +11 -25
- data/lib/mini_kraken/core/variable_ref.rb +12 -59
- data/lib/mini_kraken/core/vocabulary.rb +267 -48
- data/lib/mini_kraken/glue/fresh_env.rb +44 -6
- data/lib/mini_kraken/glue/run_star_expression.rb +49 -23
- data/lib/mini_kraken/version.rb +3 -1
- data/mini_kraken.gemspec +15 -13
- data/spec/core/association_spec.rb +4 -4
- data/spec/core/association_walker_spec.rb +25 -24
- data/spec/core/conj2_spec.rb +114 -0
- data/spec/core/cons_cell_spec.rb +12 -3
- data/spec/core/disj2_spec.rb +99 -0
- data/spec/core/duck_fiber_spec.rb +22 -12
- data/spec/core/environment_spec.rb +16 -28
- data/spec/core/equals_spec.rb +7 -7
- data/spec/core/fail_spec.rb +7 -7
- data/spec/core/goal_spec.rb +10 -10
- data/spec/core/k_symbol_spec.rb +5 -6
- data/spec/core/succeed_spec.rb +4 -4
- data/spec/core/variable_ref_spec.rb +0 -4
- data/spec/core/vocabulary_spec.rb +33 -27
- data/spec/glue/fresh_env_spec.rb +28 -2
- data/spec/glue/run_star_expression_spec.rb +370 -59
- data/spec/mini_kraken_spec.rb +4 -0
- data/spec/spec_helper.rb +3 -2
- data/spec/support/factory_methods.rb +20 -2
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10a0607b5621effb10eab26d64ff7f52228f93e9c7907b91488613c1d72469aa
|
4
|
+
data.tar.gz: 9b43c9b0d729f2c7944be72f6599decae48d977ea0be5257a365111508473ef9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32c14d82b93ddf21d2b369745b1e556faf1736b53fd7f1782809370777e059c498e1d8347806dc2135aab3bedf429c8d2b93b5f9be4b9b361f86926edb1be20f
|
7
|
+
data.tar.gz: 9b82b3afd53af4da54042f8a8852367250bc8225c1229b31486b4e8223ca533d55f3dcc6250c4de4c03d0d84b77a5e5cf737a6c151f734d7a08334ac442fa330
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,57 @@
|
|
1
|
+
## [0.1.09] - 2020-06-06
|
2
|
+
- Supports frames from "The Reasoned Scheme" book up to frame [1:76]
|
3
|
+
|
4
|
+
### CHANGED
|
5
|
+
- Method `FreshEnv#initialize`accepts an array of goals as second argument. This array is transformed into a conjunction of goals.
|
6
|
+
- Method `RunStarExpression#initialize` accepts multiple multiple variable names and goals.
|
7
|
+
- Method `RunStarExpression#run` can handle solutions with multiple variables.
|
8
|
+
|
9
|
+
## [0.1.08] - 2020-05-30
|
10
|
+
- Fix of nasty bug (object aliasing) that caused flaky failures in specs.
|
11
|
+
|
12
|
+
### FIXED
|
13
|
+
- `DuckFiber#resume` each call returns a distinct `Outcome` instance when successful.
|
14
|
+
|
15
|
+
## [0.1.07] - 2020-05-23
|
16
|
+
- Implementation of `disj2` (two arguments disjunction - or -)
|
17
|
+
|
18
|
+
### New
|
19
|
+
- Class `Disj2` as subclass of `GoalRelation` that implements the disjunction of two subgoals
|
20
|
+
|
21
|
+
### CHANGED
|
22
|
+
- Class `Disj2`: common code with `Conj2` class factored out to superclass `GoalRelation`
|
23
|
+
- File `cons_cell.rb`: prevent multiple inclusions via different requires
|
24
|
+
- Method `Vocabulary#ancestor_walker` now returns an `Enumerator` instead of a `Fiber`.
|
25
|
+
|
26
|
+
### FIXED
|
27
|
+
- Method `RunStarExpression#run` clear associations and rankings for second and consecutive solmutions
|
28
|
+
|
29
|
+
|
30
|
+
## [0.1.06] - 2020-05-20
|
31
|
+
- Implementation of `conj2` (two arguments conjunction - and -)
|
32
|
+
|
33
|
+
### New
|
34
|
+
- Class `CompositeGoal`
|
35
|
+
- Class `Conj2` as subclass of `GoalRelation` that implements the conjunction of two subgoals
|
36
|
+
- Mixin module `Designation` to factor out the common methods in `Variable` and `VariableRef` classes
|
37
|
+
- Class `GoalArg` abstract class, that is a generalization for anything that be be argument of a goal.
|
38
|
+
- Class `GoalRelation` as subclass of `Relation`. A goal that is linked to a such relation may have goals as its arguments only.
|
39
|
+
|
40
|
+
### Changed
|
41
|
+
- Class `Goal` is new subclass of class `GoalArg`. Therefore a goal can be an argument to another goal.
|
42
|
+
- Class `Term` is new subclass of class `GoalArg`. Therefore a term can be an argument of a goal.
|
43
|
+
- Classes `Variable`, `VariableRef` now include mix-in module `Designation`
|
44
|
+
- File `cd_implementation.txt` Updated with changes of class relationship
|
45
|
+
|
46
|
+
## [0.1.05] - 2020-05-09
|
47
|
+
- Changed implementation of fused variables
|
48
|
+
- Magic comments for frozen string literal
|
49
|
+
- Code re-styling to please Rubocop 0.82
|
50
|
+
|
51
|
+
### Changed
|
52
|
+
- File `README.md` Added "What is mini_kraken" text.
|
53
|
+
- File `README.md` Added badges (CI Travis build status, Gem version, license)
|
54
|
+
|
1
55
|
## [0.1.04] - 2020-05-02
|
2
56
|
### Changed
|
3
57
|
- File `README.md` Added "What is mini_kraken" text.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -11,14 +11,16 @@ Daniel P. Friedman, William E. Byrd, Oleg Kiselyov, and Jason Hemann: "The Reaso
|
|
11
11
|
ISBN: 9780262535519, (2018), MIT Press.
|
12
12
|
|
13
13
|
### Features
|
14
|
-
[X] ==
|
15
|
-
[X] run\*
|
16
|
-
[X] fresh
|
14
|
+
- [X] ==
|
15
|
+
- [X] run\*
|
16
|
+
- [X] fresh
|
17
|
+
- [X] conj2
|
18
|
+
- [X] disj2
|
17
19
|
|
18
20
|
### TODO
|
19
|
-
[
|
20
|
-
[
|
21
|
-
[
|
21
|
+
- [ ] defrel
|
22
|
+
- [ ] conde
|
23
|
+
- [ ] Occurs check
|
22
24
|
|
23
25
|
## Installation
|
24
26
|
|
data/Rakefile
CHANGED
data/lib/mini_kraken.rb
CHANGED
@@ -1,31 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module MiniKraken
|
2
4
|
module Core
|
3
5
|
class AnyValue
|
4
6
|
attr_reader :rank
|
5
|
-
|
7
|
+
|
6
8
|
# @param aName [String]
|
7
9
|
# @param anEnv [Vocabulary]
|
8
10
|
def initialize(aName, anEnv, alternate_names = [])
|
9
11
|
@rank = anEnv.get_rank(aName, alternate_names)
|
10
12
|
end
|
11
|
-
|
13
|
+
|
12
14
|
def ==(other)
|
13
15
|
rank == other.rank
|
14
16
|
end
|
15
|
-
|
17
|
+
|
16
18
|
# Use same text representation as in Reasoned Schemer.
|
17
19
|
def to_s
|
18
20
|
"_#{rank}"
|
19
21
|
end
|
20
|
-
|
22
|
+
|
21
23
|
def ground?(_env)
|
22
24
|
false
|
23
25
|
end
|
24
|
-
|
26
|
+
|
25
27
|
# @return [AnyValue]
|
26
28
|
def quote(_env)
|
27
29
|
self
|
28
|
-
end
|
30
|
+
end
|
29
31
|
end # class
|
30
32
|
end # module
|
31
|
-
end # module
|
33
|
+
end # module
|
@@ -1,21 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module MiniKraken
|
2
4
|
module Core
|
3
5
|
# A record that a given vairable is associated with a value.
|
4
6
|
class Association
|
5
|
-
# @return [String] name of
|
6
|
-
|
7
|
-
|
7
|
+
# @return [String] internal name of variable being associated the value.
|
8
|
+
attr_accessor :i_name
|
9
|
+
|
8
10
|
# @return [Term] the MiniKraken value associated with the variable
|
9
11
|
attr_reader :value
|
10
|
-
|
11
|
-
|
12
|
+
|
13
|
+
|
12
14
|
# @param aVariable [Variable, String] A variable or its name.
|
13
15
|
# @param aValue [Term] value being associated to the variable.
|
14
16
|
def initialize(aVariable, aValue)
|
15
|
-
|
17
|
+
a_name = aVariable.respond_to?(:name) ? aVariable.i_name : aVariable
|
18
|
+
@i_name = validated_name(a_name)
|
16
19
|
@value = aValue
|
17
20
|
end
|
18
|
-
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def validated_name(aName)
|
25
|
+
raise StandardError, 'Name cannot be nil' if aName.nil?
|
26
|
+
|
27
|
+
cleaned = aName.strip
|
28
|
+
raise StandardError, 'Name cannot be empty or consists of spaces' if cleaned.empty?
|
29
|
+
|
30
|
+
cleaned
|
31
|
+
end
|
19
32
|
end # class
|
20
33
|
end # module
|
21
34
|
end # module
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
require_relative 'atomic_term'
|
3
5
|
require_relative 'composite_term'
|
@@ -23,6 +25,7 @@ module MiniKraken
|
|
23
25
|
def walk_assocs(assocs, anEnv)
|
24
26
|
# Treat easy cases first...
|
25
27
|
return nil if assocs.empty?
|
28
|
+
|
26
29
|
assoc_atomic = assocs.find { |assc| assc.value.kind_of?(AtomicTerm) }
|
27
30
|
return assoc_atomic.value if assoc_atomic
|
28
31
|
|
@@ -43,6 +46,7 @@ module MiniKraken
|
|
43
46
|
|
44
47
|
def walk_value(aTerm, anEnv)
|
45
48
|
return aTerm if aTerm.kind_of?(AtomicTerm) || aTerm.kind_of?(AnyValue)
|
49
|
+
|
46
50
|
result = nil
|
47
51
|
|
48
52
|
if aTerm.kind_of?(CompositeTerm)
|
@@ -176,4 +180,4 @@ module MiniKraken
|
|
176
180
|
end
|
177
181
|
end # class
|
178
182
|
end # module
|
179
|
-
end # module
|
183
|
+
end # module
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'term'
|
2
4
|
require_relative 'freshness'
|
3
5
|
|
@@ -22,14 +24,14 @@ module MiniKraken
|
|
22
24
|
def freshness(_env)
|
23
25
|
Freshness.new(:ground, self)
|
24
26
|
end
|
25
|
-
|
27
|
+
|
26
28
|
# An atomic term is a ground term: by definition it doesn't contain
|
27
29
|
# any fresh variable.
|
28
30
|
# @param _env [Vocabulary]
|
29
31
|
# @return [FalseClass]
|
30
32
|
def fresh?(_env)
|
31
33
|
false
|
32
|
-
end
|
34
|
+
end
|
33
35
|
|
34
36
|
# An atomic term is a ground term: by definition it doesn't contain
|
35
37
|
# any fresh variable.
|
@@ -61,4 +63,4 @@ module MiniKraken
|
|
61
63
|
end
|
62
64
|
end # class
|
63
65
|
end # module
|
64
|
-
end # module
|
66
|
+
end # module
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'relation'
|
2
4
|
require_relative 'composite_term'
|
3
5
|
|
@@ -5,20 +7,20 @@ module MiniKraken
|
|
5
7
|
module Core
|
6
8
|
class BinaryRelation < Relation
|
7
9
|
# @param aName [String] Name of the relation.
|
8
|
-
# @param alternateName [String, NilClass] Alternative name (optional).
|
10
|
+
# @param alternateName [String, NilClass] Alternative name (optional).
|
9
11
|
def initialize(aName, alternateName = nil)
|
10
12
|
super(aName, alternateName)
|
11
13
|
freeze
|
12
14
|
end
|
13
|
-
|
15
|
+
|
14
16
|
# Number of arguments for the relation.
|
15
17
|
# @return [Integer]
|
16
18
|
def arity
|
17
19
|
2
|
18
20
|
end
|
19
|
-
|
21
|
+
|
20
22
|
protected
|
21
|
-
|
23
|
+
|
22
24
|
# table: Commute
|
23
25
|
# |arg1 | arg2 | arg2.ground? || Commute |
|
24
26
|
# | isa? Atomic | isa? Atomic | dont_care || Yes |
|
@@ -55,7 +57,7 @@ module MiniKraken
|
|
55
57
|
else
|
56
58
|
[arg1, arg2]
|
57
59
|
end
|
58
|
-
end
|
60
|
+
end
|
59
61
|
end # class
|
60
62
|
end # module
|
61
|
-
end # module
|
63
|
+
end # module
|
@@ -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
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'term'
|
2
4
|
require_relative 'freshness'
|
3
5
|
|
@@ -6,17 +8,18 @@ module MiniKraken
|
|
6
8
|
# An composite term is an Minikraken term that can be
|
7
9
|
# decomposed into simpler MiniKraken data value(s).
|
8
10
|
class CompositeTerm < Term
|
9
|
-
|
11
|
+
# Abstract method (to override). Return the child terms.
|
12
|
+
# @return [Array<Term>]
|
10
13
|
def children
|
11
14
|
raise NotImplementedError, 'This method must re-defined in subclass(es).'
|
12
15
|
end
|
13
16
|
|
14
17
|
# A composite term is fresh when all its members are nil or all non-nil members
|
15
18
|
# are all fresh
|
16
|
-
# A composite term is bound when it is not fresh and not ground
|
19
|
+
# A composite term is bound when it is not fresh and not ground
|
17
20
|
# A composite term is a ground term when all its non-nil members are ground.
|
18
21
|
# @param _env [Vocabulary]
|
19
|
-
# @return [Freshness]
|
22
|
+
# @return [Freshness]
|
20
23
|
def freshness(_env)
|
21
24
|
env.freshness_composite(self)
|
22
25
|
end
|
@@ -33,22 +36,6 @@ module MiniKraken
|
|
33
36
|
child.nil? || child.ground?(anEnv)
|
34
37
|
end
|
35
38
|
end
|
36
|
-
|
37
|
-
# # Data equality testing
|
38
|
-
# # @return [Boolean]
|
39
|
-
# def ==(other)
|
40
|
-
# if other.respond_to?(:value)
|
41
|
-
# value == other.value
|
42
|
-
# else
|
43
|
-
# value == other
|
44
|
-
# end
|
45
|
-
# end
|
46
|
-
|
47
|
-
# # Type and data equality testing
|
48
|
-
# # @return [Boolean]
|
49
|
-
# def eql?(other)
|
50
|
-
# (self.class == other.class) && value.eql?(other.value)
|
51
|
-
# end
|
52
39
|
end # class
|
53
40
|
end # module
|
54
|
-
end # module
|
41
|
+
end # module
|
@@ -0,0 +1,76 @@
|
|
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
|
+
if g1.relation.kind_of?(Fail) || g2.relation.kind_of?(Fail)
|
35
|
+
Fiber.yield Outcome.new(:"#u", voc)
|
36
|
+
else
|
37
|
+
outcome1 = nil
|
38
|
+
outcome2 = nil
|
39
|
+
f1 = g1.attain(voc)
|
40
|
+
loop do
|
41
|
+
outcome1 = f1.resume
|
42
|
+
break unless outcome1
|
43
|
+
|
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
|
50
|
+
|
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
|
59
|
+
end
|
60
|
+
outcome2.clear
|
61
|
+
end
|
62
|
+
else
|
63
|
+
Fiber.yield outcome1
|
64
|
+
end
|
65
|
+
voc.clear if outcome1&.successful? && outcome2&.successful?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
Fiber.yield nil
|
70
|
+
end
|
71
|
+
end # class
|
72
|
+
|
73
|
+
Conj2.instance.freeze
|
74
|
+
end # module
|
75
|
+
end # module
|
76
|
+
end # unless
|