tla2dot 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +125 -0
- data/bin/tla2dot.rb +5 -0
- data/lib/cli/cli.rb +114 -0
- data/lib/tla2dot/parser.tab.rb +712 -0
- data/lib/tla2dot/parser.y +415 -0
- data/lib/tla2dot/template.rb +184 -0
- data/lib/tla2dot.rb +10 -0
- data/lib/utils/logger.rb +70 -0
- data/mustache/defaults.mustache +19 -0
- data/mustache/root.mustache +20 -0
- data/mustache/stateOutput.mustache +1 -0
- metadata +135 -0
@@ -0,0 +1,415 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
#
|
3
|
+
# Parse state dump from TCL tlaplus
|
4
|
+
|
5
|
+
class Tla2DotParser
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
options no_result_var
|
10
|
+
|
11
|
+
rule
|
12
|
+
|
13
|
+
target : entries { @graph }
|
14
|
+
|
15
|
+
entries : entry
|
16
|
+
| entries entry
|
17
|
+
|
18
|
+
entry : state { @graph.add_node( val[0] ) }
|
19
|
+
| trans
|
20
|
+
|
21
|
+
|
22
|
+
trans : TRANS '-->' INTEGER { @graph.add_edge( val[0], val[2] ) }
|
23
|
+
|
24
|
+
|
25
|
+
# state : STATE variables { Node.new( val[0], val[1] ) }
|
26
|
+
# | STATE { Node.new( val[0], {} ) }
|
27
|
+
|
28
|
+
state : state_def variables { val[0].variables=val[1]; val[0] }
|
29
|
+
| state_def
|
30
|
+
|
31
|
+
state_def : state_id
|
32
|
+
| state_id actiondef
|
33
|
+
|
34
|
+
state_id : STATE { Node.new( val[0], {} ) }
|
35
|
+
|
36
|
+
|
37
|
+
actiondef : '<' actionspecs '>'
|
38
|
+
|
39
|
+
actionspecs : actionspec
|
40
|
+
| actionspecs actionspec
|
41
|
+
|
42
|
+
actionspec : IDENT
|
43
|
+
| ','
|
44
|
+
| INTEGER
|
45
|
+
|
46
|
+
variables : variable
|
47
|
+
| variables variable { k=val[1].keys.first; val[0][k] = val[1][k]; val[0] }
|
48
|
+
|
49
|
+
variable : AND name '=' value { { val[1] => val[3] } }
|
50
|
+
|
51
|
+
name : IDENT
|
52
|
+
|
53
|
+
value : INTEGER { val[0].to_i }
|
54
|
+
| STRING
|
55
|
+
| IDENT
|
56
|
+
| record
|
57
|
+
| set
|
58
|
+
| seq
|
59
|
+
|
60
|
+
record : '[' ']' { {} }
|
61
|
+
| '[' rlist ']' { val[1] }
|
62
|
+
|
63
|
+
rlist : ritem { val[0] }
|
64
|
+
| rlist ',' ritem { k=val[2].keys.first; val[0][k] = val[2][k]; val[0] }
|
65
|
+
|
66
|
+
ritem : name '|->' value { { val[0] => val[2] } }
|
67
|
+
|
68
|
+
|
69
|
+
seq : '<' '<' '>' '>' { [] }
|
70
|
+
| '<' '<' seqlist '>' '>' { val[2] }
|
71
|
+
|
72
|
+
seqlist : seqitem
|
73
|
+
| seqlist ',' seqitem { val[0].push( val[2][0] ); val[0] }
|
74
|
+
|
75
|
+
seqitem : value { [ val[0] ] }
|
76
|
+
|
77
|
+
|
78
|
+
set : '{' '}' { [] }
|
79
|
+
| '{' slist '}' { val[1] }
|
80
|
+
|
81
|
+
slist : sitem
|
82
|
+
| slist ',' sitem { val[0].push( val[2][0] ); val[0] }
|
83
|
+
|
84
|
+
sitem : value { [ val[0] ] }
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
---- inner
|
89
|
+
|
90
|
+
class Node
|
91
|
+
|
92
|
+
include TLA2DOT::Utils::MyLogger # mix logger
|
93
|
+
|
94
|
+
@@logger = nil; # common logger for all nodes
|
95
|
+
@@options = {}; # common logger for all nodes
|
96
|
+
|
97
|
+
|
98
|
+
# ------------------------------------------------------------------
|
99
|
+
# Attributes
|
100
|
+
|
101
|
+
# instance
|
102
|
+
attr_accessor :name #
|
103
|
+
attr_accessor :variables # hash of name=>
|
104
|
+
attr_accessor :edges # list of nodes reachable
|
105
|
+
|
106
|
+
# ------------------------------------------------------------------
|
107
|
+
# constructore
|
108
|
+
def initialize( name, variables )
|
109
|
+
@name = name
|
110
|
+
@variables = variables
|
111
|
+
@edges = []
|
112
|
+
@filter_render_variables = []
|
113
|
+
end
|
114
|
+
|
115
|
+
def myLogger
|
116
|
+
return @@logger if @@logger
|
117
|
+
@@logger = getLogger( "Node", getOptions )
|
118
|
+
@@logger.info( "#{__method__} created" )
|
119
|
+
@@logger
|
120
|
+
end
|
121
|
+
|
122
|
+
def setOptions( options )
|
123
|
+
@@options = options
|
124
|
+
end
|
125
|
+
|
126
|
+
def getOptions
|
127
|
+
@@options
|
128
|
+
end
|
129
|
+
|
130
|
+
def name
|
131
|
+
@name
|
132
|
+
end
|
133
|
+
|
134
|
+
def variables=( variables )
|
135
|
+
@variables = variables
|
136
|
+
end
|
137
|
+
|
138
|
+
def variables
|
139
|
+
@variables
|
140
|
+
end
|
141
|
+
|
142
|
+
def ==( node )
|
143
|
+
node.respond_to?( :name) && self.name == node.name
|
144
|
+
end
|
145
|
+
|
146
|
+
def add_edge( node )
|
147
|
+
@edges << node unless @edges.include?( node )
|
148
|
+
end
|
149
|
+
|
150
|
+
def successor_cnt
|
151
|
+
@edges.size
|
152
|
+
end
|
153
|
+
|
154
|
+
# array of successors for the node
|
155
|
+
def successors
|
156
|
+
@edges
|
157
|
+
end
|
158
|
+
|
159
|
+
# array tranitions (from_state->to_state) from this node to another
|
160
|
+
def transitions
|
161
|
+
myLogger.debug( "#{__method__} starting" )
|
162
|
+
successors.select{ |n| n.name != name }.map{ |n| { :to_state => n.name, :from_state => self.name } }
|
163
|
+
end
|
164
|
+
|
165
|
+
# array of variable names to render
|
166
|
+
def filter_render_variables
|
167
|
+
myLogger.debug( "#{__method__} filter_render_variables=#{@filter_render_variables} #{@filter_render_variables.class}" )
|
168
|
+
@filter_render_variables
|
169
|
+
end
|
170
|
+
|
171
|
+
# called when added to graph
|
172
|
+
def filter_render_variables=( variables )
|
173
|
+
|
174
|
+
@filter_render_variables = variables
|
175
|
+
end
|
176
|
+
|
177
|
+
# return true is state should show variable
|
178
|
+
def include_variable( variable )
|
179
|
+
|
180
|
+
return filter_render_variables.include?( variable ) if
|
181
|
+
filter_render_variables.kind_of?( Array )
|
182
|
+
|
183
|
+
# assume boolean
|
184
|
+
return filter_render_variables
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
# node variable to render as key/state hash (including 'id')
|
190
|
+
def content
|
191
|
+
myLogger.debug( "#{__method__} starting" )
|
192
|
+
vars = variables;
|
193
|
+
vars['ID'] = name
|
194
|
+
vars.select{ |k,v| include_variable( k ) }.
|
195
|
+
map { |k,v|
|
196
|
+
{:key =>k, :val => v, :state => render_value(v) }
|
197
|
+
}
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
# escape for mustache rendering
|
202
|
+
def render_value( v )
|
203
|
+
return v.to_s.
|
204
|
+
gsub( /\{/, "\\{" ).
|
205
|
+
gsub( /\}/, "\\}" ).
|
206
|
+
gsub( /=>/, "=" ).
|
207
|
+
# gsub( /\{/, "" ).
|
208
|
+
# gsub( /\}/, "" ).
|
209
|
+
# gsub( /\[/, "" ).
|
210
|
+
# gsub( /\]/, "" ).
|
211
|
+
# gsub( /\[/, "\\[" ).
|
212
|
+
# gsub( /\]/, "\\]" ).
|
213
|
+
gsub( /"/, '\\"' )
|
214
|
+
# case
|
215
|
+
# when v.kind_of?( Hash )
|
216
|
+
# return v.to_s.gsub( /\{/, "\\{" ).gsub( /\}/, "\\}" )
|
217
|
+
# else
|
218
|
+
# return v
|
219
|
+
# end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
class Graph
|
228
|
+
|
229
|
+
include TLA2DOT::Utils::MyLogger # mix logger
|
230
|
+
|
231
|
+
|
232
|
+
attr_reader :nodes #
|
233
|
+
|
234
|
+
def initialize( filter_render_variables, options )
|
235
|
+
@nodes = {}
|
236
|
+
# filter_render_variables passed to node
|
237
|
+
@filter_render_variables = filter_render_variables
|
238
|
+
setOptions( options )
|
239
|
+
end
|
240
|
+
|
241
|
+
def myLogger
|
242
|
+
return @logger if @logger
|
243
|
+
@logger = getLogger( "Graph", getOptions )
|
244
|
+
@logger.info( "#{__method__} created" )
|
245
|
+
@logger
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
def setOptions( options )
|
250
|
+
@options = options
|
251
|
+
end
|
252
|
+
|
253
|
+
def getOptions
|
254
|
+
@options
|
255
|
+
end
|
256
|
+
|
257
|
+
def add_node(node)
|
258
|
+
node.filter_render_variables=( @filter_render_variables )
|
259
|
+
node.setOptions( getOptions )
|
260
|
+
myLogger.debug( "#{__method__} added node #{node}, @filter_render_variables=#{@filter_render_variables} #{@filter_render_variables.class}" )
|
261
|
+
@nodes[node.name] = node
|
262
|
+
self
|
263
|
+
end
|
264
|
+
|
265
|
+
|
266
|
+
def nodes
|
267
|
+
@nodes
|
268
|
+
end
|
269
|
+
|
270
|
+
def states
|
271
|
+
@nodes.values
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
def node_cnt
|
276
|
+
@nodes.size
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
def add_edge(predecessor_name, successor_name)
|
281
|
+
|
282
|
+
predecessor_node = @nodes[predecessor_name]
|
283
|
+
raise "Unknown precessor node #{predecessor_name}" unless predecessor_node
|
284
|
+
successor_node = @nodes[successor_name]
|
285
|
+
raise "Unknown precessor node #{successor_name}" unless successor_node
|
286
|
+
@nodes[predecessor_name].add_edge(@nodes[successor_name])
|
287
|
+
end
|
288
|
+
|
289
|
+
def [](name)
|
290
|
+
@nodes[name]
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# ------------------------------------------------------------------
|
295
|
+
|
296
|
+
include TLA2DOT::Utils::MyLogger # mix logger
|
297
|
+
PROGNAME = "parser" # progname for logger
|
298
|
+
|
299
|
+
def initialize( options = {} )
|
300
|
+
@logger = getLogger( PROGNAME, options )
|
301
|
+
@logger.debug( "#{__method__} initialized" )
|
302
|
+
setOptions( options )
|
303
|
+
end
|
304
|
+
|
305
|
+
def setOptions( options )
|
306
|
+
@options = options
|
307
|
+
end
|
308
|
+
|
309
|
+
def getOptions
|
310
|
+
@options
|
311
|
+
end
|
312
|
+
|
313
|
+
|
314
|
+
|
315
|
+
# entry point
|
316
|
+
def parse(str, filter_render_variables = [] )
|
317
|
+
@logger.info( "#{__method__} parsing started" )
|
318
|
+
@graph = Graph.new( filter_render_variables, getOptions )
|
319
|
+
@line = 0
|
320
|
+
return @graph if str.nil? || str.empty?
|
321
|
+
@str = str.kind_of?( Array ) ? str : [ str ]
|
322
|
+
begin
|
323
|
+
ret = yyparse self, :scan
|
324
|
+
@logger.info( "#{__method__} parsing done" )
|
325
|
+
return ret
|
326
|
+
rescue Exception => e
|
327
|
+
puts "Error on line #{@line} near #{@current}"
|
328
|
+
@logger.error( "#{__method__} error #{e}" )
|
329
|
+
# puts e
|
330
|
+
return nil
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
private
|
335
|
+
|
336
|
+
# return next line for scanner
|
337
|
+
def next_str
|
338
|
+
@current = @str.any? ? @str.shift : nil
|
339
|
+
@line += 1
|
340
|
+
@current
|
341
|
+
end
|
342
|
+
|
343
|
+
|
344
|
+
# racc token scanner
|
345
|
+
def scan
|
346
|
+
while true
|
347
|
+
str = next_str
|
348
|
+
break if str.nil?
|
349
|
+
until str.empty?
|
350
|
+
case str
|
351
|
+
when /\A\s+/
|
352
|
+
str = $'
|
353
|
+
when /\AState (\d+):/
|
354
|
+
yield [ :STATE, $1 ]
|
355
|
+
str = $'
|
356
|
+
when /\AState (\d+)\/(-?\d+):/
|
357
|
+
# using fingerprint name
|
358
|
+
yield [ :STATE, $2 ]
|
359
|
+
str = $'
|
360
|
+
when /\AState (\d+):/
|
361
|
+
yield [ :STATE, $1 ]
|
362
|
+
str = $'
|
363
|
+
when /\ATransition ([0-9\-\+]+)/
|
364
|
+
yield [ :TRANS, $1 ]
|
365
|
+
str = $'
|
366
|
+
when /\A"([^"]*)"*/
|
367
|
+
yield [ :STRING, $1 ]
|
368
|
+
str = $'
|
369
|
+
when /\A[a-zA-Z]\w*/
|
370
|
+
yield [ :IDENT, $& ]
|
371
|
+
str = $'
|
372
|
+
when /\A-?\d+/
|
373
|
+
yield [ :INTEGER, $& ]
|
374
|
+
str = $'
|
375
|
+
when /\A\/\\/
|
376
|
+
yield [ :AND, $& ]
|
377
|
+
str = $'
|
378
|
+
when /\A\|->/
|
379
|
+
yield [ $&, $& ]
|
380
|
+
str = $'
|
381
|
+
# when /\A\n/
|
382
|
+
# puts "Nl"
|
383
|
+
# yield [ :NL, $& ]
|
384
|
+
# str = $'
|
385
|
+
when /\A\-->/
|
386
|
+
yield [ $&, $& ]
|
387
|
+
str = $'
|
388
|
+
when /\A=/
|
389
|
+
yield [ $&, $& ]
|
390
|
+
str = $'
|
391
|
+
else
|
392
|
+
c = str[0,1]
|
393
|
+
yield [ c, c ]
|
394
|
+
str = str[1..-1]
|
395
|
+
end
|
396
|
+
end # until
|
397
|
+
end # while
|
398
|
+
yield [ false, '$'] # is optional from Racc 1.3.7
|
399
|
+
end
|
400
|
+
|
401
|
+
---- footer
|
402
|
+
|
403
|
+
if $0 == __FILE__
|
404
|
+
src = <<EOS
|
405
|
+
{
|
406
|
+
name => MyName,
|
407
|
+
id => MyIdent
|
408
|
+
}
|
409
|
+
EOS
|
410
|
+
puts 'Parsing (String):'
|
411
|
+
print src
|
412
|
+
puts
|
413
|
+
puts 'Result (Ruby Object):'
|
414
|
+
p HashParser.new.parse(src)
|
415
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'mustache' # extendending implementation of
|
2
|
+
|
3
|
+
module TLA2DOT
|
4
|
+
|
5
|
+
class Template < Mustache
|
6
|
+
|
7
|
+
include TLA2DOT::Utils::MyLogger # mix logger
|
8
|
+
PROGNAME = "template" # progname for logger
|
9
|
+
|
10
|
+
# ------------------------------------------------------------------
|
11
|
+
# Attributes
|
12
|
+
|
13
|
+
# instance
|
14
|
+
attr_writer :partials # f: partial-name --> template string
|
15
|
+
attr_writer :templates # f: template-name --> template string
|
16
|
+
|
17
|
+
# ------------------------------------------------------------------
|
18
|
+
# Constructor
|
19
|
+
|
20
|
+
def initialize( options={} )
|
21
|
+
@logger = getLogger( PROGNAME, options )
|
22
|
+
@logger.info( "#{__method__} created" )
|
23
|
+
@logger.debug( "#{__method__}, options='#{options}" )
|
24
|
+
|
25
|
+
@template_extension = "mustache"# type part in filename
|
26
|
+
|
27
|
+
# for mustache templates
|
28
|
+
if options[:templates] then
|
29
|
+
@template_paths = options[:templates]
|
30
|
+
else
|
31
|
+
@template_paths = TLA2DOT::Cli::TEMPLATES
|
32
|
+
end
|
33
|
+
# init partial cache
|
34
|
+
@partials = {}
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
# ------------------------------------------------------------------
|
39
|
+
# Services
|
40
|
+
|
41
|
+
def to_str( template_name, data )
|
42
|
+
@logger.info( "#{__method__}: template_name=#{template_name}, data=#{data}, nodes.size=#{data.nodes.size}" )
|
43
|
+
# @logger.debug( "#{__method__}: nodes=#{data.nodes}" )
|
44
|
+
@data = data
|
45
|
+
template = get_template( template_name )
|
46
|
+
render( template, { :tsti=>"moikka", :data=>data, "tst" => "hello" } )
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
# def data
|
52
|
+
# @data
|
53
|
+
# end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
# ------------------------------------------------------------------
|
58
|
+
# Integrate with mustache
|
59
|
+
|
60
|
+
# method used by mustache framework - delegate to 'get_partial'
|
61
|
+
def partial(name)
|
62
|
+
@logger.debug( "#{__method__} name=#{name}" )
|
63
|
+
get_partial( name )
|
64
|
+
end
|
65
|
+
|
66
|
+
# cache @partials - for easier extension
|
67
|
+
def get_partial( name )
|
68
|
+
@logger.debug( "#{__method__} name=#{name}" )
|
69
|
+
return @partials[name] if @partials[name]
|
70
|
+
|
71
|
+
partial_file = get_template_filepath( name )
|
72
|
+
@logger.info( "#{__method__} read partial_file=#{partial_file}" )
|
73
|
+
@partials[name] = File.read( partial_file )
|
74
|
+
@partials[name]
|
75
|
+
end
|
76
|
+
|
77
|
+
# hide @templates - for easier extension
|
78
|
+
def get_template( name )
|
79
|
+
|
80
|
+
template_file = get_template_filepath( name )
|
81
|
+
@logger.info( "#{__method__} read template_file=#{template_file}" )
|
82
|
+
File.read( template_file )
|
83
|
+
|
84
|
+
end # def get_template( name )
|
85
|
+
|
86
|
+
|
87
|
+
# return path to an existing template file name
|
88
|
+
private
|
89
|
+
|
90
|
+
def get_template_filepath( name )
|
91
|
+
|
92
|
+
@template_paths.each do |directory_or_gemname|
|
93
|
+
|
94
|
+
template_path = get_template_filepath_resolve( directory_or_gemname, name )
|
95
|
+
|
96
|
+
return template_path if File.exists?( template_path )
|
97
|
+
|
98
|
+
end # each
|
99
|
+
|
100
|
+
|
101
|
+
# could not find
|
102
|
+
|
103
|
+
raise <<-eos
|
104
|
+
|
105
|
+
No such template '#{name}' found in directories: #{@template_paths.join(", ")}
|
106
|
+
|
107
|
+
Use opition -t list directories or Gems, where file '#{name}.#{@template_extension}' can be located.
|
108
|
+
|
109
|
+
eos
|
110
|
+
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
# return path to plain 'directory' or to gem directory
|
115
|
+
def get_template_filepath_resolve( directory_or_gemname, template_file )
|
116
|
+
@logger.info( "#{__method__} directory_or_gemname=#{directory_or_gemname}" )
|
117
|
+
if directory_or_gemname[-1] == '/' then
|
118
|
+
return get_template_filepath_in_directory( directory_or_gemname[0..-2], template_file )
|
119
|
+
else
|
120
|
+
directory = gemname_to_directory( directory_or_gemname )
|
121
|
+
return get_template_filepath_in_directory( directory, template_file )
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# return directory to 'gemspec'
|
126
|
+
def gemname_to_directory( gemname_and_spec )
|
127
|
+
|
128
|
+
# The version requirements are optional.
|
129
|
+
# You can also specify multiple version requirements, just append more at the end
|
130
|
+
gem_spec = gemname_and_spec.split(',')
|
131
|
+
gem_name, *gem_ver_reqs = gem_spec[0], gem_spec[1]
|
132
|
+
@logger.debug( "#{__method__}, gem_name=#{gem_name}, *gem_ver_reqs=#{gem_ver_reqs}" )
|
133
|
+
gdep = Gem::Dependency.new(gem_name, *gem_ver_reqs)
|
134
|
+
# find latest that satisifies
|
135
|
+
found_gspec = gdep.matching_specs.sort_by(&:version).last
|
136
|
+
@logger.debug( "#{__method__}, found_gspec=#{found_gspec}" )
|
137
|
+
# instead of using Gem::Dependency, you can also do:
|
138
|
+
# Gem::Specification.find_all_by_name(gem_name, *gem_ver_reqs)
|
139
|
+
|
140
|
+
if found_gspec
|
141
|
+
@logger.debug( "#{__method__}, Requirement '#{gdep}' already satisfied by #{found_gspec.name}-#{found_gspec.version}" )
|
142
|
+
template_path = "#{found_gspec.gem_dir}/mustache"
|
143
|
+
@logger.debug( "#{__method__}, template_path=#{template_path}" )
|
144
|
+
else
|
145
|
+
#puts "Requirement '#{gdep}' not satisfied; installing..."
|
146
|
+
# ver_args = gdep.requirements_list.map{|s| ['-v', s] }.flatten
|
147
|
+
# # multi-arg is safer, to avoid injection attacks
|
148
|
+
# system('gem', 'install', gem_name, *ver_args)
|
149
|
+
raise "Could not find gem '#{gdep}' - try 'gem install #{gdep}'"
|
150
|
+
end
|
151
|
+
|
152
|
+
return template_path
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
# return path to 'template_file' in an existing 'directory'
|
158
|
+
def get_template_filepath_in_directory( directory, template_file )
|
159
|
+
@logger.debug( "#{__method__} directory=#{directory}, template_file=#{template_file}" )
|
160
|
+
|
161
|
+
if ! File.exists?( directory ) then
|
162
|
+
raise <<-eos
|
163
|
+
|
164
|
+
No such directory '#{directory}'.
|
165
|
+
|
166
|
+
Option -t should list
|
167
|
+
- existing paths OR
|
168
|
+
- Gems which includes 'mustache' directory
|
169
|
+
|
170
|
+
eos
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
template_path = "#{directory}/#{template_file}.#{@template_extension}"
|
175
|
+
@logger.info( "#{__method__} read template_path=#{template_path}" )
|
176
|
+
|
177
|
+
return template_path
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
end # class
|
183
|
+
|
184
|
+
end # module
|
data/lib/tla2dot.rb
ADDED
data/lib/utils/logger.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
# see http://hawkins.io/2013/08/using-the-ruby-logger/
|
4
|
+
|
5
|
+
module TLA2DOT
|
6
|
+
|
7
|
+
module Utils
|
8
|
+
|
9
|
+
module MyLogger
|
10
|
+
|
11
|
+
# no logging done
|
12
|
+
|
13
|
+
class NullLoger < Logger
|
14
|
+
def initialize(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def add(*args, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
LOGFILE="tla2dot.log"
|
22
|
+
|
23
|
+
def getLogger( progname, options={} )
|
24
|
+
|
25
|
+
level = get_level( options )
|
26
|
+
|
27
|
+
if level.nil?
|
28
|
+
|
29
|
+
return NullLoger.new
|
30
|
+
|
31
|
+
else
|
32
|
+
|
33
|
+
logger = Logger.new( LOGFILE )
|
34
|
+
logger.level=level
|
35
|
+
logger.progname = progname
|
36
|
+
return logger
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end # getLogger
|
41
|
+
|
42
|
+
# ------------------------------------------------------------------
|
43
|
+
private
|
44
|
+
|
45
|
+
def get_level( options )
|
46
|
+
|
47
|
+
# puts "#{__method__}: options=#{options}"
|
48
|
+
|
49
|
+
level_name = options && options[:log] ? options[:log] : ENV['LOG_LEVEL']
|
50
|
+
|
51
|
+
level = case level_name
|
52
|
+
when 'warn', 'WARN'
|
53
|
+
Logger::WARN
|
54
|
+
when 'info', 'INFO'
|
55
|
+
Logger::INFO
|
56
|
+
when 'debug', 'DEBUG'
|
57
|
+
Logger::DEBUG
|
58
|
+
when 'error', 'ERROR'
|
59
|
+
Logger::ERROR
|
60
|
+
else
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
return level
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
fontname = "Bitstream Vera Sans";
|
2
|
+
fontsize = 8;
|
3
|
+
shape = "record";
|
4
|
+
labeljust="l";
|
5
|
+
rankdir=LR;
|
6
|
+
|
7
|
+
|
8
|
+
node [ fontname = "Courier"
|
9
|
+
fontsize = 8
|
10
|
+
shape = "record"
|
11
|
+
|
12
|
+
];
|
13
|
+
|
14
|
+
edge [
|
15
|
+
fontname = "Bitstream Vera Sans"
|
16
|
+
fontsize = 8
|
17
|
+
arrowhead = "none"
|
18
|
+
];
|
19
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
digraph G {
|
2
|
+
|
3
|
+
/* font size layout etc. */
|
4
|
+
{{> defaults }}
|
5
|
+
|
6
|
+
/* nodes */
|
7
|
+
{{# data }}
|
8
|
+
{{# states}}
|
9
|
+
{{name}} [{{> stateOutput }}];
|
10
|
+
{{/ states}}
|
11
|
+
{{/ data }}
|
12
|
+
|
13
|
+
/* arcs */
|
14
|
+
{{# data }}
|
15
|
+
{{# states}}{{# transitions }} {{ from_state }} -> {{to_state}};
|
16
|
+
{{/ transitions }}{{/ states}}
|
17
|
+
{{/ data }}
|
18
|
+
|
19
|
+
|
20
|
+
}
|