mini_kraken 0.2.02 → 0.3.02

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 (134) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +378 -333
  3. data/CHANGELOG.md +52 -0
  4. data/README.md +19 -19
  5. data/lib/mini_kraken.rb +0 -1
  6. data/lib/mini_kraken/atomic/all_atomic.rb +5 -0
  7. data/lib/mini_kraken/atomic/atomic_term.rb +96 -0
  8. data/lib/mini_kraken/atomic/k_boolean.rb +42 -0
  9. data/lib/mini_kraken/{core → atomic}/k_integer.rb +2 -5
  10. data/lib/mini_kraken/atomic/k_string.rb +17 -0
  11. data/lib/mini_kraken/{core → atomic}/k_symbol.rb +4 -8
  12. data/lib/mini_kraken/composite/all_composite.rb +4 -0
  13. data/lib/mini_kraken/composite/composite_term.rb +27 -0
  14. data/lib/mini_kraken/composite/cons_cell.rb +299 -0
  15. data/lib/mini_kraken/composite/cons_cell_visitor.rb +50 -0
  16. data/lib/mini_kraken/composite/list.rb +32 -0
  17. data/lib/mini_kraken/core/all_core.rb +8 -0
  18. data/lib/mini_kraken/core/any_value.rb +31 -7
  19. data/lib/mini_kraken/core/arity.rb +69 -0
  20. data/lib/mini_kraken/core/association.rb +29 -4
  21. data/lib/mini_kraken/core/association_copy.rb +50 -0
  22. data/lib/mini_kraken/core/base_term.rb +13 -0
  23. data/lib/mini_kraken/core/blackboard.rb +315 -0
  24. data/lib/mini_kraken/core/bookmark.rb +46 -0
  25. data/lib/mini_kraken/core/context.rb +492 -0
  26. data/lib/mini_kraken/core/duck_fiber.rb +21 -19
  27. data/lib/mini_kraken/core/entry.rb +40 -0
  28. data/lib/mini_kraken/core/fail.rb +20 -18
  29. data/lib/mini_kraken/core/fusion.rb +29 -0
  30. data/lib/mini_kraken/core/goal.rb +20 -29
  31. data/lib/mini_kraken/core/log_var.rb +22 -0
  32. data/lib/mini_kraken/core/log_var_ref.rb +108 -0
  33. data/lib/mini_kraken/core/nullary_relation.rb +2 -9
  34. data/lib/mini_kraken/core/parametrized_term.rb +61 -0
  35. data/lib/mini_kraken/core/relation.rb +14 -28
  36. data/lib/mini_kraken/core/scope.rb +67 -0
  37. data/lib/mini_kraken/core/solver_adapter.rb +58 -0
  38. data/lib/mini_kraken/core/specification.rb +48 -0
  39. data/lib/mini_kraken/core/succeed.rb +21 -17
  40. data/lib/mini_kraken/core/symbol_table.rb +137 -0
  41. data/lib/mini_kraken/core/term.rb +15 -4
  42. data/lib/mini_kraken/glue/dsl.rb +45 -81
  43. data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
  44. data/lib/mini_kraken/rela/all_rela.rb +8 -0
  45. data/lib/mini_kraken/rela/binary_relation.rb +30 -0
  46. data/lib/mini_kraken/rela/conde.rb +143 -0
  47. data/lib/mini_kraken/rela/conj2.rb +65 -0
  48. data/lib/mini_kraken/rela/def_relation.rb +93 -0
  49. data/lib/mini_kraken/rela/disj2.rb +70 -0
  50. data/lib/mini_kraken/rela/fresh.rb +98 -0
  51. data/lib/mini_kraken/{core → rela}/goal_relation.rb +7 -9
  52. data/lib/mini_kraken/rela/unify.rb +258 -0
  53. data/lib/mini_kraken/version.rb +1 -1
  54. data/mini_kraken.gemspec +2 -2
  55. data/spec/.rubocop.yml +1 -1
  56. data/spec/atomic/atomic_term_spec.rb +98 -0
  57. data/spec/{core → atomic}/k_boolean_spec.rb +19 -34
  58. data/spec/{core → atomic}/k_symbol_spec.rb +3 -16
  59. data/spec/composite/cons_cell_spec.rb +225 -0
  60. data/spec/composite/cons_cell_visitor_spec.rb +158 -0
  61. data/spec/composite/list_spec.rb +50 -0
  62. data/spec/core/any_value_spec.rb +52 -0
  63. data/spec/core/arity_spec.rb +92 -0
  64. data/spec/core/association_copy_spec.rb +69 -0
  65. data/spec/core/association_spec.rb +31 -4
  66. data/spec/core/blackboard_spec.rb +287 -0
  67. data/spec/core/bookmark_spec.rb +40 -0
  68. data/spec/core/context_spec.rb +245 -0
  69. data/spec/core/core_spec.rb +40 -0
  70. data/spec/core/duck_fiber_spec.rb +16 -46
  71. data/spec/core/fail_spec.rb +5 -6
  72. data/spec/core/goal_spec.rb +24 -14
  73. data/spec/core/log_var_ref_spec.rb +105 -0
  74. data/spec/core/log_var_spec.rb +64 -0
  75. data/spec/core/nullary_relation_spec.rb +33 -0
  76. data/spec/core/parametrized_tem_spec.rb +39 -0
  77. data/spec/core/relation_spec.rb +33 -0
  78. data/spec/core/scope_spec.rb +73 -0
  79. data/spec/core/solver_adapter_spec.rb +70 -0
  80. data/spec/core/specification_spec.rb +43 -0
  81. data/spec/core/succeed_spec.rb +5 -5
  82. data/spec/core/symbol_table_spec.rb +142 -0
  83. data/spec/glue/dsl_chap1_spec.rb +96 -144
  84. data/spec/glue/dsl_chap2_spec.rb +350 -0
  85. data/spec/glue/run_star_expression_spec.rb +82 -906
  86. data/spec/rela/conde_spec.rb +153 -0
  87. data/spec/rela/conj2_spec.rb +123 -0
  88. data/spec/rela/def_relation_spec.rb +119 -0
  89. data/spec/rela/disj2_spec.rb +117 -0
  90. data/spec/rela/fresh_spec.rb +147 -0
  91. data/spec/rela/unify_spec.rb +369 -0
  92. data/spec/support/factory_atomic.rb +29 -0
  93. data/spec/support/factory_composite.rb +21 -0
  94. data/spec/support/factory_methods.rb +11 -26
  95. metadata +100 -64
  96. data/lib/mini_kraken/core/association_walker.rb +0 -183
  97. data/lib/mini_kraken/core/atomic_term.rb +0 -67
  98. data/lib/mini_kraken/core/base_arg.rb +0 -10
  99. data/lib/mini_kraken/core/binary_relation.rb +0 -63
  100. data/lib/mini_kraken/core/composite_goal.rb +0 -46
  101. data/lib/mini_kraken/core/composite_term.rb +0 -41
  102. data/lib/mini_kraken/core/conde.rb +0 -143
  103. data/lib/mini_kraken/core/conj2.rb +0 -79
  104. data/lib/mini_kraken/core/cons_cell.rb +0 -82
  105. data/lib/mini_kraken/core/def_relation.rb +0 -50
  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 -156
  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 -62
  115. data/lib/mini_kraken/core/k_boolean.rb +0 -35
  116. data/lib/mini_kraken/core/outcome.rb +0 -53
  117. data/lib/mini_kraken/core/variable.rb +0 -41
  118. data/lib/mini_kraken/core/variable_ref.rb +0 -78
  119. data/lib/mini_kraken/core/vocabulary.rb +0 -442
  120. data/lib/mini_kraken/glue/fresh_env.rb +0 -75
  121. data/spec/core/association_walker_spec.rb +0 -192
  122. data/spec/core/conde_spec.rb +0 -147
  123. data/spec/core/conj2_spec.rb +0 -114
  124. data/spec/core/cons_cell_spec.rb +0 -107
  125. data/spec/core/def_relation_spec.rb +0 -96
  126. data/spec/core/disj2_spec.rb +0 -99
  127. data/spec/core/environment_spec.rb +0 -142
  128. data/spec/core/equals_spec.rb +0 -304
  129. data/spec/core/goal_template_spec.rb +0 -74
  130. data/spec/core/outcome_spec.rb +0 -48
  131. data/spec/core/variable_ref_spec.rb +0 -27
  132. data/spec/core/variable_spec.rb +0 -35
  133. data/spec/core/vocabulary_spec.rb +0 -219
  134. 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,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'base_arg'
