tla-trace-filter 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,85 @@
1
+ # coding: utf-8
2
+ require "treetop"
3
+
4
+ require File.join( File.dirname( __FILE__), 'node_extensions.rb')
5
+
6
+ module TlaTraceFilter
7
+ module Parser
8
+
9
+ class ParseException < Exception; end
10
+
11
+ class Parser
12
+
13
+ # @# @!attribute [TlaTracefilter::Parser::Parser] parser
14
+ # Load the Treetop grammar from the 'sexp_parser' file, and
15
+ # create a new instance of that parser as a class variable
16
+ # so we don't have to re-create it every time we need to
17
+ # parse a string
18
+ @@parser = nil
19
+
20
+ def initialize( options={} )
21
+ end
22
+
23
+ def self.parser_instance
24
+ return @@parser if @@parser
25
+ grammarPath = File.join( File.dirname( __FILE__), "grammar.treetop")
26
+ Treetop.load( grammarPath )
27
+ @@parser = TlaTraceFilter::Parser::GrammarParser.new
28
+ end
29
+
30
+ # ------------------------------------------------------------------
31
+ # @!group Services
32
+
33
+ # Parse 'data' starting with symbol start.
34
+ #
35
+ # @param start [Symbol] treetop grammar rule where to start
36
+ #
37
+ # @return [SyntaxNode] Parser tree
38
+ def parse( data, start = nil )
39
+ parser = self.class.parser_instance
40
+ tree = parser.parse(data, :root=>start )
41
+
42
+ # If the AST is nil then there was an error during parsing
43
+ # we need to report a simple error message to help the user
44
+ if(tree.nil?)
45
+ # adopted from http://whitequark.org/blog/2011/09/08/treetop-typical-errors/
46
+ parser.failure_reason =~ /(Expected .*) after/m
47
+ expected = $1.nil? ? parser.failure_reason : $1
48
+ msg= <<~EOS
49
+ Parse error:
50
+ Line : #{parser.failure_line}, offset : #{parser.index}
51
+ #{expected.gsub( "\n", '$NEWLINE')}
52
+ #{data.lines.to_a[ parser.failure_line()-1 ]}
53
+ #{'~' * (parser.failure_column-1)}^
54
+ EOS
55
+ # puts msg
56
+ raise ParseException.new msg
57
+ end
58
+
59
+ # self.class.clean_tree( tree )
60
+
61
+ tree
62
+ end
63
+
64
+ # @!endgroup
65
+
66
+ # ------------------------------------------------------------------
67
+ # @!group prvate helpser
68
+
69
+ # we have created a tree from our input, but it has a lot of
70
+ # extraneous nodes that we don’t need or care about. Let’s put in
71
+ # a system for cleaning up the tree:
72
+ def self.clean_tree(root_node)
73
+ return if(root_node.elements.nil?)
74
+ # comment the following line if parse loosees nodes
75
+ root_node.elements.delete_if{|node| node.class.name == "Treetop::Runtime::SyntaxNode" }
76
+ root_node.elements.each {|node| self.clean_tree(node) }
77
+ end
78
+
79
+ # @!endgroup
80
+
81
+
82
+ end # class parser
83
+
84
+ end
85
+ end
@@ -0,0 +1,510 @@
1
+ # coding: utf-8
2
+ require "mustache"
3
+ require "json"
4
+ require "yaml"
5
+ module TlaTraceFilter
6
+
7
+
8
+ class RenderException < Exception; end
9
+
10
+
11
+ class Render < Mustache
12
+
13
+ include TlaTraceFilter::Util::MyLogger
14
+
15
+ # ------------------------------------------------------------------
16
+ # @!group Constants
17
+
18
+ # Default templates for inputState, call, and outputState
19
+ API_INPUT_STATE_DEFAULT_PARTIAL = "api-call-input"
20
+ API_CALL_DEFAULT_PARTIAL = "api-call-default"
21
+ API_RETURN_DEFAULT_PARTIAL = "api-call-return"
22
+ API_OUTPUT_STATE_DEFAULT_PARTIAL = "api-call-output"
23
+
24
+ # Keys mapping to the output templates
25
+ API_DEFAULT_KEY = "default"
26
+ API_EMPTY_KEY = "empty"
27
+
28
+ # @!endgroup
29
+
30
+
31
+ # ------------------------------------------------------------------
32
+ # @!group Attributes
33
+
34
+ # @# @!attribute [Hash] hash mapping partial names to mustache templates
35
+ attr_accessor :partials
36
+
37
+ # @# @!attribute [Array] templatePath array of path to search for
38
+ # templates
39
+ attr_accessor :templatePaths
40
+
41
+ # @# @!attribute [Hash] callPartials hash mapping interface-names
42
+ # (with default) to partial name
43
+ attr_accessor :callPartials
44
+
45
+ # @# @!attribute [Hash] returnPartials hash mapping interface-names
46
+ # (with default) to partial name
47
+ attr_accessor :returnPartials
48
+
49
+ # @# @!attribute [Hash] inputStatePartials hash mapping interface-names
50
+ # (with default) to partial name
51
+ attr_accessor :inputStatePartials
52
+
53
+ # @# @!attribute [Hash] outputStatePartials hash mapping interface-names
54
+ # (with default) to partial name
55
+ attr_accessor :outputStatePartials
56
+
57
+ # @!endgroup
58
+
59
+
60
+ # ------------------------------------------------------------------
61
+ # @!group Constructor
62
+
63
+ def initialize( options = {} )
64
+ # super
65
+ @logger = getLogger( nil, options )
66
+ @logger.info "#{__method__}: initalized"
67
+
68
+ configMustache( options )
69
+
70
+ initPartials
71
+
72
+ end
73
+
74
+ def configMustache( options )
75
+
76
+ # exception for non existing reference
77
+ self.raise_on_context_miss=options[:debug_mustache]
78
+
79
+ # default search only GEM templates
80
+ self.templatePaths = [ File.join( File.dirname(__FILE__), "../../mustache/") ]
81
+ # user defined template search path prepended
82
+ self.templatePaths = [ options[:mustache] ] + templatePaths if options[:mustache]
83
+ end
84
+
85
+ def initPartials
86
+ self.partials = {}
87
+ API_CALL_INIT( "" )
88
+ API_RETURN_INIT( "" )
89
+ API_INPUT_STATE_INIT( "" )
90
+ API_OUTPUT_STATE_INIT( "" )
91
+ # self.callPartials = {}
92
+ # self.inputStatePartials = {}
93
+ # self.outputStatePartials = {}
94
+ end
95
+
96
+ # @!endgroup
97
+
98
+ # ------------------------------------------------------------------
99
+ # @!group Initialize dynamic dispatchers
100
+
101
+ # Lambda to configure API_CALL dynamic dispatch. If no data given
102
+ # results calling to default templates
103
+ def API_CALL_INIT( data )
104
+ @logger.info "#{__method__}: data=#{data}"
105
+ begin
106
+ self.callPartials = data.nil? || data == "" ? {} : YAML.load( data )
107
+ rescue Exception => e
108
+ msg = <<-EOS
109
+ #{e}
110
+
111
+ Error parsing YAML data in API_CALL_INIT:
112
+
113
+ #{data}
114
+ EOS
115
+ @logger.error( "#{__method__}: #{msg}" )
116
+ raise Exception.new, msg, e.backtrace
117
+ end
118
+
119
+ @logger.debug "#{__method__}: callPartials=#{callPartials}" if @logger.debug?
120
+
121
+ # ensure that 'default' and 'empty' partials are defined
122
+ self.callPartials[API_DEFAULT_KEY] = API_CALL_DEFAULT_PARTIAL if callPartials[API_DEFAULT_KEY].nil?
123
+ self.callPartials[API_EMPTY_KEY] = API_CALL_DEFAULT_PARTIAL if callPartials[API_EMPTY_KEY].nil?
124
+
125
+ end
126
+
127
+
128
+ # Initiliaze template api return templates. If no data given
129
+ # results calling to default templates
130
+ def API_RETURN_INIT( data )
131
+
132
+ @logger.info "#{__method__}: data=#{data}"
133
+ begin
134
+ self.returnPartials = data.nil? || data == "" ? {} : YAML.load( data )
135
+ rescue Exception => e
136
+ msg = <<-EOS
137
+ #{e}
138
+
139
+ Error parsing YAML data in API_RETURN_INIT:
140
+
141
+ #{data}
142
+ EOS
143
+ @logger.error( "#{__method__}: #{msg}" )
144
+ raise Exception.new, msg, e.backtrace
145
+ end
146
+
147
+ @logger.debug "#{__method__}: returnPartials=#{returnPartials}" if @logger.debug?
148
+
149
+ # ensure that 'default' and 'empty' partials are defined
150
+ self.returnPartials[API_DEFAULT_KEY] = API_RETURN_DEFAULT_PARTIAL if returnPartials[API_DEFAULT_KEY].nil?
151
+ self.returnPartials[API_EMPTY_KEY] = API_RETURN_DEFAULT_PARTIAL if returnPartials[API_EMPTY_KEY].nil?
152
+
153
+ end
154
+
155
+ # Initiliaze template calls. If no data given results calling to
156
+ # default templates
157
+ def API_INPUT_STATE_INIT( data)
158
+ @logger.info "#{__method__}: data=#{data}"
159
+ begin
160
+ self.inputStatePartials = data.nil? || data == "" ? {} : YAML.load( data )
161
+ rescue Exception => e
162
+ msg = <<-EOS
163
+ #{e}
164
+
165
+ Error parsing YAML data in API_INPUT_STATE_INIT:
166
+
167
+ #{data}
168
+ EOS
169
+ @logger.error( "#{__method__}: #{msg}" )
170
+ raise Exception.new, msg, e.backtrace
171
+ end
172
+
173
+ @logger.debug "#{__method__}: inputStatePartials=#{inputStatePartials}" if @logger.debug?
174
+
175
+ # ensure that 'default' and 'empty' partials are defined
176
+ self.inputStatePartials[API_DEFAULT_KEY] = API_INPUT_STATE_DEFAULT_PARTIAL if inputStatePartials[API_DEFAULT_KEY].nil?
177
+ self.inputStatePartials[API_EMPTY_KEY] = API_OUTPUT_STATE_DEFAULT_PARTIAL if inputStatePartials[API_EMPTY_KEY].nil?
178
+
179
+ end
180
+
181
+ # Lambda to initiliaze template calls. If no data given results
182
+ # calling to default templates
183
+ def API_OUTPUT_STATE_INIT( data)
184
+ @logger.info "#{__method__}: data=#{data}"
185
+ begin
186
+ self.outputStatePartials = data.nil? || data == "" ? {} : YAML.load( data )
187
+ rescue Exception => e
188
+ msg = <<-EOS
189
+ #{e}
190
+
191
+ Error parsing YAML data in API_INIT:
192
+
193
+ #{data}
194
+ EOS
195
+ @logger.error( "#{__method__}: #{msg}" )
196
+ raise Exception.new, msg, e.backtrace
197
+ end
198
+
199
+ @logger.debug "#{__method__}: outputStatePartials=#{outputStatePartials}" if @logger.debug?
200
+
201
+ # ensure that 'default' and 'empty' partials are defined
202
+ self.outputStatePartials[API_DEFAULT_KEY] = API_OUTPUT_STATE_DEFAULT_PARTIAL if outputStatePartials[API_DEFAULT_KEY].nil?
203
+ self.outputStatePartials[API_EMPTY_KEY] = API_OUTPUT_STATE_DEFAULT_PARTIAL if outputStatePartials[API_EMPTY_KEY].nil?
204
+
205
+ end
206
+
207
+
208
+ # @return [String] name of partial template to render API-return for
209
+ # interfaceOperation, use default if not found
210
+ #
211
+ # Implementation makes a lookup to returnPartials
212
+ def getReturnPartialName( interfaceOperation ) #
213
+ returnPartialName = nil
214
+ returnPartialName = returnPartials[API_EMPTY_KEY] if interfaceOperation.nil?
215
+ returnPartialName = returnPartials[interfaceOperation] if returnPartialName.nil?
216
+ returnPartialName = returnPartials[API_DEFAULT_KEY] if returnPartialName.nil?
217
+ raise "Missing could not resolve '#{interfaceOperation}' in returnPartials #{returnPartials}" if returnPartialName.nil?
218
+ @logger.info "#{__method__}: API-return to interfaceOperation '#{interfaceOperation}' render with '#{returnPartialName}'"
219
+ returnPartialName
220
+ end
221
+
222
+ # @return [String] name of partial template to render API-call for
223
+ # interfaceOperation, use default if not found
224
+ #
225
+ # Implementation makes a lookup to callPartials
226
+ def getCallPartialName( interfaceOperation ) #
227
+
228
+ callPartialName = nil
229
+ callPartialName = callPartials[API_EMPTY_KEY] if interfaceOperation.nil?
230
+ callPartialName = callPartials[interfaceOperation] if callPartialName.nil?
231
+ callPartialName = callPartials[API_DEFAULT_KEY] if callPartialName.nil?
232
+ raise "Missing could not resolve '#{interfaceOperation}' in callPartials #{callPartials}" if callPartialName.nil?
233
+
234
+ @logger.info "#{__method__}: API-call to interfaceOperation '#{interfaceOperation}' render with '#{callPartialName}'"
235
+ callPartialName
236
+ end
237
+
238
+ def getInputStatePartialName( interfaceOperation )
239
+ inputStatePartialName = nil
240
+ inputStatePartialName = inputStatePartials[API_EMPTY_KEY] if interfaceOperation.nil?
241
+ inputStatePartialName = inputStatePartials[interfaceOperation] if inputStatePartialName.nil?
242
+ inputStatePartialName = inputStatePartials[API_DEFAULT_KEY] if inputStatePartialName.nil?
243
+ raise "Missing could not resolve '#{interfaceOperation}' in inputStatePartials #{inputStatePartials}" if inputStatePartialName.nil?
244
+
245
+ @logger.info "#{__method__}: API-call to interfaceOperation '#{interfaceOperation}' render with '#{inputStatePartialName}'"
246
+ inputStatePartialName
247
+ end
248
+
249
+ def getOutputStatePartialName( interfaceOperation )
250
+ outputStatePartialName = nil
251
+ outputStatePartialName = outputStatePartials[API_EMPTY_KEY] if interfaceOperation.nil?
252
+ outputStatePartialName = outputStatePartials[interfaceOperation] if outputStatePartialName.nil?
253
+ outputStatePartialName = outputStatePartials[API_DEFAULT_KEY] if outputStatePartialName.nil?
254
+ raise "Missing could not resolve '#{interfaceOperation}' in outputStatePartials #{outputStatePartials}" if outputStatePartialName.nil?
255
+
256
+ @logger.info "#{__method__}: API-call to interfaceOperation '#{interfaceOperation}' render with '#{outputStatePartialName}'"
257
+ outputStatePartialName
258
+ end
259
+
260
+ # @!endgroup
261
+
262
+
263
+ # ------------------------------------------------------------------
264
+ # @!group Mustache actions
265
+
266
+
267
+ # @return [Integer] line number correspoding character
268
+ # 'interface.source.sourceLine' in file
269
+ # 'options.src_dir'/'interface.source.sourceModule', 0 if path
270
+ # not found
271
+ def SOLC_LINE( data=nil )
272
+
273
+ # Access mustache context
274
+ options = context[:options]
275
+ interface = context[:interface]
276
+
277
+ @logger.debug "#{__method__}: options=#{options}, interface #{interface}" if @logger.debug?
278
+
279
+
280
+ # options defines valid data
281
+ return 0 unless options && options["src_dir"] && File.exists?( options["src_dir"] )
282
+
283
+ return 0 unless interface && interface[:source] && interface[:source][:sourceModule] && File.exists?( File.join( options["src_dir"], interface[:source][:sourceModule] ))
284
+
285
+ fileName = File.join( options["src_dir"], interface[:source][:sourceModule] )
286
+ # now possible access line
287
+ characters = 0
288
+ lineCount = 0
289
+ File.open( fileName ).each do |line|
290
+ # count characters && increment line number
291
+ characters += line.length
292
+ lineCount += 1
293
+ break if characters > interface[:source][:sourceLine]
294
+ end
295
+ @logger.info "#{__method__}: lineCount=#{lineCount} for "
296
+ # number of 'characters' mapped to lineCount
297
+ lineCount
298
+ end
299
+
300
+ # Lamba called for 'API_INPUT_STATE' to dispatch dynamically rendering of
301
+ # [:interface][:interface_operation] inputState.
302
+ def API_INPUT_STATE
303
+ @logger.info "#{__method__}: [:interface][:interface_operation]=#{context[:interface][:interface_operation]}, now=#{context[:now]}"
304
+ callPartialName = getInputStatePartialName( context[:interface][:interface_operation] )
305
+ template = partial( callPartialName )
306
+ render( template, context )
307
+ end
308
+
309
+ # Lamba called for 'API_OUTPUT_STATE' to dispatch dynamically rendering of
310
+ # [:interface][:interface_operation] outputState.
311
+ def API_OUTPUT_STATE
312
+ @logger.info "#{__method__}: [:interface][:interface_operation]=#{context[:interface][:interface_operation]} now=#{context[:now]}"
313
+ callPartialName = getOutputStatePartialName( context[:interface][:interface_operation] )
314
+ template = partial( callPartialName )
315
+ render( template, context )
316
+ end
317
+
318
+ def API_RETURN
319
+
320
+ interfaceOperation = context[:interface][:interface_operation]
321
+ responseKey = context[:interface][:response_key]
322
+
323
+ # partial access like Bank(saveAccout)
324
+ callPartialName = getReturnPartialName( interfaceOperation )
325
+ template = partial( callPartialName )
326
+
327
+ # context access likke i_Bank_saveAccount_
328
+ # raise "keys=#{context[:outputState][:responses].to_yaml}"
329
+ context[:api_return] = context[:outputState][:responses][responseKey]
330
+
331
+ render( template, context )
332
+
333
+ end
334
+
335
+
336
+
337
+ # Mustache lamba to render API calls with input data.
338
+ #
339
+ # Lambda depends on context having corect information.
340
+ #
341
+ # context[:state_space][:step] ->
342
+ #
343
+ def API_CALL( data=nil)
344
+ # partialName = data || context[:interface][:interface_operation] if context && context[:interface]
345
+ # state_space = context[:parsed][:state_space] if context && context[:parsed]
346
+ # raise "Missing callPartial - error in API_INIT?" if callPartial.nil?
347
+ # return "" if state_space.nil?
348
+
349
+ callPartialName = getCallPartialName( context[:interface][:interface_operation] )
350
+ template = partial( callPartialName )
351
+
352
+ # # string procedure input variables ends with
353
+ # api = "#{context[:interface][:interface_name]}_input"
354
+
355
+ # # find name of procedure input varibable
356
+ # api = context[:parsed][:state_space].keys.find { |k| k.to_s.end_with?( api ) } unless api.nil?
357
+
358
+ # # access procedure input data
359
+ # api = context[:parsed][:state_space][api] unless api.nil?
360
+
361
+ # # access procedure input for currently running interface opertaion
362
+ # api = api[context[:interface][:interface_operation]] unless api.nil?
363
+
364
+ # # create context
365
+ # context[:api_input] = api
366
+
367
+ context[:api_input] =
368
+
369
+ if context[:parsed] && context[:parsed][:state_space] && context[:parsed][:state_space][:step_input]
370
+ context[:parsed][:state_space][:step_input]
371
+ else
372
+
373
+ api = "#{context[:interface][:interface_name]}_input"
374
+
375
+ # find name of procedure input varibable
376
+ api = context[:parsed][:state_space].keys.find { |k| k.to_s.end_with?( api ) } unless api.nil?
377
+
378
+ # access procedure input data
379
+ api = context[:parsed][:state_space][api] unless api.nil?
380
+
381
+ # access procedure input for currently running interface opertaion
382
+ api = api[context[:interface][:interface_operation]] unless api.nil?
383
+
384
+ api = { :error => "No input found in context[:parsed][:state_space][:step_input] - maybe set 'SetStepInput = TRUE'"} if api.nil?
385
+
386
+ api
387
+ end
388
+
389
+ # render output
390
+ render( template, context )
391
+ end
392
+
393
+
394
+ # @!endgroup
395
+
396
+
397
+
398
+ # ------------------------------------------------------------------
399
+ # @!group Mustache extesion points
400
+
401
+ # Override from mustache.rb to use my 'get_partial'.
402
+ def partial(name)
403
+ get_partial(name)
404
+ end
405
+
406
+
407
+ # @!endgroup
408
+
409
+
410
+ # ------------------------------------------------------------------
411
+ # @!group Service intefaces
412
+
413
+ # @param partial [String] name of partial for rendering
414
+ # @param data [Hash] hash to render
415
+ def render_str( template, data )
416
+ render( template, data )
417
+ end
418
+
419
+ # @!endgroup
420
+
421
+ # ------------------------------------------------------------------
422
+ # @!group Service implementation
423
+
424
+ # Return cached partial, use {#read_partial} to read if not
425
+ # defined
426
+ #
427
+ # @param name [String] partial name to read
428
+ def get_partial(name)
429
+ return partials[name] if partials[name]
430
+ @logger.info "#{__method__}: reading partial #{name}"
431
+
432
+ self.partials[name] = read_partial(name)
433
+ @logger.debug "#{__method__}: partial #{name} --> #{partials[name]}" if @logger.debug?
434
+
435
+ partials[name]
436
+ end
437
+
438
+ # Read partial with name 'name'
439
+ #
440
+ # @param name [String] name of partial to read
441
+ def read_partial(name)
442
+
443
+ tName = templateName(name)
444
+ templatePaths.each do |path|
445
+
446
+ # read from file (ends with / ) or from gem-directory
447
+ content = if path[-1] == '/'
448
+ read_partial_file( path, tName )
449
+ else
450
+ read_gem_partial_file( path, tName )
451
+ end
452
+ return content unless content.nil?
453
+ end
454
+
455
+ # Error if not found, else read file content
456
+ raise <<-EOS
457
+ Could not find template '#{name}' in directories #{templatePaths.join(',')}
458
+ EOS
459
+
460
+ end
461
+
462
+ # @return [String,nil] read content of file 'path/templateName',
463
+ # nil if not found
464
+ def read_partial_file( path, templateName )
465
+ raise RenderException.new "Expected '#{path}' to end with / character" unless path[-1,1] == '/'
466
+ filePath = File.join( path, templateName )
467
+ return File.read( filePath ) if File.exist? filePath
468
+ end
469
+
470
+ def templateName( name )
471
+ "#{name}.mustache"
472
+ end
473
+
474
+
475
+ # # @return [Array<String>] array of template paths
476
+ # def templatePaths
477
+ # ["/tmp", File.join( File.dirname(__FILE__), "../../mustache") ]
478
+ # end
479
+
480
+
481
+ # @!endgroup
482
+
483
+ # # ------------------------------------------------------------------
484
+ # @!group Access partial from GEM
485
+
486
+ # Read 'templateName' from '@gemName/mustache' -directory
487
+ def read_gem_partial_file( gemName , templateName)
488
+ gemPath = gem_location( gemName )
489
+ read_partial_file( File.join(gemPath, "mustache" ) + "/", templateName )
490
+ end
491
+
492
+ # Find installation directory of 'gemName' Should find gems
493
+ # defined in gemspec and Gemfile. Should not find gemName
494
+ # otherwiset (= also NOT even if installed locally in gems)
495
+ def gem_location( gemName )
496
+ gemSpec = Gem.loaded_specs[gemName]
497
+ raise "Unknown gemName #{gemName}" if gemSpec.nil?
498
+ gemSpec.gem_dir
499
+ end
500
+
501
+
502
+
503
+ # @!endgroup
504
+
505
+
506
+
507
+
508
+
509
+ end
510
+ end