mini_kraken 0.2.03 → 0.3.03
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/.rubocop.yml +378 -333
- data/CHANGELOG.md +48 -0
- data/README.md +29 -21
- data/lib/mini_kraken/atomic/all_atomic.rb +5 -0
- data/lib/mini_kraken/atomic/atomic_term.rb +96 -0
- data/lib/mini_kraken/atomic/k_boolean.rb +42 -0
- data/lib/mini_kraken/{core → atomic}/k_integer.rb +2 -5
- data/lib/mini_kraken/atomic/k_string.rb +17 -0
- data/lib/mini_kraken/{core → atomic}/k_symbol.rb +4 -8
- data/lib/mini_kraken/composite/all_composite.rb +4 -0
- data/lib/mini_kraken/composite/composite_term.rb +27 -0
- data/lib/mini_kraken/composite/cons_cell.rb +301 -0
- data/lib/mini_kraken/composite/cons_cell_visitor.rb +50 -0
- data/lib/mini_kraken/composite/list.rb +32 -0
- data/lib/mini_kraken/core/all_core.rb +8 -0
- data/lib/mini_kraken/core/any_value.rb +31 -7
- data/lib/mini_kraken/core/arity.rb +69 -0
- data/lib/mini_kraken/core/association.rb +29 -4
- data/lib/mini_kraken/core/association_copy.rb +50 -0
- data/lib/mini_kraken/core/base_term.rb +13 -0
- data/lib/mini_kraken/core/blackboard.rb +315 -0
- data/lib/mini_kraken/core/bookmark.rb +46 -0
- data/lib/mini_kraken/core/context.rb +492 -0
- data/lib/mini_kraken/core/duck_fiber.rb +21 -19
- data/lib/mini_kraken/core/entry.rb +40 -0
- data/lib/mini_kraken/core/fail.rb +20 -18
- data/lib/mini_kraken/core/fusion.rb +29 -0
- data/lib/mini_kraken/core/goal.rb +20 -29
- data/lib/mini_kraken/core/log_var.rb +22 -0
- data/lib/mini_kraken/core/log_var_ref.rb +108 -0
- data/lib/mini_kraken/core/nullary_relation.rb +2 -9
- data/lib/mini_kraken/core/parametrized_term.rb +68 -0
- data/lib/mini_kraken/core/relation.rb +14 -28
- data/lib/mini_kraken/core/scope.rb +67 -0
- data/lib/mini_kraken/core/solver_adapter.rb +58 -0
- data/lib/mini_kraken/core/specification.rb +48 -0
- data/lib/mini_kraken/core/succeed.rb +21 -17
- data/lib/mini_kraken/core/symbol_table.rb +137 -0
- data/lib/mini_kraken/core/term.rb +15 -4
- data/lib/mini_kraken/glue/dsl.rb +44 -88
- data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
- data/lib/mini_kraken/rela/all_rela.rb +8 -0
- data/lib/mini_kraken/rela/binary_relation.rb +30 -0
- data/lib/mini_kraken/rela/conde.rb +143 -0
- data/lib/mini_kraken/rela/conj2.rb +65 -0
- data/lib/mini_kraken/rela/def_relation.rb +93 -0
- data/lib/mini_kraken/rela/disj2.rb +70 -0
- data/lib/mini_kraken/rela/fresh.rb +98 -0
- data/lib/mini_kraken/{core → rela}/goal_relation.rb +7 -9
- data/lib/mini_kraken/rela/unify.rb +265 -0
- data/lib/mini_kraken/version.rb +1 -1
- data/mini_kraken.gemspec +2 -2
- data/spec/.rubocop.yml +1 -1
- data/spec/atomic/atomic_term_spec.rb +98 -0
- data/spec/{core → atomic}/k_boolean_spec.rb +19 -34
- data/spec/{core → atomic}/k_symbol_spec.rb +3 -16
- data/spec/composite/cons_cell_spec.rb +225 -0
- data/spec/{core → composite}/cons_cell_visitor_spec.rb +36 -20
- data/spec/composite/list_spec.rb +50 -0
- data/spec/core/any_value_spec.rb +52 -0
- data/spec/core/arity_spec.rb +92 -0
- data/spec/core/association_copy_spec.rb +69 -0
- data/spec/core/association_spec.rb +31 -4
- data/spec/core/blackboard_spec.rb +287 -0
- data/spec/core/bookmark_spec.rb +40 -0
- data/spec/core/context_spec.rb +245 -0
- data/spec/core/core_spec.rb +40 -0
- data/spec/core/duck_fiber_spec.rb +16 -46
- data/spec/core/fail_spec.rb +5 -6
- data/spec/core/goal_spec.rb +22 -12
- data/spec/core/log_var_ref_spec.rb +105 -0
- data/spec/core/log_var_spec.rb +64 -0
- data/spec/core/nullary_relation_spec.rb +33 -0
- data/spec/core/parametrized_tem_spec.rb +39 -0
- data/spec/core/relation_spec.rb +33 -0
- data/spec/core/scope_spec.rb +73 -0
- data/spec/core/solver_adapter_spec.rb +70 -0
- data/spec/core/specification_spec.rb +43 -0
- data/spec/core/succeed_spec.rb +5 -5
- data/spec/core/symbol_table_spec.rb +142 -0
- data/spec/glue/dsl_chap1_spec.rb +88 -144
- data/spec/glue/dsl_chap2_spec.rb +454 -19
- data/spec/glue/run_star_expression_spec.rb +81 -906
- data/spec/rela/conde_spec.rb +153 -0
- data/spec/rela/conj2_spec.rb +123 -0
- data/spec/rela/def_relation_spec.rb +119 -0
- data/spec/rela/disj2_spec.rb +117 -0
- data/spec/rela/fresh_spec.rb +147 -0
- data/spec/rela/unify_spec.rb +369 -0
- data/spec/support/factory_atomic.rb +29 -0
- data/spec/support/factory_composite.rb +21 -0
- data/spec/support/factory_methods.rb +11 -26
- metadata +98 -70
- data/lib/mini_kraken/core/association_walker.rb +0 -183
- data/lib/mini_kraken/core/atomic_term.rb +0 -67
- data/lib/mini_kraken/core/base_arg.rb +0 -10
- data/lib/mini_kraken/core/binary_relation.rb +0 -63
- data/lib/mini_kraken/core/composite_goal.rb +0 -46
- data/lib/mini_kraken/core/composite_term.rb +0 -41
- data/lib/mini_kraken/core/conde.rb +0 -143
- data/lib/mini_kraken/core/conj2.rb +0 -79
- data/lib/mini_kraken/core/cons_cell.rb +0 -82
- data/lib/mini_kraken/core/cons_cell_visitor.rb +0 -102
- data/lib/mini_kraken/core/def_relation.rb +0 -53
- data/lib/mini_kraken/core/designation.rb +0 -55
- data/lib/mini_kraken/core/disj2.rb +0 -72
- data/lib/mini_kraken/core/environment.rb +0 -73
- data/lib/mini_kraken/core/equals.rb +0 -193
- data/lib/mini_kraken/core/formal_arg.rb +0 -22
- data/lib/mini_kraken/core/formal_ref.rb +0 -25
- data/lib/mini_kraken/core/freshness.rb +0 -45
- data/lib/mini_kraken/core/goal_arg.rb +0 -12
- data/lib/mini_kraken/core/goal_template.rb +0 -102
- data/lib/mini_kraken/core/k_boolean.rb +0 -35
- data/lib/mini_kraken/core/outcome.rb +0 -63
- data/lib/mini_kraken/core/variable.rb +0 -41
- data/lib/mini_kraken/core/variable_ref.rb +0 -84
- data/lib/mini_kraken/core/vocabulary.rb +0 -446
- data/lib/mini_kraken/glue/fresh_env.rb +0 -103
- data/lib/mini_kraken/glue/fresh_env_factory.rb +0 -83
- data/spec/core/association_walker_spec.rb +0 -192
- data/spec/core/conde_spec.rb +0 -147
- data/spec/core/conj2_spec.rb +0 -114
- data/spec/core/cons_cell_spec.rb +0 -107
- data/spec/core/def_relation_spec.rb +0 -97
- data/spec/core/disj2_spec.rb +0 -99
- data/spec/core/environment_spec.rb +0 -142
- data/spec/core/equals_spec.rb +0 -317
- data/spec/core/goal_template_spec.rb +0 -74
- data/spec/core/outcome_spec.rb +0 -56
- data/spec/core/variable_ref_spec.rb +0 -30
- data/spec/core/variable_spec.rb +0 -35
- data/spec/core/vocabulary_spec.rb +0 -219
- data/spec/glue/fresh_env_factory_spec.rb +0 -97
- data/spec/glue/fresh_env_spec.rb +0 -62
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module MiniKraken
|
4
|
-
module Core
|
5
|
-
# The generalization of any iem that can be
|
6
|
-
# passed as arugement to a goal.
|
7
|
-
class FormalArg
|
8
|
-
# @return [String]
|
9
|
-
attr_reader :name
|
10
|
-
|
11
|
-
def initialize(aName)
|
12
|
-
@name = validated_name(aName)
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def validated_name(aName)
|
18
|
-
aName
|
19
|
-
end
|
20
|
-
end # class
|
21
|
-
end # module
|
22
|
-
end # module
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base_arg'
|
4
|
-
|
5
|
-
module MiniKraken
|
6
|
-
module Core
|
7
|
-
# A formal reference represents the occurrence of a formal argument name in a
|
8
|
-
# goal template argument list.
|
9
|
-
class FormalRef < BaseArg
|
10
|
-
# @return [String] The name of a formal argument.
|
11
|
-
attr_reader :name
|
12
|
-
|
13
|
-
def initialize(aName)
|
14
|
-
super()
|
15
|
-
@name = validated_name(aName)
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def validated_name(aName)
|
21
|
-
aName
|
22
|
-
end
|
23
|
-
end # class
|
24
|
-
end # module
|
25
|
-
end # module
|
@@ -1,45 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module MiniKraken
|
4
|
-
module Core
|
5
|
-
# Freshness: fresh, bound, ground
|
6
|
-
# fresh: no association at all
|
7
|
-
# bound: associated to something that is itself not ground.
|
8
|
-
# ground: associated to something that is either an atomic, a composite with ground members,
|
9
|
-
# a variable reference to something that is itself ground.
|
10
|
-
# RS fresh == fresh or bound
|
11
|
-
# RS not fresh == ground
|
12
|
-
# RS result == fresh => any or bound => expr(any)
|
13
|
-
Freshness = Struct.new(:degree, :associated) do
|
14
|
-
def initialize(aDegree, anAssociated)
|
15
|
-
super(aDegree, valid_associated(anAssociated))
|
16
|
-
end
|
17
|
-
|
18
|
-
def fresh?
|
19
|
-
degree == :fresh
|
20
|
-
end
|
21
|
-
|
22
|
-
def bound?
|
23
|
-
degree == :bound
|
24
|
-
end
|
25
|
-
|
26
|
-
def ground?
|
27
|
-
degree == :ground
|
28
|
-
end
|
29
|
-
|
30
|
-
# Does this instance represent something fresh according to
|
31
|
-
# "Reasoned Schemer" book ?
|
32
|
-
def rs_fresh?
|
33
|
-
degree != ground
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def valid_associated(anAssociated)
|
39
|
-
raise StandardError, 'Wrong argument' if anAssociated.kind_of?(self.class)
|
40
|
-
|
41
|
-
anAssociated
|
42
|
-
end
|
43
|
-
end # struct
|
44
|
-
end # module
|
45
|
-
end # module
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base_arg'
|
4
|
-
|
5
|
-
module MiniKraken
|
6
|
-
module Core
|
7
|
-
# The generalization of any item that can be
|
8
|
-
# passed as arugement to a goal object
|
9
|
-
class GoalArg < BaseArg
|
10
|
-
end # class
|
11
|
-
end # module
|
12
|
-
end # module
|
@@ -1,102 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base_arg'
|
4
|
-
require_relative 'cons_cell_visitor'
|
5
|
-
|
6
|
-
module MiniKraken
|
7
|
-
module Core
|
8
|
-
# A meta-goal that is parametrized with generic formal arguments.
|
9
|
-
# The individual goals are instantiated when the formal arguments
|
10
|
-
# are bound to goal arguments.
|
11
|
-
class GoalTemplate < BaseArg
|
12
|
-
# @return [Array<BaseArg>] Arguments of goal template.
|
13
|
-
attr_reader :args
|
14
|
-
|
15
|
-
# @return [Relation] Main relation for the goal template
|
16
|
-
attr_reader :relation
|
17
|
-
|
18
|
-
# @param aRelation [Core::Rzlation] the relation
|
19
|
-
# @param theArgs [Array<Core::BaseArg>] Arguments of goal template.
|
20
|
-
def initialize(aRelation, theArgs)
|
21
|
-
super()
|
22
|
-
@relation = validated_relation(aRelation)
|
23
|
-
@args = validated_args(theArgs)
|
24
|
-
args.freeze
|
25
|
-
end
|
26
|
-
|
27
|
-
# Factory method: Create a goal object.
|
28
|
-
# @param formals [Array<FormalArg>] Array of formal arguments
|
29
|
-
# @param actuals [Array<GoalArg>] Array of actual arguments
|
30
|
-
# @return [Goal] instantiate a goal object given the actuals and environment
|
31
|
-
def instantiate(formals, actuals)
|
32
|
-
formals2actuals = {}
|
33
|
-
formals.each_with_index do |frml, i|
|
34
|
-
formals2actuals[frml.name] = actuals[i]
|
35
|
-
end
|
36
|
-
|
37
|
-
do_instantiate(formals2actuals)
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def validated_relation(aRelation)
|
43
|
-
aRelation
|
44
|
-
end
|
45
|
-
|
46
|
-
def validated_args(theArgs)
|
47
|
-
theArgs
|
48
|
-
end
|
49
|
-
|
50
|
-
def do_instantiate(formals2actuals)
|
51
|
-
goal_args = []
|
52
|
-
args.each do |arg|
|
53
|
-
if arg.kind_of?(FormalRef)
|
54
|
-
goal_args << formals2actuals[arg.name]
|
55
|
-
elsif arg.kind_of?(GoalTemplate)
|
56
|
-
goal_args << arg.send(:do_instantiate, formals2actuals)
|
57
|
-
elsif arg.kind_of?(ConsCell)
|
58
|
-
# if list contains a formal_ref it must be replaced by the actual
|
59
|
-
goal_args << transform(arg, formals2actuals)
|
60
|
-
else
|
61
|
-
goal_args << arg
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
Goal.new(relation, goal_args)
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def transform(aConsCell, formals2actuals)
|
71
|
-
return aConsCell if aConsCell.null?
|
72
|
-
|
73
|
-
member = { car: :@car, cdr: :@cdr }
|
74
|
-
visitor = ConsCellVisitor.df_visitor(aConsCell)
|
75
|
-
side, cell = visitor.resume
|
76
|
-
result = ConsCell.new(nil, nil)
|
77
|
-
node = result
|
78
|
-
|
79
|
-
loop do
|
80
|
-
side, cell = visitor.resume
|
81
|
-
break if side == :stop
|
82
|
-
|
83
|
-
converted = nil
|
84
|
-
case cell
|
85
|
-
when FormalRef
|
86
|
-
converted = formals2actuals[cell.name]
|
87
|
-
when ConsCell
|
88
|
-
converted = ConsCell.new(nil, nil)
|
89
|
-
when GoalTemplate
|
90
|
-
converted = cell.send(:do_instantiate, formals2actuals)
|
91
|
-
else
|
92
|
-
converted = cell
|
93
|
-
end
|
94
|
-
node.instance_variable_set(member[side], converted)
|
95
|
-
node = converted if converted.kind_of?(ConsCell)
|
96
|
-
end
|
97
|
-
|
98
|
-
result
|
99
|
-
end
|
100
|
-
end # class
|
101
|
-
end # module
|
102
|
-
end # module
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'atomic_term'
|
4
|
-
|
5
|
-
module MiniKraken
|
6
|
-
module Core
|
7
|
-
# A specialized atomic term that represents an boolean (true/false) value.
|
8
|
-
# in MiniKraken
|
9
|
-
class KBoolean < AtomicTerm
|
10
|
-
# @param aValue [Boolean, Symbol] Ruby representation of boolean value
|
11
|
-
def initialize(aValue)
|
12
|
-
super(validated_value(aValue))
|
13
|
-
end
|
14
|
-
|
15
|
-
def to_s
|
16
|
-
value.to_s
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def validated_value(aValue)
|
22
|
-
case aValue
|
23
|
-
when true, false
|
24
|
-
aValue
|
25
|
-
when :"#t", '#t'
|
26
|
-
true
|
27
|
-
when :"#f", '#f'
|
28
|
-
false
|
29
|
-
else
|
30
|
-
raise StandardError, "Invalid boolean literal '#{aValue}'"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end # class
|
34
|
-
end # module
|
35
|
-
end # module
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'vocabulary'
|
4
|
-
|
5
|
-
unless MiniKraken::Core.constants(false).include? :Outcome
|
6
|
-
module MiniKraken
|
7
|
-
module Core
|
8
|
-
class Outcome
|
9
|
-
include Vocabulary # Use mix-in module
|
10
|
-
|
11
|
-
# @return [Symbol] One of: :"#s" (success), :"#u" (failure)
|
12
|
-
attr_reader :resultant
|
13
|
-
|
14
|
-
def initialize(aResult, aParent = nil)
|
15
|
-
init_vocabulary(aParent)
|
16
|
-
@resultant = aResult
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.failure(aParent = nil)
|
20
|
-
new(:"#u", aParent)
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.success(aParent = nil)
|
24
|
-
new(:"#s", aParent)
|
25
|
-
end
|
26
|
-
|
27
|
-
def failure?
|
28
|
-
resultant != :"#s"
|
29
|
-
end
|
30
|
-
|
31
|
-
def success?
|
32
|
-
resultant == :"#s"
|
33
|
-
end
|
34
|
-
|
35
|
-
def ==(other)
|
36
|
-
are_equal = false
|
37
|
-
|
38
|
-
if resultant == other.resultant && parent == other.parent &&
|
39
|
-
associations == other.associations
|
40
|
-
are_equal = true
|
41
|
-
end
|
42
|
-
|
43
|
-
are_equal
|
44
|
-
end
|
45
|
-
|
46
|
-
# Remove associations of variables of this environment, if
|
47
|
-
# persistence flag is set to false.
|
48
|
-
def prune!
|
49
|
-
parent.prune(self)
|
50
|
-
end
|
51
|
-
|
52
|
-
protected
|
53
|
-
|
54
|
-
def introspect
|
55
|
-
", @resultant=#{resultant}"
|
56
|
-
end
|
57
|
-
end # class
|
58
|
-
|
59
|
-
Failure = Outcome.new(:"#u")
|
60
|
-
BasicSuccess = Outcome.new(:"#s")
|
61
|
-
end # module
|
62
|
-
end # module
|
63
|
-
end # defined
|
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'designation'
|
4
|
-
require_relative 'any_value'
|
5
|
-
require_relative 'vocabulary'
|
6
|
-
|
7
|
-
module MiniKraken
|
8
|
-
module Core
|
9
|
-
# Representation of a MiniKraken variable.
|
10
|
-
# It is a named slot that can be associated with one value.
|
11
|
-
class Variable
|
12
|
-
include Designation # Mixin: Acquire name attribute
|
13
|
-
|
14
|
-
# @return [String] Internal variable name used by MiniKraken
|
15
|
-
attr_accessor :i_name
|
16
|
-
|
17
|
-
# @param aName [String] The name of the variable
|
18
|
-
def initialize(aName)
|
19
|
-
init_designation(aName)
|
20
|
-
@i_name = name.dup
|
21
|
-
end
|
22
|
-
|
23
|
-
def fused?
|
24
|
-
name != i_name
|
25
|
-
end
|
26
|
-
|
27
|
-
def quote(env)
|
28
|
-
raise StandardError, "class #{env}" unless env.kind_of?(Vocabulary)
|
29
|
-
|
30
|
-
val = env.quote_ref(self)
|
31
|
-
unless val
|
32
|
-
result = AnyValue.new(name, env, env.names_fused(name))
|
33
|
-
else
|
34
|
-
result = val
|
35
|
-
end
|
36
|
-
|
37
|
-
result
|
38
|
-
end
|
39
|
-
end # class
|
40
|
-
end # module
|
41
|
-
end # module
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'term'
|
4
|
-
require_relative 'designation'
|
5
|
-
require_relative 'any_value'
|
6
|
-
|
7
|
-
module MiniKraken
|
8
|
-
module Core
|
9
|
-
# A variable reference represents the occurrence of a variable (name) in a
|
10
|
-
# MiniKraken term.
|
11
|
-
class VariableRef < Term
|
12
|
-
include Designation # Mixin: Acquire name attribute
|
13
|
-
alias var_name name
|
14
|
-
|
15
|
-
# @param aName [String] The name of the variable
|
16
|
-
def initialize(aName)
|
17
|
-
super()
|
18
|
-
init_designation(aName)
|
19
|
-
name.freeze
|
20
|
-
end
|
21
|
-
|
22
|
-
def to_s
|
23
|
-
name
|
24
|
-
end
|
25
|
-
|
26
|
-
# @param aValue [Term]
|
27
|
-
# @param env [Environment]
|
28
|
-
def associate(aValue, env)
|
29
|
-
env.add_assoc(var_name, aValue)
|
30
|
-
end
|
31
|
-
|
32
|
-
# @param env [Environment]
|
33
|
-
# @return [Array<Term>]
|
34
|
-
def values(env)
|
35
|
-
env[var_name].map(&:value)
|
36
|
-
end
|
37
|
-
|
38
|
-
# @param env [Environment]
|
39
|
-
# @return [Term, NilClass]
|
40
|
-
def value(env)
|
41
|
-
freshness = env.freshness_ref(self)
|
42
|
-
freshness.associated
|
43
|
-
end
|
44
|
-
|
45
|
-
# @param env [Environment]
|
46
|
-
def quote(env)
|
47
|
-
val = env.quote_ref(self)
|
48
|
-
val.nil? ? AnyValue.new(var_name, env, names_fused(env)) : val
|
49
|
-
end
|
50
|
-
|
51
|
-
# param another [VariableRef]
|
52
|
-
# @param env [Environment]
|
53
|
-
# @return [Boolean]
|
54
|
-
def fused_with?(another, env)
|
55
|
-
my_var = env.name2var(var_name)
|
56
|
-
return false unless my_var.fused?
|
57
|
-
|
58
|
-
other_var = env.name2var(another.var_name)
|
59
|
-
return my_var.i_name == other_var.i_name
|
60
|
-
end
|
61
|
-
|
62
|
-
def names_fused(env)
|
63
|
-
env.names_fused(var_name)
|
64
|
-
end
|
65
|
-
|
66
|
-
# param another [VariableRef]
|
67
|
-
# @param env [Environment]
|
68
|
-
# @return [Boolean]
|
69
|
-
def different_from?(another, env)
|
70
|
-
!fused_with?(another, env)
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def valid_name(aName)
|
76
|
-
if aName.empty?
|
77
|
-
raise StandardError, 'Variable name may not be empty.'
|
78
|
-
end
|
79
|
-
|
80
|
-
aName
|
81
|
-
end
|
82
|
-
end # class
|
83
|
-
end # module
|
84
|
-
end # module
|
@@ -1,446 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'set'
|
4
|
-
require_relative 'association'
|
5
|
-
require_relative 'association_walker'
|
6
|
-
|
7
|
-
module MiniKraken
|
8
|
-
module Core
|
9
|
-
module Vocabulary
|
10
|
-
# @return [Environment] Parent environment to this one.
|
11
|
-
attr_accessor :parent
|
12
|
-
|
13
|
-
# @return [Hash] Pairs of the kind {String => Array[Association]}
|
14
|
-
attr_reader :associations
|
15
|
-
|
16
|
-
# @return [Hash] Pairs of the kind {String => Integer}
|
17
|
-
attr_reader :rankings
|
18
|
-
|
19
|
-
# @param aParent [Environment, NilClass] Parent environment to this one.
|
20
|
-
def init_vocabulary(aParent = nil)
|
21
|
-
@parent = validated_parent(aParent)
|
22
|
-
@associations = {}
|
23
|
-
@rankings = {} unless aParent
|
24
|
-
end
|
25
|
-
|
26
|
-
# Return a Enumerator object that can iterate over this vocabulary and
|
27
|
-
# all its direct and indirect parent(s).
|
28
|
-
# @return [Enumerator<Vocabulary, NilClass>]
|
29
|
-
def ancestor_walker
|
30
|
-
unless @ancestors # Not yet in cache?...
|
31
|
-
@ancestors = []
|
32
|
-
relative = self
|
33
|
-
while relative
|
34
|
-
@ancestors << relative
|
35
|
-
relative = relative.parent
|
36
|
-
end
|
37
|
-
@ancestors << nil # nil marks end of iteration...
|
38
|
-
end
|
39
|
-
|
40
|
-
@ancestors.to_enum
|
41
|
-
end
|
42
|
-
|
43
|
-
def clear_rankings
|
44
|
-
walker = ancestor_walker
|
45
|
-
orphan = nil
|
46
|
-
loop do
|
47
|
-
orphan_temp = walker.next
|
48
|
-
break unless orphan_temp
|
49
|
-
|
50
|
-
orphan = orphan_temp
|
51
|
-
end
|
52
|
-
|
53
|
-
orphan.rankings&.clear
|
54
|
-
end
|
55
|
-
|
56
|
-
# @param aName [String]
|
57
|
-
# @param alternate_names [Array<String>]
|
58
|
-
def get_rank(aName, alternate_names = [])
|
59
|
-
walker = ancestor_walker
|
60
|
-
orphan = nil
|
61
|
-
loop do
|
62
|
-
orphan_temp = walker.next
|
63
|
-
break unless orphan_temp
|
64
|
-
|
65
|
-
orphan = orphan_temp
|
66
|
-
end
|
67
|
-
|
68
|
-
raise StandardError unless orphan
|
69
|
-
|
70
|
-
rank = nil
|
71
|
-
if orphan.rankings.include?(aName)
|
72
|
-
rank = orphan.rankings[aName]
|
73
|
-
else
|
74
|
-
other = alternate_names.find do |a_name|
|
75
|
-
rank = orphan.rankings.include?(a_name)
|
76
|
-
end
|
77
|
-
if other
|
78
|
-
rank = get_rank(other)
|
79
|
-
else
|
80
|
-
rank = orphan.rankings.keys.size
|
81
|
-
orphan.rankings[aName] = rank
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
rank
|
86
|
-
end
|
87
|
-
|
88
|
-
# Record an association between a variable with given user-defined name
|
89
|
-
# and a term.
|
90
|
-
# @param aName [String, Variable] A user-defined variable name
|
91
|
-
# @param aTerm [Term] A term to associate with the variable
|
92
|
-
def add_assoc(aName, aTerm)
|
93
|
-
name = aName.respond_to?(:name) ? aName.name : aName
|
94
|
-
|
95
|
-
var = name2var(name)
|
96
|
-
unless var
|
97
|
-
err_msg = "Unknown variable '#{name}'."
|
98
|
-
raise StandardError, err_msg
|
99
|
-
end
|
100
|
-
siblings = detect_fuse(var, aTerm)
|
101
|
-
if siblings.empty?
|
102
|
-
anAssociation = Association.new(var.i_name, aTerm)
|
103
|
-
do_add_assocs([anAssociation]).first
|
104
|
-
else
|
105
|
-
fuse_vars(siblings << var)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
# Handler for the event: an outcome has been produced.
|
110
|
-
# Can be overridden in other to propagate associations from child
|
111
|
-
# @param _descendent [Outcome]
|
112
|
-
def propagate(_descendent)
|
113
|
-
# Do nothing...
|
114
|
-
end
|
115
|
-
|
116
|
-
# Remove all the associations of this vocabulary
|
117
|
-
def clear
|
118
|
-
associations.clear
|
119
|
-
end
|
120
|
-
|
121
|
-
# @param aVarName [String] A user-defined variable name
|
122
|
-
# @param other [Vocabulary]
|
123
|
-
def move_assocs(aVarName, other)
|
124
|
-
i_name = to_internal(aVarName)
|
125
|
-
assocs = other.associations[i_name]
|
126
|
-
if assocs
|
127
|
-
do_add_assocs(assocs)
|
128
|
-
other.associations.delete(i_name)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
# Merge the associations from another vocabulary-like object.
|
133
|
-
# @param another [Vocabulary]
|
134
|
-
def merge(another)
|
135
|
-
another.associations.each_value { |assocs| do_add_assocs(assocs) }
|
136
|
-
end
|
137
|
-
|
138
|
-
# Check that the provided variable must be fused with the argument.
|
139
|
-
# @return [Array<Variable>]
|
140
|
-
def detect_fuse(aVariable, aTerm)
|
141
|
-
return [] unless aTerm.kind_of?(VariableRef)
|
142
|
-
|
143
|
-
assocs = self[aTerm.var_name]
|
144
|
-
# Simplified implementation: cope with binary cycles only...
|
145
|
-
# TODO: Extend to n-ary (n > 2) cycles
|
146
|
-
assoc_refs = assocs.select { |a| a.value.kind_of?(VariableRef) }
|
147
|
-
return [] if assoc_refs.empty? # No relevant association...
|
148
|
-
|
149
|
-
visitees = Set.new
|
150
|
-
to_fuse = []
|
151
|
-
to_visit = assoc_refs
|
152
|
-
loop do
|
153
|
-
assc = to_visit.shift
|
154
|
-
next if visitees.include?(assc)
|
155
|
-
|
156
|
-
visitees.add(assc)
|
157
|
-
ref = assc.value
|
158
|
-
if ref.var_name == aVariable.name
|
159
|
-
to_fuse << assc.i_name unless assc.i_name == aVariable.i_name
|
160
|
-
end
|
161
|
-
other_assocs = self[ref.var_name]
|
162
|
-
other_assoc_refs = other_assocs.select { |a| a.value.kind_of?(VariableRef) }
|
163
|
-
other_assoc_refs.each do |a|
|
164
|
-
to_visit << a unless visitess.include?(a)
|
165
|
-
end
|
166
|
-
|
167
|
-
|
168
|
-
break if to_visit.empty?
|
169
|
-
end
|
170
|
-
|
171
|
-
to_fuse.map { |i_name| i_name2var(i_name) }
|
172
|
-
end
|
173
|
-
|
174
|
-
# Fuse the given variables:
|
175
|
-
# Collect all their associations
|
176
|
-
# Put them under a new internal name
|
177
|
-
# Remove all entries from old internal names
|
178
|
-
# For all fused variables, change internal names
|
179
|
-
# @param theVars [Array<Variable>]
|
180
|
-
def fuse_vars(theVars)
|
181
|
-
new_i_name = Object.new.object_id.to_s
|
182
|
-
fused_vars = theVars.dup
|
183
|
-
fused_vars.each do |a_var|
|
184
|
-
old_i_name = a_var.i_name
|
185
|
-
old_names = fused_vars.map(&:name)
|
186
|
-
walker = ancestor_walker
|
187
|
-
|
188
|
-
loop do
|
189
|
-
voc = walker.next
|
190
|
-
break unless voc
|
191
|
-
|
192
|
-
if voc.associations.include?(old_i_name)
|
193
|
-
assocs = voc.associations[old_i_name]
|
194
|
-
keep_assocs = assocs.reject do |assc|
|
195
|
-
assc.value.kind_of?(VariableRef) && old_names.include?(assc.value.var_name)
|
196
|
-
end
|
197
|
-
unless keep_assocs.empty?
|
198
|
-
keep_assocs.each { |assc| assc.i_name = new_i_name }
|
199
|
-
if voc.associations.include?(new_i_name)
|
200
|
-
voc.associations[new_i_name].concat(keep_assocs)
|
201
|
-
else
|
202
|
-
voc.associations[new_i_name] = keep_assocs
|
203
|
-
end
|
204
|
-
end
|
205
|
-
voc.associations.delete(old_i_name)
|
206
|
-
end
|
207
|
-
next unless voc.respond_to?(:vars) && voc.vars.include?(a_var.name)
|
208
|
-
|
209
|
-
user_names = voc.ivars[old_i_name]
|
210
|
-
unseen = user_names.reject { |nm| old_names.include?(nm) }
|
211
|
-
unseen.each do |usr_name|
|
212
|
-
new_var = name2var(usr_name)
|
213
|
-
fused_vars << new_var
|
214
|
-
end
|
215
|
-
unless voc.ivars.include?(new_i_name)
|
216
|
-
voc.ivars[new_i_name] = user_names
|
217
|
-
else
|
218
|
-
voc.ivars[new_i_name].merge(user_names)
|
219
|
-
end
|
220
|
-
voc.ivars.delete(old_i_name)
|
221
|
-
break
|
222
|
-
end
|
223
|
-
a_var.i_name = new_i_name
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
# @param var [Variable, VariableRef] the variable to check.
|
228
|
-
# @return [Boolean]
|
229
|
-
def fresh?(var)
|
230
|
-
ground_term = ground_value(var)
|
231
|
-
ground_term.nil? ? true : false
|
232
|
-
end
|
233
|
-
|
234
|
-
# @param var [Variable, VariableRef] variable for which the value to retrieve
|
235
|
-
# @return [Term, NilClass]
|
236
|
-
def ground_value(var)
|
237
|
-
name = var.respond_to?(:var_name) ? var.var_name : var.name
|
238
|
-
|
239
|
-
walker = AssociationWalker.new
|
240
|
-
walker.find_ground(name, self)
|
241
|
-
end
|
242
|
-
|
243
|
-
# @param val [CompositeTerm] the composite term to check.
|
244
|
-
# @return [Boolean]
|
245
|
-
def fresh_value?(val)
|
246
|
-
walker = AssociationWalker.new
|
247
|
-
ground_term = walker.walk_value(val, self)
|
248
|
-
ground_term.nil? ? true : false
|
249
|
-
end
|
250
|
-
|
251
|
-
# A composite term is fresh when all its members are nil or all non-nil members
|
252
|
-
# are all fresh
|
253
|
-
# A composite term is bound when it is not fresh and not ground
|
254
|
-
# A composite term is a ground term when all its non-nil members are ground.
|
255
|
-
# @param aComposite [CompositeTerm]
|
256
|
-
# @return [Freshness]
|
257
|
-
def freshness_composite(aComposite)
|
258
|
-
walker = AssociationWalker.new
|
259
|
-
walker.freshness_composite(aComposite)
|
260
|
-
end
|
261
|
-
|
262
|
-
# Determine whether the reference points to a fresh, bound or ground term.
|
263
|
-
# @param aVariableRef [VariableRef]
|
264
|
-
# @return [Freshness]
|
265
|
-
def freshness_ref(aVariableRef)
|
266
|
-
walker = AssociationWalker.new
|
267
|
-
walker.determine_freshness(aVariableRef, self)
|
268
|
-
end
|
269
|
-
|
270
|
-
# @param aVariableRef [VariableRef]
|
271
|
-
# @return [Term, NilClass]
|
272
|
-
def quote_ref(aVariableRef)
|
273
|
-
walker = AssociationWalker.new
|
274
|
-
walker.quote_term(aVariableRef, self)
|
275
|
-
end
|
276
|
-
|
277
|
-
# Return the variable with given user-defined variable name.
|
278
|
-
# @param aName [String] User-defined variable name
|
279
|
-
def name2var(aName)
|
280
|
-
var = nil
|
281
|
-
walker = ancestor_walker
|
282
|
-
|
283
|
-
loop do
|
284
|
-
voc = walker.next
|
285
|
-
if voc
|
286
|
-
next unless voc.respond_to?(:vars) && voc.vars.include?(aName)
|
287
|
-
|
288
|
-
var = voc.vars[aName]
|
289
|
-
end
|
290
|
-
|
291
|
-
break
|
292
|
-
end
|
293
|
-
|
294
|
-
var
|
295
|
-
end
|
296
|
-
|
297
|
-
# Return the variable with given internal variable name.
|
298
|
-
# @param i_name [String] internal variable name
|
299
|
-
# @return [Variable]
|
300
|
-
def i_name2var(i_name)
|
301
|
-
var = nil
|
302
|
-
voc = nil
|
303
|
-
walker = ancestor_walker
|
304
|
-
|
305
|
-
loop do
|
306
|
-
voc = walker.next
|
307
|
-
if voc
|
308
|
-
next unless voc.respond_to?(:ivars) && voc.ivars.include?(i_name)
|
309
|
-
|
310
|
-
var_name = voc.ivars[i_name].first # TODO: what if multiple vars?
|
311
|
-
var = voc.vars[var_name]
|
312
|
-
end
|
313
|
-
|
314
|
-
break
|
315
|
-
end
|
316
|
-
|
317
|
-
raise StandardError, 'Nil variable object' if var.nil?
|
318
|
-
|
319
|
-
var
|
320
|
-
end
|
321
|
-
|
322
|
-
# Return the internal name to corresponding to a given user-defined
|
323
|
-
# variable name.
|
324
|
-
# @param aName [String] User-defined variable name
|
325
|
-
def to_internal(aName)
|
326
|
-
var = name2var(aName)
|
327
|
-
var ? var.i_name : nil
|
328
|
-
end
|
329
|
-
|
330
|
-
# Return the internal names fused with given user-defined
|
331
|
-
# variable name.
|
332
|
-
# @param aName [String] User-defined variable name
|
333
|
-
def names_fused(aName)
|
334
|
-
# require 'debug'
|
335
|
-
var = name2var(aName)
|
336
|
-
return [] unless var&.fused?
|
337
|
-
|
338
|
-
i_name = var.i_name
|
339
|
-
names = []
|
340
|
-
walker = ancestor_walker
|
341
|
-
|
342
|
-
loop do
|
343
|
-
voc = walker.next
|
344
|
-
break unless voc
|
345
|
-
next unless voc.respond_to?(:ivars)
|
346
|
-
|
347
|
-
if voc.ivars.include?(i_name)
|
348
|
-
fused = voc.ivars[i_name]
|
349
|
-
names.concat(fused.to_a)
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
names.uniq!
|
354
|
-
names.reject { |nm| nm == aName }
|
355
|
-
end
|
356
|
-
|
357
|
-
# Retrieve all the associations for a given variable
|
358
|
-
# @param aVariable [Variable]
|
359
|
-
# @return [Array<Association>]
|
360
|
-
def assocs4var(aVariable)
|
361
|
-
i_name = aVariable.i_name
|
362
|
-
assocs = []
|
363
|
-
walker = ancestor_walker
|
364
|
-
|
365
|
-
loop do
|
366
|
-
voc = walker.next
|
367
|
-
break unless voc
|
368
|
-
next unless voc.associations.include?(i_name)
|
369
|
-
|
370
|
-
assocs.concat(voc.associations[i_name])
|
371
|
-
end
|
372
|
-
|
373
|
-
assocs
|
374
|
-
end
|
375
|
-
|
376
|
-
# @param aName [String] User-defined variable name
|
377
|
-
# @return [Array<Association>]
|
378
|
-
def [](aName)
|
379
|
-
iname = to_internal(aName)
|
380
|
-
return [] unless iname
|
381
|
-
|
382
|
-
assoc_arr = associations[iname]
|
383
|
-
assoc_arr = [] if assoc_arr.nil?
|
384
|
-
|
385
|
-
# TODO: Optimize
|
386
|
-
assoc_arr.concat(parent[aName]) if parent
|
387
|
-
assoc_arr
|
388
|
-
end
|
389
|
-
|
390
|
-
# Check that a variable with given name is defined in this vocabulary
|
391
|
-
# or one of its ancestor.
|
392
|
-
# @param aVarName [String] A user-defined variable name.
|
393
|
-
# @return [Boolean]
|
394
|
-
def include?(aVarName)
|
395
|
-
name2var(aVarName) ? true : false
|
396
|
-
end
|
397
|
-
|
398
|
-
def prune(anOutcome)
|
399
|
-
anOutcome # Don't touch outcome
|
400
|
-
end
|
401
|
-
|
402
|
-
def inspect
|
403
|
-
result = +"#<#{self.class.name}:#{object_id.to_s(16)} @parent="
|
404
|
-
if parent
|
405
|
-
result << "#<#{parent.class.name}:#{parent.object_id.to_s(16)}>"
|
406
|
-
else
|
407
|
-
result << 'nil'
|
408
|
-
end
|
409
|
-
result << introspect
|
410
|
-
result << '>'
|
411
|
-
result
|
412
|
-
end
|
413
|
-
|
414
|
-
protected
|
415
|
-
|
416
|
-
def validated_parent(aParent)
|
417
|
-
if aParent
|
418
|
-
unless aParent.kind_of?(Vocabulary)
|
419
|
-
raise StandardError, "Invalid parent type #{aParent.class}"
|
420
|
-
end
|
421
|
-
end
|
422
|
-
|
423
|
-
aParent
|
424
|
-
end
|
425
|
-
|
426
|
-
# @param theAssociations [Array<Association>]
|
427
|
-
def do_add_assocs(theAssociations)
|
428
|
-
theAssociations.each do |assc|
|
429
|
-
i_name = assc.i_name
|
430
|
-
found_assocs = associations[i_name]
|
431
|
-
if found_assocs
|
432
|
-
found_assocs << assc
|
433
|
-
else
|
434
|
-
associations[i_name] = [assc]
|
435
|
-
end
|
436
|
-
|
437
|
-
assc
|
438
|
-
end
|
439
|
-
end
|
440
|
-
|
441
|
-
def introspect
|
442
|
-
''
|
443
|
-
end
|
444
|
-
end # class
|
445
|
-
end # module
|
446
|
-
end # module
|