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.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +378 -333
  3. data/CHANGELOG.md +48 -0
  4. data/README.md +29 -21
  5. data/lib/mini_kraken/atomic/all_atomic.rb +5 -0
  6. data/lib/mini_kraken/atomic/atomic_term.rb +96 -0
  7. data/lib/mini_kraken/atomic/k_boolean.rb +42 -0
  8. data/lib/mini_kraken/{core → atomic}/k_integer.rb +2 -5
  9. data/lib/mini_kraken/atomic/k_string.rb +17 -0
  10. data/lib/mini_kraken/{core → atomic}/k_symbol.rb +4 -8
  11. data/lib/mini_kraken/composite/all_composite.rb +4 -0
  12. data/lib/mini_kraken/composite/composite_term.rb +27 -0
  13. data/lib/mini_kraken/composite/cons_cell.rb +301 -0
  14. data/lib/mini_kraken/composite/cons_cell_visitor.rb +50 -0
  15. data/lib/mini_kraken/composite/list.rb +32 -0
  16. data/lib/mini_kraken/core/all_core.rb +8 -0
  17. data/lib/mini_kraken/core/any_value.rb +31 -7
  18. data/lib/mini_kraken/core/arity.rb +69 -0
  19. data/lib/mini_kraken/core/association.rb +29 -4
  20. data/lib/mini_kraken/core/association_copy.rb +50 -0
  21. data/lib/mini_kraken/core/base_term.rb +13 -0
  22. data/lib/mini_kraken/core/blackboard.rb +315 -0
  23. data/lib/mini_kraken/core/bookmark.rb +46 -0
  24. data/lib/mini_kraken/core/context.rb +492 -0
  25. data/lib/mini_kraken/core/duck_fiber.rb +21 -19
  26. data/lib/mini_kraken/core/entry.rb +40 -0
  27. data/lib/mini_kraken/core/fail.rb +20 -18
  28. data/lib/mini_kraken/core/fusion.rb +29 -0
  29. data/lib/mini_kraken/core/goal.rb +20 -29
  30. data/lib/mini_kraken/core/log_var.rb +22 -0
  31. data/lib/mini_kraken/core/log_var_ref.rb +108 -0
  32. data/lib/mini_kraken/core/nullary_relation.rb +2 -9
  33. data/lib/mini_kraken/core/parametrized_term.rb +68 -0
  34. data/lib/mini_kraken/core/relation.rb +14 -28
  35. data/lib/mini_kraken/core/scope.rb +67 -0
  36. data/lib/mini_kraken/core/solver_adapter.rb +58 -0
  37. data/lib/mini_kraken/core/specification.rb +48 -0
  38. data/lib/mini_kraken/core/succeed.rb +21 -17
  39. data/lib/mini_kraken/core/symbol_table.rb +137 -0
  40. data/lib/mini_kraken/core/term.rb +15 -4
  41. data/lib/mini_kraken/glue/dsl.rb +44 -88
  42. data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
  43. data/lib/mini_kraken/rela/all_rela.rb +8 -0
  44. data/lib/mini_kraken/rela/binary_relation.rb +30 -0
  45. data/lib/mini_kraken/rela/conde.rb +143 -0
  46. data/lib/mini_kraken/rela/conj2.rb +65 -0
  47. data/lib/mini_kraken/rela/def_relation.rb +93 -0
  48. data/lib/mini_kraken/rela/disj2.rb +70 -0
  49. data/lib/mini_kraken/rela/fresh.rb +98 -0
  50. data/lib/mini_kraken/{core → rela}/goal_relation.rb +7 -9
  51. data/lib/mini_kraken/rela/unify.rb +265 -0
  52. data/lib/mini_kraken/version.rb +1 -1
  53. data/mini_kraken.gemspec +2 -2
  54. data/spec/.rubocop.yml +1 -1
  55. data/spec/atomic/atomic_term_spec.rb +98 -0
  56. data/spec/{core → atomic}/k_boolean_spec.rb +19 -34
  57. data/spec/{core → atomic}/k_symbol_spec.rb +3 -16
  58. data/spec/composite/cons_cell_spec.rb +225 -0
  59. data/spec/{core → composite}/cons_cell_visitor_spec.rb +36 -20
  60. data/spec/composite/list_spec.rb +50 -0
  61. data/spec/core/any_value_spec.rb +52 -0
  62. data/spec/core/arity_spec.rb +92 -0
  63. data/spec/core/association_copy_spec.rb +69 -0
  64. data/spec/core/association_spec.rb +31 -4
  65. data/spec/core/blackboard_spec.rb +287 -0
  66. data/spec/core/bookmark_spec.rb +40 -0
  67. data/spec/core/context_spec.rb +245 -0
  68. data/spec/core/core_spec.rb +40 -0
  69. data/spec/core/duck_fiber_spec.rb +16 -46
  70. data/spec/core/fail_spec.rb +5 -6
  71. data/spec/core/goal_spec.rb +22 -12
  72. data/spec/core/log_var_ref_spec.rb +105 -0
  73. data/spec/core/log_var_spec.rb +64 -0
  74. data/spec/core/nullary_relation_spec.rb +33 -0
  75. data/spec/core/parametrized_tem_spec.rb +39 -0
  76. data/spec/core/relation_spec.rb +33 -0
  77. data/spec/core/scope_spec.rb +73 -0
  78. data/spec/core/solver_adapter_spec.rb +70 -0
  79. data/spec/core/specification_spec.rb +43 -0
  80. data/spec/core/succeed_spec.rb +5 -5
  81. data/spec/core/symbol_table_spec.rb +142 -0
  82. data/spec/glue/dsl_chap1_spec.rb +88 -144
  83. data/spec/glue/dsl_chap2_spec.rb +454 -19
  84. data/spec/glue/run_star_expression_spec.rb +81 -906
  85. data/spec/rela/conde_spec.rb +153 -0
  86. data/spec/rela/conj2_spec.rb +123 -0
  87. data/spec/rela/def_relation_spec.rb +119 -0
  88. data/spec/rela/disj2_spec.rb +117 -0
  89. data/spec/rela/fresh_spec.rb +147 -0
  90. data/spec/rela/unify_spec.rb +369 -0
  91. data/spec/support/factory_atomic.rb +29 -0
  92. data/spec/support/factory_composite.rb +21 -0
  93. data/spec/support/factory_methods.rb +11 -26
  94. metadata +98 -70
  95. data/lib/mini_kraken/core/association_walker.rb +0 -183
  96. data/lib/mini_kraken/core/atomic_term.rb +0 -67
  97. data/lib/mini_kraken/core/base_arg.rb +0 -10
  98. data/lib/mini_kraken/core/binary_relation.rb +0 -63
  99. data/lib/mini_kraken/core/composite_goal.rb +0 -46
  100. data/lib/mini_kraken/core/composite_term.rb +0 -41
  101. data/lib/mini_kraken/core/conde.rb +0 -143
  102. data/lib/mini_kraken/core/conj2.rb +0 -79
  103. data/lib/mini_kraken/core/cons_cell.rb +0 -82
  104. data/lib/mini_kraken/core/cons_cell_visitor.rb +0 -102
  105. data/lib/mini_kraken/core/def_relation.rb +0 -53
  106. data/lib/mini_kraken/core/designation.rb +0 -55
  107. data/lib/mini_kraken/core/disj2.rb +0 -72
  108. data/lib/mini_kraken/core/environment.rb +0 -73
  109. data/lib/mini_kraken/core/equals.rb +0 -193
  110. data/lib/mini_kraken/core/formal_arg.rb +0 -22
  111. data/lib/mini_kraken/core/formal_ref.rb +0 -25
  112. data/lib/mini_kraken/core/freshness.rb +0 -45
  113. data/lib/mini_kraken/core/goal_arg.rb +0 -12
  114. data/lib/mini_kraken/core/goal_template.rb +0 -102
  115. data/lib/mini_kraken/core/k_boolean.rb +0 -35
  116. data/lib/mini_kraken/core/outcome.rb +0 -63
  117. data/lib/mini_kraken/core/variable.rb +0 -41
  118. data/lib/mini_kraken/core/variable_ref.rb +0 -84
  119. data/lib/mini_kraken/core/vocabulary.rb +0 -446
  120. data/lib/mini_kraken/glue/fresh_env.rb +0 -103
  121. data/lib/mini_kraken/glue/fresh_env_factory.rb +0 -83
  122. data/spec/core/association_walker_spec.rb +0 -192
  123. data/spec/core/conde_spec.rb +0 -147
  124. data/spec/core/conj2_spec.rb +0 -114
  125. data/spec/core/cons_cell_spec.rb +0 -107
  126. data/spec/core/def_relation_spec.rb +0 -97
  127. data/spec/core/disj2_spec.rb +0 -99
  128. data/spec/core/environment_spec.rb +0 -142
  129. data/spec/core/equals_spec.rb +0 -317
  130. data/spec/core/goal_template_spec.rb +0 -74
  131. data/spec/core/outcome_spec.rb +0 -56
  132. data/spec/core/variable_ref_spec.rb +0 -30
  133. data/spec/core/variable_spec.rb +0 -35
  134. data/spec/core/vocabulary_spec.rb +0 -219
  135. data/spec/glue/fresh_env_factory_spec.rb +0 -97
  136. 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