4
-
5
- module MiniKraken
6
- module Core
7
- # A meta-goal that is parametrized with generic formal arguments.
8
- # The individual goals are instantiated when the formal arguments
9
- # are bound to goal arguments
10
- class GoalTemplate < BaseArg
11
- # @return [Array<BaseArg>] Arguments of goal template.
12
- attr_reader :args
13
-
14
- # @return [Relation] Main relation for the goal template
15
- attr_reader :relation
16
-
17
- def initialize(aRelation, theArgs)
18
- super()
19
- @relation = validated_relation(aRelation)
20
- @args = validated_args(theArgs)
21
- freeze
22
- end
23
-
24
- # @param formals [Array<FormalArg>] Array of formal arguments
25
- # @param actuals [Array<GoalArg>] Array of actual arguments
26
- # @return [Goal] instantiate a goal object given the actuals and environment
27
- def instantiate(formals, actuals)
28
- formals2actuals = {}
29
- formals.each_with_index do |frml, i|
30
- formals2actuals[frml.name] = actuals[i]
31
- end
32
-
33
- do_instantiate(formals2actuals)
34
- end
35
-
36
- private
37
-
38
- def validated_relation(aRelation)
39
- aRelation
40
- end
41
-
42
- def validated_args(theArgs)
43
- theArgs
44
- end
45
-
46
- def do_instantiate(formals2actuals)
47
- goal_args = []
48
- args.each do |arg|
49
- if arg.kind_of?(FormalRef)
50
- goal_args << formals2actuals[arg.name]
51
- elsif arg.kind_of?(GoalTemplate)
52
- goal_args << arg.send(:do_instantiate, formals2actuals)
53
- else
54
- goal_args << arg
55
- end
56
- end
57
-
58
- Goal.new(relation, goal_args)
59
- end
60
- end # class
61
- end # module
62
- 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,53 +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 successful?
28
- resultant == :"#s"
29
- end
30
-
31
- def ==(other)
32
- are_equal = false
33
-
34
- if resultant == other.resultant && parent == other.parent &&
35
- associations == other.associations
36
- are_equal = true
37
- end
38
-
39
- are_equal
40
- end
41
-
42
- protected
43
-
44
- def introspect
45
- ", @resultant=#{resultant}"
46
- end
47
- end # class
48
-
49
- Failure = Outcome.new(:"#u")
50
- BasicSuccess = Outcome.new(:"#s")
51
- end # module
52
- end # module
53
- 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,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'term'
4
- require_relative 'any_value'
5
-
6
- module MiniKraken
7
- module Core
8
- # A variable reference represents the occurrence of a variable (name) in a
9
- # MiniKraken term.
10
- class VariableRef < Term
11
- include Designation # Mixin: Acquire name attribute
12
- alias var_name name
13
-
14
- # @param aName [String] The name of the variable
15
- def initialize(aName)
16
- super()
17
- init_designation(aName)
18
- end
19
-
20
- # @param aValue [Term]
21
- # @param env [Environment]
22
- def associate(aValue, env)
23
- env.add_assoc(var_name, aValue)
24
- end
25
-
26
- # @param env [Environment]
27
- # @return [Array<Term>]
28
- def values(env)
29
- env[var_name].map(&:value)
30
- end
31
-
32
- # @param env [Environment]
33
- # @return [Term, NilClass]
34
- def value(env)
35
- freshness = env.freshness_ref(self)
36
- freshness.associated
37
- end
38
-
39
- # @param env [Environment]
40
- def quote(env)
41
- val = env.quote_ref(self)
42
- val.nil? ? AnyValue.new(var_name, env, names_fused(env)) : val
43
- end
44
-
45
- # param another [VariableRef]
46
- # @param env [Environment]
47
- # @return [Boolean]
48
- def fused_with?(another, env)
49
- my_var = env.name2var(var_name)
50
- return false unless my_var.fused?
51
-
52
- other_var = env.name2var(another.var_name)
53
- return my_var.i_name == other_var.i_name
54
- end
55
-
56
- def names_fused(env)
57
- env.names_fused(var_name)
58
- end
59
-
60
- # param another [VariableRef]
61
- # @param env [Environment]
62
- # @return [Boolean]
63
- def different_from?(another, env)
64
- !fused_with?(another, env)
65
- end
66
-
67
- private
68
-
69
- def valid_name(aName)
70
- if aName.empty?
71
- raise StandardError, 'Variable name may not be empty.'
72
- end
73
-
74
- aName
75
- end
76
- end # class
77
- end # module
78
- end # module
@@ -1,442 +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, that is:
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 inspect
399
- result = +"#<#{self.class.name}:#{object_id.to_s(16)} @parent="
400
- if parent
401
- result << "#<#{parent.class.name}:#{parent.object_id.to_s(16)}>"
402
- else
403
- result << nil
404
- end
405
- result << introspect
406
- result << '>'
407
- result
408
- end
409
-
410
- protected
411
-
412
- def validated_parent(aParent)
413
- if aParent
414
- unless aParent.kind_of?(Vocabulary)
415
- raise StandardError, "Invalid parent type #{aParent.class}"
416
- end
417
- end
418
-
419
- aParent
420
- end
421
-
422
- # @param theAssociations [Array<Association>]
423
- def do_add_assocs(theAssociations)
424
- theAssociations.each do |assc|
425
- i_name = assc.i_name
426
- found_assocs = associations[i_name]
427
- if found_assocs
428
- found_assocs << assc
429
- else
430
- associations[i_name] = [assc]
431
- end
432
-
433
- assc
434
- end
435
- end
436
-
437
- def introspect
438
- ''
439
- end
440
- end # class
441
- end # module
442
- end # module