mini_kraken 0.2.04 → 0.3.00

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +16 -16
  4. data/lib/mini_kraken/atomic/all_atomic.rb +1 -0
  5. data/lib/mini_kraken/atomic/atomic_term.rb +32 -17
  6. data/lib/mini_kraken/atomic/k_integer.rb +0 -4
  7. data/lib/mini_kraken/atomic/k_string.rb +17 -0
  8. data/lib/mini_kraken/atomic/k_symbol.rb +0 -6
  9. data/lib/mini_kraken/composite/all_composite.rb +4 -0
  10. data/lib/mini_kraken/composite/composite_term.rb +2 -18
  11. data/lib/mini_kraken/composite/cons_cell.rb +178 -11
  12. data/lib/mini_kraken/composite/cons_cell_visitor.rb +12 -64
  13. data/lib/mini_kraken/composite/list.rb +32 -0
  14. data/lib/mini_kraken/core/all_core.rb +8 -0
  15. data/lib/mini_kraken/core/any_value.rb +31 -7
  16. data/lib/mini_kraken/core/arity.rb +69 -0
  17. data/lib/mini_kraken/core/association.rb +29 -4
  18. data/lib/mini_kraken/core/association_copy.rb +50 -0
  19. data/lib/mini_kraken/core/base_term.rb +13 -0
  20. data/lib/mini_kraken/core/blackboard.rb +315 -0
  21. data/lib/mini_kraken/core/bookmark.rb +46 -0
  22. data/lib/mini_kraken/core/context.rb +624 -0
  23. data/lib/mini_kraken/core/duck_fiber.rb +21 -19
  24. data/lib/mini_kraken/core/entry.rb +40 -0
  25. data/lib/mini_kraken/core/fail.rb +20 -18
  26. data/lib/mini_kraken/core/fusion.rb +29 -0
  27. data/lib/mini_kraken/core/goal.rb +20 -29
  28. data/lib/mini_kraken/core/log_var.rb +4 -30
  29. data/lib/mini_kraken/core/log_var_ref.rb +72 -48
  30. data/lib/mini_kraken/core/nullary_relation.rb +2 -9
  31. data/lib/mini_kraken/core/parametrized_term.rb +61 -0
  32. data/lib/mini_kraken/core/relation.rb +14 -28
  33. data/lib/mini_kraken/core/scope.rb +67 -0
  34. data/lib/mini_kraken/core/solver_adapter.rb +58 -0
  35. data/lib/mini_kraken/core/specification.rb +48 -0
  36. data/lib/mini_kraken/core/succeed.rb +21 -17
  37. data/lib/mini_kraken/core/symbol_table.rb +137 -0
  38. data/lib/mini_kraken/core/term.rb +15 -4
  39. data/lib/mini_kraken/glue/dsl.rb +35 -69
  40. data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
  41. data/lib/mini_kraken/rela/all_rela.rb +8 -0
  42. data/lib/mini_kraken/rela/binary_relation.rb +30 -0
  43. data/lib/mini_kraken/rela/conde.rb +146 -0
  44. data/lib/mini_kraken/rela/conj2.rb +65 -0
  45. data/lib/mini_kraken/rela/def_relation.rb +64 -0
  46. data/lib/mini_kraken/rela/disj2.rb +70 -0
  47. data/lib/mini_kraken/rela/fresh.rb +98 -0
  48. data/lib/mini_kraken/{core → rela}/goal_relation.rb +6 -8
  49. data/lib/mini_kraken/rela/unify.rb +258 -0
  50. data/lib/mini_kraken/version.rb +1 -1
  51. data/spec/atomic/atomic_term_spec.rb +23 -20
  52. data/spec/atomic/k_symbol_spec.rb +0 -5
  53. data/spec/composite/cons_cell_spec.rb +116 -0
  54. data/spec/composite/cons_cell_visitor_spec.rb +16 -3
  55. data/spec/composite/list_spec.rb +50 -0
  56. data/spec/core/any_value_spec.rb +52 -0
  57. data/spec/core/arity_spec.rb +91 -0
  58. data/spec/core/association_copy_spec.rb +69 -0
  59. data/spec/core/association_spec.rb +25 -0
  60. data/spec/core/blackboard_spec.rb +287 -0
  61. data/spec/core/bookmark_spec.rb +40 -0
  62. data/spec/core/context_spec.rb +221 -0
  63. data/spec/core/core_spec.rb +40 -0
  64. data/spec/core/duck_fiber_spec.rb +22 -46
  65. data/spec/core/fail_spec.rb +5 -6
  66. data/spec/core/goal_spec.rb +20 -11
  67. data/spec/core/log_var_ref_spec.rb +80 -5
  68. data/spec/core/log_var_spec.rb +35 -6
  69. data/spec/core/nullary_relation_spec.rb +33 -0
  70. data/spec/core/parametrized_tem_spec.rb +39 -0
  71. data/spec/core/relation_spec.rb +33 -0
  72. data/spec/core/scope_spec.rb +73 -0
  73. data/spec/core/solver_adapter_spec.rb +70 -0
  74. data/spec/core/specification_spec.rb +43 -0
  75. data/spec/core/succeed_spec.rb +5 -5
  76. data/spec/core/symbol_table_spec.rb +142 -0
  77. data/spec/glue/dsl_chap1_spec.rb +88 -99
  78. data/spec/glue/dsl_chap2_spec.rb +59 -41
  79. data/spec/glue/run_star_expression_spec.rb +69 -896
  80. data/spec/{core → rela}/conde_spec.rb +50 -46
  81. data/spec/rela/conj2_spec.rb +123 -0
  82. data/spec/rela/def_relation_spec.rb +119 -0
  83. data/spec/rela/disj2_spec.rb +117 -0
  84. data/spec/rela/fresh_spec.rb +147 -0
  85. data/spec/rela/unify_spec.rb +369 -0
  86. data/spec/support/factory_atomic.rb +7 -0
  87. data/spec/support/factory_composite.rb +21 -0
  88. metadata +71 -48
  89. data/lib/mini_kraken/core/association_walker.rb +0 -183
  90. data/lib/mini_kraken/core/base_arg.rb +0 -10
  91. data/lib/mini_kraken/core/binary_relation.rb +0 -63
  92. data/lib/mini_kraken/core/composite_goal.rb +0 -46
  93. data/lib/mini_kraken/core/conde.rb +0 -143
  94. data/lib/mini_kraken/core/conj2.rb +0 -79
  95. data/lib/mini_kraken/core/def_relation.rb +0 -53
  96. data/lib/mini_kraken/core/designation.rb +0 -55
  97. data/lib/mini_kraken/core/disj2.rb +0 -72
  98. data/lib/mini_kraken/core/environment.rb +0 -73
  99. data/lib/mini_kraken/core/equals.rb +0 -191
  100. data/lib/mini_kraken/core/formal_arg.rb +0 -22
  101. data/lib/mini_kraken/core/formal_ref.rb +0 -25
  102. data/lib/mini_kraken/core/freshness.rb +0 -45
  103. data/lib/mini_kraken/core/goal_arg.rb +0 -12
  104. data/lib/mini_kraken/core/goal_template.rb +0 -102
  105. data/lib/mini_kraken/core/outcome.rb +0 -63
  106. data/lib/mini_kraken/core/tap.rb +0 -46
  107. data/lib/mini_kraken/core/vocabulary.rb +0 -446
  108. data/lib/mini_kraken/glue/fresh_env.rb +0 -108
  109. data/lib/mini_kraken/glue/fresh_env_factory.rb +0 -83
  110. data/spec/core/association_walker_spec.rb +0 -194
  111. data/spec/core/conj2_spec.rb +0 -116
  112. data/spec/core/def_relation_spec.rb +0 -99
  113. data/spec/core/disj2_spec.rb +0 -100
  114. data/spec/core/environment_spec.rb +0 -144
  115. data/spec/core/equals_spec.rb +0 -319
  116. data/spec/core/goal_template_spec.rb +0 -74
  117. data/spec/core/outcome_spec.rb +0 -56
  118. data/spec/core/vocabulary_spec.rb +0 -220
  119. data/spec/glue/fresh_env_factory_spec.rb +0 -99
  120. data/spec/glue/fresh_env_spec.rb +0 -62
