tla-parser-s 0.1.0
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 +7 -0
- data/README.md +75 -0
- data/VERSION +1 -0
- data/bin/tla-resolver.rb +7 -0
- data/lib/cli/cli.rb +90 -0
- data/lib/parser/exception.rb +6 -0
- data/lib/parser/lvalue.rb +63 -0
- data/lib/parser/parser.rb +129 -0
- data/lib/parser/parser_nodes.rb +1063 -0
- data/lib/parser/parser_sexp.treetop +442 -0
- data/lib/semantics/context.rb +355 -0
- data/lib/semantics/exception.rb +13 -0
- data/lib/semantics/resolver.rb +327 -0
- data/lib/semantics/symbol_table.rb +139 -0
- data/lib/tla-parser-s.rb +15 -0
- data/lib/utils/logger.rb +80 -0
- data/lib/utils/syntax_node.rb +73 -0
- data/lib/utils/version.rb +13 -0
- data/spec/fixtures/callables1.tla +64 -0
- data/spec/fixtures/directives.tla +7 -0
- data/spec/fixtures/resolver1/comments.tla +1 -0
- data/spec/fixtures/resolver1/directives.cfg +6 -0
- data/spec/fixtures/resolver1/directives.tla +12 -0
- data/spec/fixtures/resolver1/empty.tla +0 -0
- data/spec/fixtures/resolver1/macro1.tla +3 -0
- data/spec/fixtures/resolver1/macro2.tla +3 -0
- data/spec/fixtures/resolver1/op1.tla +3 -0
- data/spec/fixtures/resolver1/op2.tla +3 -0
- data/spec/fixtures/resolver1/proc1.tla +4 -0
- data/spec/fixtures/resolver1/proc2.tla +7 -0
- data/spec/fixtures/resolver1/proc3.tla +4 -0
- data/spec/fixtures/resolver1/proc4.tla +4 -0
- data/spec/fixtures/resolver1/proc4_hide.tla +4 -0
- data/spec/fixtures/resolver1/proc5.tla +4 -0
- data/spec/fixtures/resolver1/proc6.tla +4 -0
- data/spec/fixtures/resolver1/proc7.tla +4 -0
- data/spec/fixtures/resolver1/stmt_assert.tla +8 -0
- data/spec/fixtures/resolver1/stmt_assign.tla +6 -0
- data/spec/fixtures/resolver1/stmt_assign2.tla +6 -0
- data/spec/fixtures/resolver1/stmt_cond.tla +13 -0
- data/spec/fixtures/resolver1/stmt_either.tla +12 -0
- data/spec/fixtures/resolver1/stmt_print.tla +5 -0
- data/spec/fixtures/resolver1/var4.tla +1 -0
- data/spec/fixtures/resolver1/var5.tla +1 -0
- data/spec/fixtures/resolver1/var_choose_except.tla +2 -0
- data/spec/fixtures/resolver1/var_quantify.tla +4 -0
- data/spec/fixtures/resolver1/var_rec_except.tla +4 -0
- data/spec/fixtures/resolver1/var_rec_expr.tla +3 -0
- data/spec/fixtures/resolver1/var_record.tla +2 -0
- data/spec/fixtures/resolver1/var_seq.tla +1 -0
- data/spec/fixtures/resolver1/var_set.tla +1 -0
- data/spec/fixtures/resolver1/var_set_expr_map.tla +1 -0
- data/spec/fixtures/resolver1/var_x.tla +1 -0
- data/spec/fixtures/resolver1/variables.tla +4 -0
- data/spec/parser/parser_fixtures_spec.rb +117 -0
- data/spec/parser/parser_spec.rb +1649 -0
- data/spec/semantics/context_spec.rb +392 -0
- data/spec/semantics/resolver_spec.rb +364 -0
- data/spec/semantics/symbol_table_spec.rb +144 -0
- data/spec/spec_helper.rb +5 -0
- data/tla-parser-s.gemspec +41 -0
- metadata +153 -0
@@ -0,0 +1,327 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module TlaParserS
|
4
|
+
|
5
|
+
class Resolver
|
6
|
+
|
7
|
+
attr_reader :context # TlaParserS::Context
|
8
|
+
# create context to resolve
|
9
|
+
# references
|
10
|
+
|
11
|
+
|
12
|
+
attr_reader :parser # TlaParserS::Parser to
|
13
|
+
# parse TLA+ source code
|
14
|
+
|
15
|
+
|
16
|
+
attr_reader :report_unresolved # true if warn on unresolves
|
17
|
+
|
18
|
+
attr_reader :directives # [String:Array] of names
|
19
|
+
# for INVARIANTS and
|
20
|
+
# ASSUMPTIONS
|
21
|
+
|
22
|
+
attr_reader :directive_modules # array of module names containing
|
23
|
+
# directives
|
24
|
+
|
25
|
+
# ******************************************************************
|
26
|
+
# Load TLA+ snippets from file (or strings in unit test), and build
|
27
|
+
# global context to use in resolving.
|
28
|
+
#
|
29
|
+
# constructor:
|
30
|
+
# #initContext: add initContext
|
31
|
+
# #initSnippets: parse TLA+ snippets to context
|
32
|
+
# #resolveModules: resolve modules
|
33
|
+
#
|
34
|
+
# ******************************************************************
|
35
|
+
|
36
|
+
|
37
|
+
# ------------------------------------------------------------------
|
38
|
+
# Logger
|
39
|
+
|
40
|
+
PROGNAME = "resolver" # progname for logger
|
41
|
+
include TlaParserS::Utils::MyLogger # mix logger
|
42
|
+
|
43
|
+
# ------------------------------------------------------------------
|
44
|
+
# constructore
|
45
|
+
def initialize( options={} )
|
46
|
+
|
47
|
+
@logger = getLogger( PROGNAME, options )
|
48
|
+
@logger.info( "#{__method__} initialized" )
|
49
|
+
|
50
|
+
@report_unresolved = options[:report_unresolved]
|
51
|
+
|
52
|
+
|
53
|
+
@context = TlaParserS::Context.new( options )
|
54
|
+
@parser = TlaParserS::Parser.new( options )
|
55
|
+
|
56
|
+
# initiall no directives
|
57
|
+
@directives = []
|
58
|
+
|
59
|
+
# initially no directive modules
|
60
|
+
@directive_modules = []
|
61
|
+
end
|
62
|
+
|
63
|
+
# Parse entry ie. file or possibly a string
|
64
|
+
#
|
65
|
+
# @param entry [File|String] to parse
|
66
|
+
# @param moduleName [String] identifying 'entry' for error messages
|
67
|
+
# @return [Snippets] parsed syntax tree node
|
68
|
+
def parseSnippets( entry, moduleName )
|
69
|
+
begin
|
70
|
+
parser.parse( entry )
|
71
|
+
rescue ParseException => ee
|
72
|
+
msg = "Parsing '#{moduleName}' results to error \n\n#{ee.message}"
|
73
|
+
@logger.error( "#{__method__} #{msg}" )
|
74
|
+
raise ParseException.new( ee ), msg, ee.backtrace
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
# Add of definition names in 'snippets' to '@directives', and
|
80
|
+
# 'moduleName' to '@directive_modules' if any 'snippets' contained
|
81
|
+
# 'directive_definitions'
|
82
|
+
#
|
83
|
+
# @param moduleName [String] name of module where 'snippets' are parses
|
84
|
+
# @param snippets [Snippets] parsed snippts, respons to :directive_definitions
|
85
|
+
def addDirectives( snippets, moduleName )
|
86
|
+
new_directives = snippets.directive_definitions
|
87
|
+
|
88
|
+
@logger.debug( "#{__method__} moduleName=#{moduleName} -> new_directives=#{new_directives}" )
|
89
|
+
@directives = directives + new_directives
|
90
|
+
|
91
|
+
@directive_modules << moduleName if new_directives && new_directives.any?
|
92
|
+
end
|
93
|
+
|
94
|
+
# Build initial context (TLA+ reserved words, etc)
|
95
|
+
#
|
96
|
+
# @param names [String:Array] to put context
|
97
|
+
def initContext( names )
|
98
|
+
|
99
|
+
# create global context
|
100
|
+
context.initEntries(
|
101
|
+
names.map do |name|
|
102
|
+
{
|
103
|
+
:context_type => 'initalContext',
|
104
|
+
:context => "initalContext",
|
105
|
+
:symbol_type => "initalContext",
|
106
|
+
:symbol_name => name
|
107
|
+
}
|
108
|
+
end
|
109
|
+
)
|
110
|
+
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
# ------------------------------------------------------------------
|
116
|
+
# Load TLA+ snippets from file (or strings in unit test), and
|
117
|
+
# build global context to use in resolving.
|
118
|
+
#
|
119
|
+
# @param entries [ File:Array | String:Array] containg TLA+ snippets
|
120
|
+
#
|
121
|
+
# @param blk [Block] yields entry
|
122
|
+
|
123
|
+
def initSnippets( entries, &blk )
|
124
|
+
|
125
|
+
# create global context
|
126
|
+
context.pushContext
|
127
|
+
|
128
|
+
# iterate
|
129
|
+
entries && entries.each_with_index do |entry,i|
|
130
|
+
|
131
|
+
# skip directories
|
132
|
+
# next if entry.is_a?( File ) && File.directory?( entry )
|
133
|
+
next if File.directory?( entry )
|
134
|
+
|
135
|
+
# meaningfull modulename
|
136
|
+
moduleName = entry.is_a?( File ) ? entry.path : "entries[#{i}]"
|
137
|
+
|
138
|
+
@logger.info( "#{__method__} entry=#{entry}" )
|
139
|
+
begin
|
140
|
+
|
141
|
+
# String|File --> AST
|
142
|
+
snippets = parseSnippets( entry, moduleName )
|
143
|
+
yield( true, entry ) if blk
|
144
|
+
|
145
|
+
# collect directive defitions
|
146
|
+
addDirectives( snippets, moduleName )
|
147
|
+
|
148
|
+
# add to contex tree nodes, which respond to
|
149
|
+
# 'symbol_definitions'
|
150
|
+
context.addContext( snippets, moduleName )
|
151
|
+
|
152
|
+
rescue TlaParserS::ParseException => e
|
153
|
+
|
154
|
+
# quit if no block - or block decides to quit
|
155
|
+
if !blk || !yield( false, entry, e ) then
|
156
|
+
raise e
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
self
|
163
|
+
end
|
164
|
+
|
165
|
+
# ------------------------------------------------------------------
|
166
|
+
# Find modules 1) containing TLA+ snippets reachable from
|
167
|
+
# 'entryPoints', 2) modules reachabable for directive defintision,
|
168
|
+
# and 3) modules containing directive definitions
|
169
|
+
#
|
170
|
+
# @param entryPoints [String:Array] TLA+ definition names to implement
|
171
|
+
# @return [String:Array] names of modules needed in implementation
|
172
|
+
def resolveModules( entryPoints, &blk )
|
173
|
+
definitionModules = resolveModulesDo( entryPoints + directives, &blk )
|
174
|
+
return definitionModules + directive_modules
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
# ------------------------------------------------------------------
|
179
|
+
# Find modules containing TLA+ snippets needed to implement code
|
180
|
+
# for 'entryPoints'. Notice: A module may include additional
|
181
|
+
# entrypoint, which must also be satisfied.
|
182
|
+
#
|
183
|
+
# @param entryPoints [String:Array] TLA+ definition names to implement
|
184
|
+
#
|
185
|
+
# @param blk [Proc] called with (key, String:Array), where key \in
|
186
|
+
# {'start', 'resolved', 'unresolved'}, and String:Array list of
|
187
|
+
# symbols.
|
188
|
+
#
|
189
|
+
# @return [String:Arrays] of module names needed in implementation.
|
190
|
+
def resolveModulesDo( entryPoints, &blk )
|
191
|
+
|
192
|
+
@logger.info( "#{__method__} resolveModules for entryPoint=#{entryPoints.join(',')}" )
|
193
|
+
yield( "start", entryPoints ) if blk
|
194
|
+
|
195
|
+
# collected during processing
|
196
|
+
moduleNames = [].to_set
|
197
|
+
|
198
|
+
# collect here
|
199
|
+
unresolvedSymbols = []
|
200
|
+
|
201
|
+
unResolved = entryPoints.to_set
|
202
|
+
resolved = [].to_set
|
203
|
+
|
204
|
+
while TRUE do
|
205
|
+
|
206
|
+
# done - if no mode 'unResolved'
|
207
|
+
break if unResolved.empty?
|
208
|
+
# next to resolve
|
209
|
+
entryPoint = unResolved.to_a.shift
|
210
|
+
|
211
|
+
# array of hashes how entry point is resolved
|
212
|
+
resolvedSymbols = resolveEntryPoint( entryPoint )
|
213
|
+
@logger.debug( "#{__method__} entryPoint #{entryPoint} ->resolvedSymbols=#{resolvedSymbols}" )
|
214
|
+
|
215
|
+
resolvedModules = # array of mod.names
|
216
|
+
resolvedSymbols. # { :symbol=>, :resolved=> }
|
217
|
+
select { |e| e[:resolved] }. # successfully resolved
|
218
|
+
map { |e| e[:resolved] }. # discard :symbol =>.. :resolved => ...
|
219
|
+
select { |s| s[:module] }. # defines module ie. not
|
220
|
+
# TLA+ standar module, nor
|
221
|
+
# local context
|
222
|
+
map { |s| s[:module] } # extract moduleName
|
223
|
+
|
224
|
+
|
225
|
+
@logger.debug( "#{__method__} resolvedModules=#{resolvedModules}" )
|
226
|
+
newModules = resolvedModules.select { |m| !moduleNames.include?( m ) }
|
227
|
+
|
228
|
+
# collect unresolved
|
229
|
+
unresolvedSymbols +=
|
230
|
+
resolvedSymbols. # { :symbol=>, :resolved=> }
|
231
|
+
select { |r| r[:resolved].nil? }.# unresolved
|
232
|
+
map{ |r|
|
233
|
+
{
|
234
|
+
:symbol => r[:symbol],
|
235
|
+
:entry => entryPoint }} # add entry point causing error
|
236
|
+
|
237
|
+
# return list of definitions (ie. newEntryPoint) in 'modules'
|
238
|
+
newEntryPoints = entryPointsForModules( newModules )
|
239
|
+
@logger.debug( "#{__method__} newEntryPoints=#{newEntryPoints}" )
|
240
|
+
|
241
|
+
# one more resolved: move it to resolved
|
242
|
+
unResolved = unResolved.delete( entryPoint )
|
243
|
+
resolved = resolved.add( entryPoint )
|
244
|
+
|
245
|
+
# still to resolve: unResolved + newEntryPoints - resolved
|
246
|
+
unResolved = unResolved.union( newEntryPoints.to_set ).subtract( resolved )
|
247
|
+
@logger.debug( "#{__method__} unResolved=#{unResolved}" )
|
248
|
+
|
249
|
+
# collect modules
|
250
|
+
moduleNames = moduleNames.union( newModules.to_set )
|
251
|
+
|
252
|
+
end # looop for ever
|
253
|
+
|
254
|
+
# warn on output unresolved - if option set
|
255
|
+
reportUnresolved( unresolvedSymbols ) if report_unresolved
|
256
|
+
yield( "resolved", resolved.to_a ) if blk
|
257
|
+
yield( "unresolved", unresolvedSymbols ) if blk
|
258
|
+
|
259
|
+
@logger.info( "#{__method__} resolve-result ->#{moduleNames.to_a}" )
|
260
|
+
# set --> array
|
261
|
+
moduleNames.to_a
|
262
|
+
|
263
|
+
end # resolveModules
|
264
|
+
|
265
|
+
# Ouptput to stderr if 'unresolvedSymbols.any
|
266
|
+
#
|
267
|
+
# @param unresolvedSymbols [Hash:Array] of {:symbol,:entry} of unresolved symbols
|
268
|
+
#
|
269
|
+
def reportUnresolved( unresolvedSymbols )
|
270
|
+
|
271
|
+
warn <<-EOS if unresolvedSymbols.any?
|
272
|
+
Unresolved symbols:
|
273
|
+
-- #{unresolvedSymbols.map{ |u| 'Symbol \'' + u[:symbol] + '\' in entry \'' + u[:entry] + '\''}.join("\n -- ")}
|
274
|
+
EOS
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
# Return Hash:Array of symbols resolved in 'entryPoint'. First
|
279
|
+
# locates 'entryPoint' syntax tree in 'context', and then
|
280
|
+
# use 'resolveDefine' to resolve entries.
|
281
|
+
#
|
282
|
+
# @param entryPoint [String] name entry point to resolve
|
283
|
+
#
|
284
|
+
# @return [Hash:Array] identifier in entry points
|
285
|
+
def resolveEntryPoint( entryPoint )
|
286
|
+
symbolTableEntry = context.resolveSymbol( entryPoint )
|
287
|
+
@logger.debug( "#{__method__} symbolTableEntry #{entryPoint}-->#{symbolTableEntry}" )
|
288
|
+
if symbolTableEntry && symbolTableEntry[:resolved]
|
289
|
+
tree = symbolTableEntry[:resolved][:tree]
|
290
|
+
@logger.debug( "#{__method__} tree-->#{tree.inspect}" )
|
291
|
+
symbols = context.resolveDefine( tree )
|
292
|
+
@logger.debug( "#{__method__} resolveDefine-->#{symbols.join('\n')}" )
|
293
|
+
else
|
294
|
+
msg = <<-EOS
|
295
|
+
Unknown entrypoint '#{entryPoint}'
|
296
|
+
|
297
|
+
Known entry points: #{context.entries.join(',')}
|
298
|
+
EOS
|
299
|
+
@logger.error( "#{__method__} #{msg}" )
|
300
|
+
raise ResolverException.new msg
|
301
|
+
end
|
302
|
+
|
303
|
+
return [ symbolTableEntry ] + symbols
|
304
|
+
|
305
|
+
end
|
306
|
+
|
307
|
+
# return list of definitions in 'modules'
|
308
|
+
#
|
309
|
+
# @param modules [String:Array] of module names to include
|
310
|
+
# @return [String:Array] of entry points in the module
|
311
|
+
def entryPointsForModules( modules )
|
312
|
+
|
313
|
+
moduleSymbols = []
|
314
|
+
modules.each do |moduleName|
|
315
|
+
# resolve module entry poinsts
|
316
|
+
moduleEntries = context.resolveModule( moduleName )
|
317
|
+
|
318
|
+
moduleSymbols = moduleSymbols + moduleEntries
|
319
|
+
end
|
320
|
+
ret = moduleSymbols.map { |s| s[:symbol_name] }
|
321
|
+
@logger.debug( "#{__method__} modules=#{modules.join(',')}-->ret=#{ret.join(',')}" )
|
322
|
+
return ret
|
323
|
+
end
|
324
|
+
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module TlaParserS
|
4
|
+
|
5
|
+
class SymbolTable
|
6
|
+
|
7
|
+
attr_reader :stack # Array as symbol table context
|
8
|
+
|
9
|
+
# ------------------------------------------------------------------
|
10
|
+
# Logger
|
11
|
+
|
12
|
+
PROGNAME = "symbols" # progname for logger
|
13
|
+
include TlaParserS::Utils::MyLogger # mix logger
|
14
|
+
|
15
|
+
|
16
|
+
# ------------------------------------------------------------------
|
17
|
+
# constructore
|
18
|
+
def initialize( options={} )
|
19
|
+
|
20
|
+
@logger = getLogger( PROGNAME, options )
|
21
|
+
@logger.info( "#{__method__} initialized" )
|
22
|
+
|
23
|
+
@stack = []
|
24
|
+
end
|
25
|
+
|
26
|
+
# Add a hash (with :symbol_type, :context_type, :context,
|
27
|
+
# :symbol_name) to 'currentContext'
|
28
|
+
#
|
29
|
+
# @param entry [Hash] add hash to 'currentContext'
|
30
|
+
def addEntry( entry )
|
31
|
+
# get refeence to current contex
|
32
|
+
context = currentContext
|
33
|
+
context[entry[:symbol_name]] = entry
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param tree_node [SyntaxNode] node implementing
|
37
|
+
# `symbol_definitions` - method returning hash entries
|
38
|
+
# (:value,:node_type)
|
39
|
+
def addContext( tree_node, moduleName=nil )
|
40
|
+
|
41
|
+
if tree_node && tree_node.respond_to?(:symbol_definitions)
|
42
|
+
tree_node.symbol_definitions.each do |symbol_definition|
|
43
|
+
@logger.debug( "#{__method__} symbol_definition=#{symbol_definition}" )
|
44
|
+
entry = {
|
45
|
+
:context_type => tree_node.node_type,
|
46
|
+
:context => tree_node.name,
|
47
|
+
:symbol_type => symbol_definition[:node_type],
|
48
|
+
:symbol_name => symbol_definition[:value],
|
49
|
+
}
|
50
|
+
# optionally add moduleName
|
51
|
+
entry[:module] = moduleName if moduleName
|
52
|
+
|
53
|
+
# syntax tree defined only on procedure & macros
|
54
|
+
entry[:tree] = symbol_definition[:tree] if symbol_definition[:tree]
|
55
|
+
|
56
|
+
addEntry( entry )
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Create new context: add one more level to symbol table, add
|
62
|
+
# entries from 'tree_node.symbol_definitions'
|
63
|
+
#
|
64
|
+
# @param tree_node [SyntaxNode] define initial context content
|
65
|
+
def pushContext( tree_node, moduleName=nil )
|
66
|
+
|
67
|
+
context = {}
|
68
|
+
@stack.push( context )
|
69
|
+
|
70
|
+
addContext( tree_node, moduleName )
|
71
|
+
end
|
72
|
+
|
73
|
+
def popContext
|
74
|
+
if stack.length == 0 then
|
75
|
+
msg = "Stack underflow"
|
76
|
+
@logger.error( "#{__method__} #{msg}" )
|
77
|
+
raise SymbolTableException.new msg
|
78
|
+
end
|
79
|
+
@stack.pop
|
80
|
+
end
|
81
|
+
|
82
|
+
# @param level [int] number of levels to recurse (default 1)
|
83
|
+
# @return [String:Array] of entry names
|
84
|
+
|
85
|
+
def entries( level=1)
|
86
|
+
entries = []
|
87
|
+
stack.reverse_each do |context|
|
88
|
+
# add key to entries unless it is already there
|
89
|
+
context.keys().each { |k| entries << k unless entries.include?( k ) }
|
90
|
+
level -= 1
|
91
|
+
break if level <= 0
|
92
|
+
end
|
93
|
+
entries
|
94
|
+
end
|
95
|
+
|
96
|
+
def resolveContext( identifier )
|
97
|
+
stack.reverse_each do |context|
|
98
|
+
# @logger.debug "context=#{context}, identifier=#{identifier}"
|
99
|
+
return context[identifier] if context.has_key?( identifier )
|
100
|
+
end
|
101
|
+
return nil
|
102
|
+
end
|
103
|
+
|
104
|
+
# @param moduleName [String] name of module to resolve
|
105
|
+
# @return [Hash:Array] of symbol definition
|
106
|
+
# {:context_type,:context,:symbol_type,:symbol_name} where
|
107
|
+
# {:module} == 'moduleName'
|
108
|
+
|
109
|
+
|
110
|
+
def resolveModule( moduleName )
|
111
|
+
entries = []
|
112
|
+
stack.reverse_each do |context|
|
113
|
+
context.each do |symbol,entry|
|
114
|
+
@logger.debug( "#{__method__} symbol=#{symbol}, entry=#{entry[:module]}" )
|
115
|
+
entries << entry if entry[:module] == moduleName
|
116
|
+
end
|
117
|
+
end
|
118
|
+
@logger.debug( "#{__method__} entries-->#{entries.join(',')}" )
|
119
|
+
return entries
|
120
|
+
end
|
121
|
+
|
122
|
+
def currentContext
|
123
|
+
return stack.last
|
124
|
+
end
|
125
|
+
|
126
|
+
def dumpContext
|
127
|
+
stack.reverse_each.with_index do |context, i|
|
128
|
+
@logger.debug "Context #{i}"
|
129
|
+
@logger.debug context.to_yaml
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def contextLevels
|
134
|
+
0
|
135
|
+
end
|
136
|
+
|
137
|
+
end # class
|
138
|
+
|
139
|
+
end
|