@@ -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,46 +0,0 @@
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
- module MiniKraken
10
- module Core
11
- class Tap < GoalRelation
12
- include Singleton
13
-
14
- def initialize
15
- super('tap', nil)
16
- end
17
-
18
- def arity
19
- 1
20
- end
21
-
22
- # @param actuals [Array<Term>] A two-elements array
23
- # @param anEnv [Vocabulary] A vocabulary object
24
- # @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
25
- def solver_for(actuals, anEnv)
26
- args = *validated_args(actuals)
27
- DuckFiber.new(:custom) do
28
- outcome = tap(args.first, anEnv)
29
- # outcome.prune!
30
- end
31
- end
32
-
33
- def tap(aGoal, anEnv)
34
- require 'debug'
35
- f1 = aGoal.attain(anEnv)
36
- outcome1 = f1.resume
37
- # key = outcome1.associations.keys.first
38
- # outcome1.associations['x'] = outcome1.associations[key]
39
- # outcome1.associations.delete(key)
40
- outcome1
41
- end
42
- end # class
43
-
44
- Tap.instance.freeze
45
- end # module
46
- 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?(LogVarRef)
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?(LogVarRef) }
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?(LogVarRef) }
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?(LogVarRef) && 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, LogVarRef] 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, LogVarRef] 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 [LogVarRef]
264
- # @return [Freshness]
265
- def freshness_ref(aVariableRef)
266
- walker = AssociationWalker.new
267
- walker.determine_freshness(aVariableRef, self)
268
- end
269
-
270
- # @param aVariableRef [LogVarRef]
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