sbuilder-al 0.0.8

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.
@@ -0,0 +1,159 @@
1
+ # ------------------------------------------------------------------
2
+ # Hack mustache to allow change default otag '{{' ctag '}}'
3
+ require 'mustache'
4
+
5
+ class Mustache
6
+
7
+
8
+ # Open mustache to define class accessors 'otag' and 'ctag'
9
+ def self.otag=( o )
10
+ @@otag = o
11
+ end
12
+ def self.ctag=( c )
13
+ @@ctag = c
14
+ end
15
+ def self.otag
16
+ @@otag ||= '{{'
17
+ end
18
+ def self.ctag
19
+ @@ctag ||= '}}'
20
+ end
21
+
22
+
23
+ class Template
24
+
25
+ # Open template to set otag/ctag on parser.
26
+ #
27
+ # Returns an array of tokens for a given template.
28
+ #
29
+ # @return [Array] Array of tokens.
30
+ #
31
+ def tokens(src = @source)
32
+ p = Parser.new
33
+ p.otag = Mustache.otag
34
+ p.ctag = Mustache.ctag
35
+ p.compile(src)
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ module Sbuilder
42
+ module Al
43
+ module Translator
44
+
45
+ # not catched
46
+ class RendererException < Sbuilder::Al::Util::AlException; end
47
+ # to be catched
48
+ class RendererError < Sbuilder::Al::Util::AlError; end
49
+
50
+
51
+ class Renderer < Mustache
52
+
53
+ # @attr [Logger] loger
54
+ attr_reader :logger
55
+ PROGNAME = nil # progname for logger default class name
56
+ include Sbuilder::Al::Util::MyLogger # mix logger
57
+
58
+ # @!attribute partials [Hash] must be set before +to_str+, see
59
+ # {#partials} for reader
60
+ attr_writer :partials
61
+
62
+ # @!attribute preferences modify mustache rendering (debug?)
63
+ attr_reader :preferences
64
+
65
+ # @!attribute partialsRef [Object] object with partials method
66
+ attr_accessor :partialsRef
67
+
68
+ def initialize( options={} )
69
+ # exception raise if accessing unknown property
70
+ self.raise_on_context_miss = true
71
+
72
+ @options = options
73
+ @logger = getLogger( nil, options )
74
+ @logger.info "#{__method__}: starting options=#{options}"
75
+
76
+ self.partials = {}
77
+
78
+ end
79
+
80
+ def self.start( partialsRef )
81
+ renderer = Renderer.new
82
+ raise RendererException, "Expect partialsRef #{partialsRef} to respond to :partials" unless partialsRef.respond_to?( :partials )
83
+ renderer.partialsRef= partialsRef
84
+ renderer
85
+ end
86
+
87
+ def setPreferences( preferences )
88
+ @preferences = preferences
89
+ @logger.info "#{__method__}: preferences=#{preferences}"
90
+ end
91
+
92
+ # Use 'to_str' instead of 'render' to use non-default otag/ctag's
93
+ def to_str( template, data=nil )
94
+ Mustache.otag = '{|'
95
+ Mustache.ctag = '|}'
96
+
97
+ begin
98
+ ret = render( template, data )
99
+ ensure
100
+ # restore default otag/ctag
101
+ Mustache.otag = '{{'
102
+ Mustache.ctag = '}}'
103
+ end
104
+ ret
105
+ end
106
+
107
+ # partials with key for partial name, and value
108
+ # a partial template to render.
109
+ #
110
+ # @return [Hash] @partialsRef.partials if @partialsRef given, else @partials
111
+
112
+ def partials
113
+ @partialsRef.nil? ? @partials : @partialsRef.partials
114
+ end
115
+
116
+ # Render conxtext.current[key].
117
+
118
+ # @param [String, '.'] key to render, '.' means current context,
119
+ # string lookup key in current context
120
+ def EVAL( key )
121
+
122
+ logger.info "#{__method__}: key='#{key}', context.current=#{context.current}" if logger.debug?
123
+
124
+ # find generate sexp( :sym, data ) to render
125
+ if key == '.'
126
+ sexp = context.current
127
+ else
128
+ # to evaluate
129
+ hash = context.current.is_a?( Array ) ? context.current[1] : context.current
130
+ sexp = hash[key] || hash[key.to_sym]
131
+ raise RendererException, "No such key #{key}:#{key.class} found in #{hash}" if sexp.nil?
132
+ end
133
+
134
+ sexp_type = sexp[0]
135
+ data = sexp[1]
136
+ logger.debug "#{__method__}: key=#{key}, sexp_type=#{sexp_type}" if logger.debug?
137
+
138
+
139
+ # find correct template
140
+ template = partials[sexp_type]
141
+ raise RendererException,
142
+ <<-EOS if (template.nil?)
143
+ Unknown partial for sexp_type '#{sexp_type}'
144
+
145
+ Known partials: #{partials.keys.join(',')}"
146
+ Conxt = #{context.current}
147
+ EOS
148
+
149
+ logger.debug "#{__method__}: template=#{template}, data=#{data}" if logger.debug?
150
+ return render( template, data )
151
+
152
+ end
153
+ end # class Renderer < Mustache
154
+
155
+
156
+ end
157
+ end
158
+ end
159
+
@@ -0,0 +1,57 @@
1
+ module Sbuilder
2
+ module Al
3
+ module Translator
4
+ class Sexp < Array
5
+
6
+ def initialize(*args)
7
+ super(args)
8
+ end
9
+
10
+ ##
11
+ # Returns the node type of the Sexp.
12
+
13
+ def sexp_type
14
+ first
15
+ end
16
+
17
+ ##
18
+ # Returns the Sexp body, ie the values without the node type.
19
+
20
+ def sexp_body
21
+ self[1..-1]
22
+ end
23
+
24
+ # @return [Sexp|Nil] sexp_body.first for corresponding 'prop'
25
+ def sexp_property( prop )
26
+ sexp_element = sexp_body && sexp_body.select{ |e| e.is_a?(Sexp) && e.sexp_type == prop }.first
27
+ sexp_element.sexp_body.first unless sexp_element.nil?
28
+ end
29
+
30
+ # @return [Sexp:Array] sexp_body for corresponding 'prop'
31
+ def sexp_properties( prop )
32
+ sexp_element = sexp_body && sexp_body.select{ |e| e.is_a?(Sexp) && e.sexp_type == prop }
33
+ # [[:sexp, [:sexp1, ...], [:sexp1, ...]]] --> [:sexp, [:sexp1, ...], [:sexp1, ...]]
34
+ sexp_element.flatten(1)[1..-1]
35
+ end
36
+
37
+
38
+ # alias :head :sexp_type
39
+ # alias :rest :sexp_body
40
+
41
+
42
+ end # class Sexp
43
+
44
+
45
+ # Important shortcut
46
+ def s(*args)
47
+ Sexp.new(*args )
48
+ end
49
+ module_function :s
50
+ public :s
51
+
52
+ end # module Translator
53
+
54
+ end
55
+ end
56
+
57
+
@@ -0,0 +1,1348 @@
1
+
2
+ require 'mustache'
3
+
4
+
5
+ module Sbuilder
6
+ module Al
7
+ module Translator
8
+
9
+
10
+ ##
11
+ # Create tla sexps, which can be rendered using
12
+ # {Sbuilder::Al::Translator::Renderer}
13
+ #
14
+ # To be able procude valid tla-language snippets tla sexps
15
+ # created by class have to consider:
16
+ #
17
+ # 1) strings for tla-language constructs:
18
+ # {Sbuilder::Al::Translator::Renderer} Mustache template
19
+ # generator uses tag separator {| }} and implements #EVAL
20
+ #
21
+ # 2) need to add statement labels in tla proc language: 2.1)
22
+ # flag proc language statement requiring labels (see
23
+ # {Sbuilder::Al::Translator::TlaGenerator#tlaStatementTypeRequiresLabel}
24
+ # 2.2) proc block statements implement recursion to check for
25
+ # labels
26
+ # {Sbuilder::Al::Translator::TlaGenerator#setLabelBlockStatements}
27
+ #
28
+ # 3) avoid name collisions in final tla language result:
29
+ # tla-sbuilder facade mustache template generator uses {{ }}
30
+ # tags and implements #SPEC_NAME
31
+ #
32
+ # 4) performance: manages tla -snippets in a
33
+ # {Sbuilder::Al::Translator::TlaGenerator#partials} hash,
34
+ # which contains compiled templates Mustache.templateify
35
+ #
36
+ #
37
+
38
+ class TlaGenerator
39
+
40
+ # @attr [Hash] partials intialized with #setPartials, cumulated
41
+ # using #addPartial
42
+ attr_reader :partials
43
+
44
+ # @attr [Hash] partials_duplicateCheck, used only as guarantee
45
+ # againts redefinition
46
+ attr_reader :partials_duplicateCheck
47
+
48
+ # # @attr [Hash] validMetatypes raise exception for unknwon mentateyps
49
+ # attr_reader :validMetatypes
50
+
51
+ # @attr [Hash] labeledStmts
52
+ #
53
+ # @option labeledStmts [:Symbol] <sTlaType> generate template
54
+ # map to true is label should be followed
55
+ attr_reader :labeledStmts
56
+
57
+ # text to prefix trace print
58
+ TRACE_PREFIX="TRACE>"
59
+
60
+ # Constants
61
+ TLA_LABEL_ABORT = "_abort"
62
+ TLA_LABEL_END = "_end"
63
+ TLA_LABEL_FAIL = "_fail"
64
+
65
+
66
+
67
+ # ------------------------------------------------------------------
68
+ # @!group Construct & configure
69
+
70
+ def initialize( options = {} )
71
+ @options = options
72
+ # @logger = getLogger( nil, options )
73
+ # @logger.info "#{__method__}: initalized"
74
+
75
+ # initially no known template requiring labe
76
+ @labeledStmts = {}
77
+
78
+ end
79
+
80
+ # Init w. application context
81
+ def self.start( partials = {} )
82
+ tlaGenerator = TlaGenerator.new
83
+ tlaGenerator.setPartials( partials )
84
+ # tlaGenerator.setValidMetatypes( METATYPES )
85
+ tlaGenerator
86
+ end
87
+
88
+
89
+ # # @return [Hash] defaultPartials for mustache templates
90
+ # def self.defaultPartials
91
+ # {
92
+ # :Snippet => <<-EOS
93
+ # (* {|metatype|}:{|appName|}{|# comment |} - {|{ comment }|}{|/ comment |} *)
94
+ # {|#snippet|}{|#EVAL|}.{|/EVAL|}{|/snippet|}
95
+ # EOS
96
+ # }
97
+ # end
98
+
99
+ def setPartials( partials )
100
+ @partials = partials
101
+ @partials_duplicateCheck = {}
102
+ end
103
+
104
+ # def setValidMetatypes( validMetatypes )
105
+ # @validMetatypes = validMetatypes
106
+ # end
107
+
108
+
109
+ # @!endgroup
110
+
111
+
112
+ # ------------------------------------------------------------------
113
+ # @!group Utitilities to create generate elements
114
+
115
+ # create s() with +sym+ (to dispatch correct template) and +hash+ as
116
+ # template data to render.
117
+ def stla( sym, hash)
118
+ sTla = Sbuilder::Al::Translator.s( sym, hash )
119
+ end
120
+
121
+ def s(*args)
122
+ Sbuilder::Al::Translator::Sexp.new(*args )
123
+ end
124
+
125
+ ###
126
+ # Add +partialTemplate+ with name +partialSymbol+ to partial
127
+ # collection.
128
+ #
129
+ # NOTICE: as an optimization Mustache template implementation
130
+ # can use partials, which are initalized using
131
+ # 'Mustache.templateify' to speed up rendering phase.
132
+ #
133
+ # @param [Symbol] partialSymbol identifying partial
134
+ #
135
+ # @param [String] partialTemplate to render element SEXP
136
+ #
137
+ #
138
+ def addPartial( partialSymbol, partialTemplate )
139
+ raise "Should have called #setPartials before actions" if partials.nil?
140
+ # would like to guarantee that no template refinition takes
141
+ # place - currently assume that implementation is correct and
142
+ # just return
143
+
144
+ return if partials.key?(partialSymbol)
145
+ raise <<~EOS if partials_duplicateCheck.key?(partialSymbol) && partials_duplicateCheckpartials[partialSymbol] != partialTemplate
146
+ Partial #{partialSymbol} redefinitition:
147
+
148
+ Previous:
149
+ #{partials[partialSymbol]}
150
+
151
+ New:
152
+ #{partialTemplate}
153
+
154
+ EOS
155
+
156
+ partials[partialSymbol] = Mustache.templateify(partialTemplate)
157
+ partials_duplicateCheck[partialSymbol] = partialTemplate
158
+ end
159
+
160
+ # @!endgroup
161
+
162
+
163
+ # ------------------------------------------------------------------
164
+ # @!group Statement labeling
165
+
166
+ ##
167
+ # Configure +sTlaType+ in +labeledStmts+ to require a label
168
+ #
169
+ # @see requiresLabelAfter
170
+ def tlaStatementTypeRequiresLabel( sTlaType )
171
+ labeledStmts[sTlaType] = true
172
+ end
173
+
174
+ ##
175
+ # Flag +stmt+ to include label if +hiddenStmt+ requires label.
176
+ #
177
+ # @see requiresLabelAfter
178
+ def statementRequiresLabel( stmt, labedStatement=nil )
179
+ stmt.define_singleton_method( :labelFollowingStatement ) do |inBlock|
180
+ return true if labedStatement.nil?
181
+ inBlock ? true : labedStatement
182
+ end
183
+ end
184
+
185
+ ##
186
+ # Statement 'stmt' contains a block with 'stmts', which should
187
+ # also be labeled. Later, use :labelBlockStatements to return
188
+ # 'stmts' to label.
189
+ def setLabelBlockStatements( stmt, stmts, methodName = :labelBlockStatements )
190
+ stmt.define_singleton_method( methodName ) do
191
+ stmts
192
+ end
193
+ end
194
+
195
+
196
+ ##
197
+ # Check whether +stmt+ with +stmt.sexp_type+ should be followed
198
+ # by a labeled statement.
199
+ #
200
+ # @param [SexpTla] stmt sexp which may respond to ':labelFollowingStatement'
201
+ #
202
+ # @return [Boolean] true is +sTlaType+ is found in
203
+ # +labeledStmts+ hash OR stmt.labelFollowingStatement
204
+ def requiresLabelAfter( stmt, inBlock=false )
205
+ return false unless stmt
206
+ # if s( s(:sym, ..), s(:sym,)) vs. s(:sym, ..)
207
+ sTlaType = stmt.any? && !stmt[0].is_a?(Symbol) ? stmt.last.sexp_type : stmt.sexp_type
208
+
209
+ # static
210
+ if labeledStmts[sTlaType] ||
211
+ ( stmt.respond_to?(:labelFollowingStatement) && stmt.labelFollowingStatement(inBlock))
212
+ return true
213
+ end
214
+ # dynamic recursion to block
215
+ if stmt.respond_to?(:requiresLabelAfterChecker)
216
+ stmt.requiresLabelAfterChecker.each do |st|
217
+ return true if requiresLabelAfter( st, true )
218
+ end
219
+ end
220
+ return false
221
+ end
222
+
223
+ def gen_serviceProcedureLabelStatements( stmts, labelPrefix )
224
+ stmts = stmts[1][:stmts] if stmts.is_a?(Array) && stmts[0] == :TLA_stmts
225
+ stmts, index = serviceProcedureLabelStatementsWorker( stmts, labelPrefix, 0, 0, true, [], [] )
226
+ stmts
227
+ end
228
+
229
+ ##
230
+ # Should add label 1) if block statements requires label after
231
+ # the statements 2) label between assignments to a same
232
+ # variable 3) call same macro
233
+ #
234
+ # @return [Array, index] stmts as passed as input, index incremented
235
+ ##
236
+ private def serviceProcedureLabelStatementsWorker( stmts, labelPrefix, index, level, labelFirst, assignments, macrocalls )
237
+ # assignments = []
238
+ stmts.each_with_index do |stmt,i|
239
+
240
+ # prev stamement in current block
241
+ prevStmt = stmts[i-1] if i > 0
242
+ # puts "#{' ' * level } - #{level}: #{labelPrefix[1][:specname][:appName]}-#{index} #{stmts[i][0]}"
243
+ if labelFirst || ( !prevStmt.nil? && requiresLabelAfter( prevStmt ) ) ||
244
+ stmt.respond_to?( :labelCurrentStatement ) && stmt.labelCurrentStatement ||
245
+ ( stmt[0] == :TLA_assign ) && assignments.include?( getLvalVariableName(stmt[1][:lval]) ) ||
246
+ ( stmt[0] == :TLA_macro_call ) && macrocalls.include?( getMacroName(stmt[1][:macroName]) )
247
+ stmts[i] = gen_labeled_stmt( stmt, labelPrefix, index )
248
+ # puts "#{' ' * level } - #{level}: --> label #{labelPrefix[1][:specname][:appName]}-#{index} #{stmt[0]}"
249
+ index += 1
250
+ # restart collection
251
+ assignments = []
252
+ macrocalls = []
253
+ end
254
+ # collect assingments
255
+ assignments << getLvalVariableName(stmt[1][:lval]) if stmt[0] == :TLA_assign
256
+ macrocalls << getMacroName(stmt[1][:macroName]) if stmt[0] == :TLA_macro_call
257
+
258
+ labelFirst = false
259
+
260
+ # then & while>?
261
+ if stmt.respond_to?( :labelBlockStatements )
262
+ # recursion
263
+ s, index = serviceProcedureLabelStatementsWorker( stmt.labelBlockStatements, labelPrefix, index, level+1, false, assignments, macrocalls )
264
+ end
265
+
266
+ # else block
267
+ if stmt.respond_to?( :labelBlockStatementsElse )
268
+ # recursion
269
+ s, index = serviceProcedureLabelStatementsWorker( stmt.labelBlockStatementsElse, labelPrefix, index, level+1, false, assignments, macrocalls )
270
+ end
271
+
272
+
273
+ end
274
+ return stmts, index
275
+ end
276
+
277
+ # @param [:TLA_plainname, :TLA_specname] tla from which to exect macro name
278
+ def getMacroName( macroTla )
279
+ getLvalVariableName(macroTla )
280
+ end
281
+
282
+ # @param [TLA_plainname] lval from which to extract variable name
283
+ def getLvalVariableName( lval )
284
+ case lval[0]
285
+ when :TLA_plainname
286
+ lval[1][:name]
287
+ when :TLA_specname
288
+ lval[1][:specname][:appName]
289
+ else
290
+ raise "Unknown lval type '#{lval[0]}' in #{lval}"
291
+ end
292
+ end
293
+
294
+
295
+
296
+ # @!endgroup
297
+
298
+ # ------------------------------------------------------------------
299
+ # @!group Create TLA specific generate element SEXPs
300
+
301
+ def gen_label_it( tla )
302
+ sym = :TLA_label_it
303
+ tlaStatementTypeRequiresLabel( sym )
304
+ addPartial( sym, "{|#EVAL|}tla{|/EVAL|}" )
305
+ stla( sym, { :tla => tla } )
306
+ end
307
+
308
+ # @param name [String,tlaSexp] name of macro to call
309
+ def gen_macro( name, parameters, stmts )
310
+
311
+ sym = :TLA_macro
312
+ addPartial(
313
+ sym,
314
+ "macro {|#EVAL|}name{|/EVAL|}( {|#parameters|}{|#EVAL|}parameter{|/EVAL|}{|_comma|}{|/parameters|} ) {\n"\
315
+ "{|#EVAL|}stmts{|/EVAL|}"\
316
+ "}"
317
+ )
318
+
319
+ # allow singleton parameter
320
+ parameters = parameters.is_a?( Array ) ? parameters : [parameters]
321
+
322
+ stla( sym, {
323
+ :name => name.is_a?( String ) ? gen_plainname(name) : name,
324
+ :parameters => parameters.any? ?
325
+ parameters.map.with_index do |p,i|
326
+ { :_comma => (i+1) == parameters.length ? '' : ',',
327
+ :parameter => p.is_a?(String) ? gen_plainname( p ): p,
328
+ }
329
+ end :
330
+ false,
331
+ :stmts => stmts,
332
+ })
333
+ end
334
+
335
+ def gen_service_procedure( serviceName, interfaceName, localVariables, stmts=gen_stmts(s()) )
336
+
337
+ sym = :TLA_Procedure
338
+ addPartial(
339
+ sym,
340
+ <<~EOS
341
+ procedure {|#EVAL|}name{|/EVAL|}( {|#EVAL|}name{|/EVAL|}_input )
342
+ {|#locals|}variable {|/locals|}{|#localVariables|} {|#EVAL|}variable{|/EVAL|}{|_comma|}{|/localVariables|}{|#locals|};
343
+ {|/locals|}{|!
344
+ Start procudedure block
345
+ |}{
346
+ {|#EVAL|}name{|/EVAL|}_start:
347
+ {|#EVAL|}start{|/EVAL|};
348
+
349
+ (* Body of {|#EVAL|}interfaceName{|/EVAL|} *)
350
+ {|#EVAL|}stmts{|/EVAL|}
351
+
352
+ {|#EVAL|}name{|/EVAL|}_exit:
353
+ goto {|#EVAL|}name{|/EVAL|}#{TLA_LABEL_END};
354
+ {|#EVAL|}name{|/EVAL|}#{TLA_LABEL_FAIL}:
355
+ \\* throw command sends here
356
+ InfrastructureServiceReturn( "{|#EVAL|}interfaceName{|/EVAL|}", FALSE, Nil);
357
+ goto {|#EVAL|}name{|/EVAL|}#{TLA_LABEL_END};
358
+
359
+ {|#EVAL|}name{|/EVAL|}#{TLA_LABEL_ABORT}:
360
+ \\* should not happen??
361
+ print <<"ABORT {|#EVAL|}name{|/EVAL|}">>;
362
+ InfrastructureServiceReturn( "{|#EVAL|}interfaceName{|/EVAL|}", Abort, Nil);
363
+ \\* schedule_throw( "{|#EVAL|}name{|/EVAL|}_exit" );
364
+ {|#EVAL|}name{|/EVAL|}#{TLA_LABEL_END}:
365
+ skip;
366
+ (* ethereum_service_pop( "{|#EVAL|}interfaceName{|/EVAL|}" ); *)
367
+ {|#EVAL|}return{|/EVAL|};
368
+ }
369
+ EOS
370
+ )
371
+
372
+ name = serviceName.is_a?( String) ? gen_plainname(serviceName) : serviceName
373
+
374
+ stla( sym, {
375
+ :name => name,
376
+ :locals => localVariables && localVariables.length > 0 ? true : false,
377
+ :localVariables => localVariables.map.with_index do |v,i|
378
+ {
379
+ :variable => v.is_a?(String) ? gen_plainname(v) : v,
380
+ :_comma => (i+1) == localVariables.length ? '' : ',',
381
+ }
382
+ end,
383
+ :interfaceName => interfaceName.is_a?( String) ? gen_plainname(interfaceName) : interfaceName,
384
+ :start => gen_traced_stmt(
385
+ gen_skip,
386
+ s( "ENTRY:" ), # + entry_exit,
387
+ ),
388
+ :stmts => stmts,
389
+ :return =>
390
+ gen_traced_stmt(
391
+ gen_return,
392
+ s( "EXIT:", ), # + entry_exit,
393
+ ),
394
+ })
395
+ end
396
+
397
+
398
+
399
+ ##
400
+ # @return +specName+
401
+ def gen_expression2( expr1, op, expr2 )
402
+ sym = :TLA_expression2
403
+ addPartial( sym, "{|#EVAL|}expr1{|/EVAL|} {|#EVAL|}op{|/EVAL|} {|#EVAL|}expr2{|/EVAL|}" )
404
+ stla( sym, {
405
+ :expr1 => expr1,
406
+ :op => op,
407
+ :expr2 => expr2,
408
+ })
409
+ end
410
+
411
+ ##
412
+ # @return [ f1 |-> type1, f2 |->type2 ]
413
+ def gen_record_definition( arrOfFields )
414
+
415
+ sym = :TLA_record_definition
416
+ addPartial( sym, "[ {|#fields|}{|#EVAL|}val{|/EVAL|}{|_comma|} {|/fields|}]" )
417
+
418
+ # simplify if just one given
419
+ arrOfFields = arrOfFields[0].is_a?( Symbol ) ? s(arrOfFields) : arrOfFields
420
+
421
+ # last element no comma
422
+ stla( sym,
423
+ {
424
+ :fields => arrOfFields.map.with_index { |f,i|
425
+ { :_comma => (i+1) == arrOfFields.length ? '' : ',', :val => f }
426
+ }
427
+ })
428
+
429
+ end
430
+
431
+
432
+ ##
433
+ # +variable+ \in +set+
434
+ def gen_set_iterate( variable, set )
435
+
436
+ sym = :TLA_set_iterate
437
+ addPartial( sym, "{|#EVAL|}variable{|/EVAL|} \\in {|#EVAL|}set{|/EVAL|} " )
438
+
439
+ # last element no comma
440
+ stla( sym,
441
+ {
442
+ :variable => variable.is_a?( String) ? gen_plainname(variable) : variable,
443
+ :set => set.is_a?( String) ? gen_plainname(set) : set,
444
+ })
445
+ end
446
+
447
+ ##
448
+ # { map : S}
449
+ def gen_set_map( map, set )
450
+
451
+ sym = :TLA_set_map
452
+ addPartial( sym, "{ {|#EVAL|}map{|/EVAL|} : {|#EVAL|}set{|/EVAL|} } " )
453
+
454
+ # last element no comma
455
+ stla( sym,
456
+ {
457
+ :map => map.is_a?( String) ? gen_plainname(map) : map,
458
+ :set => set.is_a?( String) ? gen_plainname(set) : set,
459
+ })
460
+ end
461
+
462
+
463
+
464
+
465
+ # # @return [ idName |-> idVal, f2 |-> Nil, ... ]
466
+ def gen_new_record( idVal, arrOfFields )
467
+
468
+ sym = :TLA_new_record
469
+ addPartial(
470
+ sym,
471
+ <<~EOS
472
+ [ {|#EVAL|}idVal{|/EVAL|}, {|#fields|}{|#EVAL|}val{|/EVAL|}{|_comma|} {|/fields|} ]
473
+ EOS
474
+ )
475
+
476
+ # last element no comma
477
+ stla( sym,
478
+ {
479
+ :idVal => idVal,
480
+ :fields => arrOfFields.map.with_index do |f,i|
481
+ { :_comma => (i+1) == arrOfFields.length ? '' : ',',
482
+ :val => f
483
+ }
484
+ end
485
+ })
486
+
487
+
488
+ end
489
+
490
+ def gen_record_field_definition( name, value )
491
+ # name |-> value (notice spaces)
492
+ gen_name_op_operand( name, '|->', value )
493
+ end
494
+ def gen_record_field( record, field )
495
+ # A.x
496
+ gen_name_op_operand( record, '.', field, "" )
497
+ end
498
+ def gen_record_named_field( record, field )
499
+ # A[x]
500
+ sym = :TLA_record_named_field
501
+
502
+ addPartial( sym, "{|#EVAL|}record{|/EVAL|}{|sepl|}{|#EVAL|}field{|/EVAL|}{|sepr|}" )
503
+
504
+ stla( sym,
505
+ {
506
+ :record => record.is_a?( String ) ? gen_plainname(record) : record,
507
+ :sepl => '[',
508
+ :sepr => ']',
509
+ :field => field.is_a?( String ) ? gen_plainname(field) : field,
510
+ })
511
+ end
512
+
513
+
514
+
515
+ def gen_name_op_operand( name, op, operand, sep=" " )
516
+
517
+ sym = :TLA_name_op_operand
518
+ addPartial( sym, "{|#EVAL|}name{|/EVAL|}{|sep|}{|{op}|}{|sep|}{|#EVAL|}operand{|/EVAL|}" )
519
+
520
+ stla( sym,
521
+ {
522
+ :name => name.is_a?( String ) ? gen_plainname(name) : name,
523
+ :op => op,
524
+ :sep => sep,
525
+ :operand => operand.is_a?( String ) ? gen_plainname(operand) : operand,
526
+ })
527
+
528
+ end
529
+
530
+ ##
531
+ # Map interface to service name e.g./customer(get), Demo()
532
+ def gen_service_name( interface )
533
+ gen_identifier( interface )
534
+ end
535
+
536
+ def gen_identifier( identifier, posfix="" )
537
+
538
+ sym = :TLA_identifier
539
+ addPartial( sym, "{|#EVAL|}identifier{|/EVAL|}{|#EVAL|}postfix{|/EVAL|}" )
540
+
541
+ stla( sym, {
542
+ :identifier => identifier.is_a?( String) ?
543
+ gen_plainname(identifier) :
544
+ identifier,
545
+ :postfix => posfix.is_a?( String) ? gen_plainname(posfix) : posfix,
546
+ })
547
+ end
548
+
549
+
550
+ ##
551
+ # @return Nil
552
+ def gen_empty_set()
553
+ sym = :TLA_emptyset
554
+
555
+ addPartial( sym, "{}" )
556
+
557
+ stla(sym, {
558
+ })
559
+ end
560
+
561
+ def gen_list( elements )
562
+ sym = :TLA_list
563
+ addPartial( sym, "{|#elements|}{|#EVAL|}element{|/EVAL|}{|_comma|}{|/elements|}" )
564
+
565
+ # allow singleton parameter
566
+
567
+ elements = elements.is_a?( String) ? gen_plainname(elements) : elements
568
+ elements = elements[0].is_a?( Symbol ) ? s(elements) : elements
569
+
570
+ stla( sym,
571
+ {
572
+ :elements => elements.map.with_index { |f,i|
573
+ { :_comma => (i+1) == elements.length ? '' : ',', :element => f }
574
+ }
575
+ })
576
+
577
+
578
+ end
579
+
580
+ def gen_set( element=nil )
581
+ element = s() if element.nil?
582
+ element = gen_list( element )
583
+
584
+ sym = :TLA_set
585
+ addPartial( sym, "{ {|#EVAL|}element{|/EVAL|} }" )
586
+
587
+ stla(sym, {
588
+ :element => element
589
+ })
590
+ end
591
+
592
+ def gen_sequence( element=nil )
593
+ element = s() if element.nil?
594
+ element = gen_list( element )
595
+
596
+ sym = :TLA_sequence
597
+ addPartial( sym, "<< {|#EVAL|}element{|/EVAL|} >>" )
598
+
599
+ stla(sym, {
600
+ :element => element
601
+ })
602
+
603
+
604
+ # elements = elements.is_a?( String) ? gen_plainname(elements) : elements
605
+ # elements = elements[0].is_a?( Symbol ) ? s(elements) : elements
606
+ # stla(sym, {
607
+ # :elements => elements.any? ?
608
+ # elements.map.with_index do |p,i|
609
+ # { :_comma => (i+1) == elements.length ? '' : ',',
610
+ # :element => p.is_a?(String) ? gen_plainname( p ): p,
611
+ # }
612
+ # end :
613
+ # false,
614
+ # })
615
+ end
616
+
617
+ def gen_TRUE
618
+ gen_plainname( "TRUE" )
619
+ end
620
+
621
+ def gen_FALSE
622
+ gen_plainname( "FALSE" )
623
+ end
624
+
625
+ ##
626
+ # @return Nil
627
+ def gen_nilElement( args )
628
+ sym = :TLA_nil_element
629
+
630
+ addPartial( sym, "Nil" )
631
+
632
+ stla( sym, {})
633
+ end
634
+
635
+
636
+ def gen_op_set_minus()
637
+ gen_op_operator( "\\" )
638
+ end
639
+
640
+ ##
641
+ # Common behavior to output operator +op+
642
+ def gen_op_operator( op )
643
+ sym = :TLA_op_operator
644
+
645
+ addPartial( sym, "{|operator|}" )
646
+
647
+ stla( sym, {
648
+ :operator => op
649
+ })
650
+
651
+ end
652
+
653
+ ##
654
+ # Define state variable +variable+ and initialize
655
+ # with +initExpr+
656
+ def gen_state_variable( variable, initExpr )
657
+ sym = :TLA_state_variable
658
+ addPartial( sym, "{|#EVAL|}variable{|/EVAL|} = {|#EVAL|}init_expression{|/EVAL|};" )
659
+
660
+ stla( sym, {
661
+ :variable => variable,
662
+ :init_expression => initExpr,
663
+ })
664
+ end
665
+
666
+ ##
667
+ # @param [String] interfaceName e.g. 'Demo()' or 'Demo(set)'
668
+ #
669
+ # @return [String] input variable name
670
+ def gen_interface_input_variable_name( interfaceName, metatype=:solidity_contract_function )
671
+ gen_identifier( gen_specname( metatype, interfaceName), "_input" )
672
+ end
673
+
674
+ ##
675
+ # @param [String] interfaceName e.g. 'Demo()' or 'Demo(set)'
676
+ #
677
+ def gen_interface_abort_label( interfaceName, serviceName=nil )
678
+ serviceName = gen_serviceName( interfaceName ) if serviceName.nil?
679
+ gen_identifier( serviceName, TlaElementText::TLA_LABEL_ABORT )
680
+ end
681
+
682
+ def gen_interface_end_label( interfaceName, serviceName=nil )
683
+ serviceName = gen_serviceName( interfaceName ) if serviceName.nil?
684
+ gen_identifier( serviceName, TlaElementText::TLA_LABEL_END )
685
+ end
686
+
687
+ ##
688
+ # Solidity code should check for error
689
+ def gen_interface_fail_label( interfaceName, serviceName=nil )
690
+ serviceName = gen_serviceName( interfaceName ) if serviceName.nil?
691
+ gen_identifier( serviceName, TlaElementText::TLA_LABEL_FAIL )
692
+ end
693
+
694
+ private def gen_serviceName( interfaceName )
695
+ metatype = :solidity_contract_function
696
+ gen_specname( metatype, interfaceName)
697
+ end
698
+
699
+
700
+ ##
701
+ # Create +specName+ corresponging +appName+ in +metatype+
702
+ def gen_specname( metatype, appName )
703
+
704
+ sym = :TLA_specname
705
+ addPartial( sym, "#{spec_name('specname')}" )
706
+
707
+ stla( sym,{
708
+ :specname => {
709
+ :metatype => metatype,
710
+ :appName => appName,
711
+ }})
712
+ end
713
+
714
+ # @return name
715
+ def gen_plainname( name )
716
+
717
+ sym = :TLA_plainname
718
+ addPartial( sym, "{|{name}|}" )
719
+
720
+ stla( sym, {
721
+ :name => name
722
+ })
723
+ end
724
+
725
+ def gen_constant( constant )
726
+ gen_plainname( constant )
727
+ end
728
+
729
+ # @return "str"
730
+ def gen_str( str )
731
+
732
+ sym = :TLA_str
733
+ addPartial( sym, '"{|#EVAL|}str{|/EVAL|}"' )
734
+
735
+ stla( sym, {
736
+ :str=> str.is_a?( String ) ? gen_plainname(str) : str,
737
+ })
738
+ end
739
+
740
+ # @return ( "expr" )
741
+ def gen_parenthesis( expr )
742
+
743
+ sym = :TLA_parenthesis
744
+ addPartial( sym, '( {|#EVAL|}expr{|/EVAL|} )' )
745
+
746
+ stla( sym, {
747
+ :expr=> expr.is_a?( String ) ? gen_plainname(expr) : expr,
748
+ })
749
+ end
750
+
751
+
752
+ # ------------------------------------------------------------------
753
+ # Statements
754
+
755
+ def gen_stmts( stmts )
756
+ # do not wrap twice
757
+ return stmts if stmts.is_a?( Array ) && stmts.any? && stmts[0] == :TLA_stmts
758
+
759
+ sym = :TLA_stmts
760
+ addPartial( sym,
761
+ "{|#stmts|}{|#EVAL|}.{|/EVAL|};\n"\
762
+ "{|/stmts|}"
763
+ )
764
+
765
+ stmts = stla( sym, {
766
+ # 1 stmt or array of stmts
767
+ :stmts => stmts && stmts.any? && stmts[0].is_a?(Symbol) ? s(stmts) : stmts
768
+ })
769
+
770
+ # add method to return statements in stmts block for checking
771
+ # the need to add label after if-statement
772
+ methodName = :requiresLabelAfterChecker
773
+ stmts.define_singleton_method( methodName ) do
774
+ # pass if & else
775
+ stmts[1][:stmts]
776
+ end
777
+
778
+ stmts
779
+
780
+ end
781
+
782
+ # @param [Integer|String] labelIndex generate unique labels using index
783
+ #
784
+ def gen_labeled_stmt( stmt, labelPrefix=false, labelIndex=0 )
785
+ sym = :TLA_labeled_stmt
786
+ addPartial( sym, "{|#label|}{|#EVAL|}labelPrefix{|/EVAL|}_{|labelIndex|}: {|/label|}{|#EVAL|}stmt{|/EVAL|}" )
787
+ labTla = stla( sym, {
788
+ :stmt => stmt,
789
+ :label => labelPrefix ? true : false,
790
+ :labelPrefix => labelPrefix.is_a?( String) ? gen_plainname(labelPrefix) : labelPrefix,
791
+ :labelIndex => labelIndex,
792
+ })
793
+
794
+ # labelel stmt should be followed by a label - if it is infront
795
+ # +stmt+, which should be followed by a label or if
796
+ # label is used within block
797
+ statementRequiresLabel( labTla, requiresLabelAfter( stmt ))
798
+ labTla
799
+ end
800
+
801
+ ##
802
+ # Generate statement, which may also generate trace output.
803
+ #
804
+ # @param [Array] traceArray message/evaluatable mesage
805
+ # @option traceArray [String|TlaSexp] parameter for print operation
806
+ #
807
+ def gen_traced_stmt( stmt, traceArray=false )
808
+ sym = :TLA_traced_stmt
809
+ addPartial( sym,
810
+ <<~EOS
811
+ {|#trace|}{|^preferences.tla-trace|}\\* {|/preferences.tla-trace|} {|#EVAL|}trace_print{|/EVAL|};
812
+ {|^preferences.tla-trace|}
813
+ {|/preferences.tla-trace|}{|/trace|}{|#EVAL|}stmt{|/EVAL|}
814
+ EOS
815
+ )
816
+
817
+ trace_stmt = stla( sym, {
818
+ :stmt => stmt,
819
+ :trace => traceArray ? true : false,
820
+ :trace_print => gen_print( traceArray ),
821
+ })
822
+
823
+ # trace_stmt should be followed by a label - if it is infront
824
+ # +stmt+, which should be followed by a label
825
+ statementRequiresLabel( trace_stmt ) if requiresLabelAfter( stmt )
826
+ trace_stmt
827
+
828
+ end
829
+
830
+ def gen_print( parameters )
831
+ sym = :TLA_print
832
+ addPartial(
833
+ sym,
834
+ "print "\
835
+ "{|#parameters|}{|#EVAL|}parameter{|/EVAL|}{|_comma|}{|/parameters|}"\
836
+ ""
837
+ )
838
+
839
+ # String mapper to Sexp && convert whole into to s() -array
840
+ parameters = gen_str( parameters ) if parameters.is_a?( String )
841
+ parameters = parameters[0].is_a?( Symbol ) ? s(parameters) : parameters
842
+ # parameters.unshift( TRACE_PREFIX )
843
+
844
+ stla( sym, {
845
+ :parameters => parameters.any? ?
846
+ parameters.map.with_index do |p,i|
847
+ { :_comma => (i+1) == parameters.length ? '' : ',',
848
+ :parameter => p.is_a?(String) ? gen_str(p) : p
849
+ }
850
+ end :
851
+ false,
852
+ })
853
+
854
+ end
855
+
856
+ def gen_assign( lval, rval )
857
+ sym = :TLA_assign
858
+ # tlaStatementTypeRequiresLabel( sym )
859
+ addPartial( sym, "{|#EVAL|}lval{|/EVAL|} := {|#EVAL|}rval{|/EVAL|}" )
860
+ stla( sym, {
861
+ :lval => lval.is_a?( String) ? gen_plainname(lval) : lval,
862
+ :rval => rval.is_a?( String) ? gen_plainname(rval) : rval,
863
+ })
864
+ end
865
+
866
+ def gen_return()
867
+
868
+ sym = :TLA_return
869
+ tlaStatementTypeRequiresLabel( sym )
870
+ addPartial( sym, "return" )
871
+ stla( sym, {})
872
+
873
+ end
874
+
875
+ def gen_assert( cond, msg )
876
+
877
+ sym = :TLA_assert
878
+ tlaStatementTypeRequiresLabel( sym )
879
+ addPartial( sym,
880
+ "(* {|#EVAL|}msg{|/EVAL|} *)\n"\
881
+ "assert( {|#EVAL|}cond{|/EVAL|} )"
882
+ )
883
+ stla( sym, {
884
+ :cond => cond.is_a?(String) ? gen_plainname(cond) : cond,
885
+ :msg => msg.is_a?(String) ? gen_str(msg) : msg,
886
+ })
887
+ end
888
+
889
+
890
+
891
+
892
+ def gen_free_text( str )
893
+ sym = :TLA_free_text
894
+ addPartial( sym, "{|{text}|}" )
895
+ # tlaStatementTypeRequiresLabel( sym )
896
+ stla( sym, {
897
+ :text => str,
898
+ })
899
+ end
900
+
901
+ ##
902
+ # preable at the start of service procedure
903
+ def gen_check_resume( procedureName )
904
+
905
+ sym = :TLA_check_resume
906
+ addPartial(
907
+ sym,
908
+ <<~EOS
909
+ (* check if resuming {|#EVAL|}procedureName{|/EVAL|} *)
910
+ if ( resume_context # Nil ) {
911
+ (* prepare stack entry with resume context followed by
912
+ a return to jump to resume location *)
913
+ stack := <<resume_context.ret_ctx >> \\o stack[self];
914
+ {|#EVAL|}procedureName{|/EVAL|}_resumed:
915
+ return;
916
+ }
917
+ EOS
918
+ )
919
+ tlaStatementTypeRequiresLabel( sym )
920
+ stla( sym, {
921
+ :procedureName => procedureName,
922
+ })
923
+
924
+ end
925
+
926
+ def gen_construct_contract( stateVariable, newContract, idPool, id )
927
+ sym = :TLA_construct_contract
928
+ addPartial( sym,
929
+ <<~EOS
930
+ \\* construct {|#EVAL|}contracts{|/EVAL|}
931
+ create_new_contract( {|#EVAL|}contracts{|/EVAL|}, {|#EVAL|}newContract{|/EVAL|}, {|#EVAL|}idPool{|/EVAL|}, {|#EVAL|}id{|/EVAL|} )
932
+ EOS
933
+ )
934
+ stla( sym, {
935
+ :contracts => stateVariable,
936
+ :newContract => newContract,
937
+ :idPool => idPool,
938
+ :id => id,
939
+ })
940
+
941
+ end
942
+
943
+
944
+ def gen_debug_msg( str )
945
+ sym = :TLA_debug_msg
946
+ # addPartial( sym, '{|^preferences.tla-debug|}\*{|/preferences.tla-debug|} print << "DEBUG: {|#EVAL|}text{|/EVAL|}" >>' )
947
+ addPartial( sym, 'print << "DEBUG: {|#EVAL|}text{|/EVAL|}" >>' )
948
+ # tlaStatementTypeRequiresLabel( sym )
949
+ stla( sym, {
950
+ :text => str.is_a?( String ) ? gen_plainname(str) : str,
951
+ })
952
+ end
953
+
954
+ def gen_commented( content )
955
+ sym = :TLA_commented
956
+ addPartial( sym,
957
+ "(* {|#EVAL|}content{|/EVAL|} *)\n"\
958
+ "skip"
959
+ )
960
+ stla( sym, {
961
+ :content => content.is_a?( String ) ? gen_plainname(content) : content,
962
+ })
963
+ end
964
+
965
+
966
+ def gen_procedure_call( procedure, parameters=s() )
967
+ sym = :TLA_procedure_call
968
+ # add label after proc.call
969
+ tlaStatementTypeRequiresLabel( sym )
970
+ addPartial(
971
+ sym,
972
+ "call {|#EVAL|}procedure{|/EVAL|}( {|#parameters|}{|#EVAL|}parameter{|/EVAL|}{|_comma|}{|/parameters|})"
973
+ )
974
+
975
+ parameters = parameters.is_a?( String ) ? gen_plainname(parameters) : parameters
976
+ parameters = parameters[0].is_a?( Symbol ) ? s(parameters) : parameters
977
+ stla( sym, {
978
+ :procedure => procedure.is_a?( String ) ? gen_plainname(procedure) : procedure,
979
+ # false or [ {:p, _comma: }]
980
+ :parameters => parameters.any? ?
981
+ parameters.map.with_index do |p,i|
982
+ { :_comma => (i+1) == parameters.length ? '' : ',',
983
+ :parameter => p
984
+ }
985
+ end :
986
+ false,
987
+ })
988
+ end
989
+
990
+ def gen_and( lhs, rhs )
991
+ gen_bin_op( '/\\', lhs, rhs )
992
+ end
993
+
994
+ def gen_not( expression )
995
+ gen_unary_op( '~', expression )
996
+ # sym = :TLA_not
997
+ # addPartial(
998
+ # sym,
999
+ # "~{|#EVAL|}expression{|/EVAL|}"
1000
+ # )
1001
+ # stla( sym, {
1002
+ # :expression =>expression,
1003
+ # })
1004
+ end
1005
+
1006
+ def gen_unary_op( op, expr )
1007
+ sym = :TLA_unary_op
1008
+ addPartial(
1009
+ sym, "{|{op}|} {|#EVAL|}expr{|/EVAL|}"
1010
+ )
1011
+
1012
+ stla( sym, {
1013
+ :expr => expr.is_a?( String) ? gen_plainname(expr) : expr,
1014
+ :op => op,
1015
+ })
1016
+ end
1017
+
1018
+ def gen_bin_op( op, lhs, rhs )
1019
+ sym = :TLA_bin_op
1020
+ addPartial(
1021
+ sym, "{|#EVAL|}lhs{|/EVAL|} {|{op}|} {|#EVAL|}rhs{|/EVAL|}"
1022
+ )
1023
+
1024
+ stla( sym, {
1025
+ :lhs => lhs.is_a?( String) ? gen_plainname(lhs) : lhs,
1026
+ :op => op,
1027
+ :rhs => rhs.is_a?( String) ? gen_plainname(rhs) : rhs,
1028
+ })
1029
+ end
1030
+
1031
+ def gen_either( stmt, stmts )
1032
+ sym = :TLA_either
1033
+ tlaStatementTypeRequiresLabel( sym )
1034
+ addPartial(
1035
+ sym,
1036
+ "either {|#EVAL|}stmt{|/EVAL|}" \
1037
+ "{|#stmts|} or {|#EVAL|}.{|/EVAL|}"\
1038
+ "{|/stmts|}"
1039
+ )
1040
+
1041
+ stla( sym, {
1042
+ :stmt => stmt,
1043
+ :stmts => stmts[0].is_a?(Symbol) ? s(stmts) : stmts,
1044
+ })
1045
+
1046
+ end
1047
+
1048
+ # @param [TLA_stmts|sTla] thenStatement
1049
+ # @param [TLA_stmts|sTla|nil|false] elseStatement
1050
+ def gen_if( expression, thenStatement, elseStatement=nil )
1051
+ sym = :TLA_if
1052
+ addPartial(
1053
+ sym,
1054
+
1055
+ "if ( {|#EVAL|}cond{|/EVAL|} ) {\n"\
1056
+ ' {|#EVAL|}then{|/EVAL|}'\
1057
+ '}{|#else_given|} '\
1058
+ "else {\n"\
1059
+ ' {|#EVAL|}else{|/EVAL|}'\
1060
+ '}{|/else_given|}'
1061
+ )
1062
+
1063
+
1064
+ stmt_if = stla( sym, {
1065
+ :cond => expression.is_a?(String) ? gen_plainname(expression) : expression,
1066
+ # :then => gen_stmts( thenStatement.length == 1 ? s(stmt) : thenStatement ),
1067
+ :then => thenStatement[0] == :TLA_stmts ? thenStatement : gen_stmts( thenStatement ),
1068
+ :else_given => elseStatement && elseStatement.length > 0 ? true : false,
1069
+ :else => elseStatement ? ( elseStatement[0] == :TLA_stmts ? elseStatement : gen_stmts( elseStatement ) ) : false,
1070
+ })
1071
+
1072
+ tlaStatementTypeRequiresLabel( sym )
1073
+ # add label if then part of else part requires label
1074
+ # statementRequiresLabel( stmt_if ) if requiresLabelAfter( thenStatement) || requiresLabelAfter(elseStatement)
1075
+
1076
+ # add method to return statements in then/else braches for
1077
+ # checking the need to add label after if-statement
1078
+ methodName = :requiresLabelAfterChecker
1079
+ stmt_if.define_singleton_method( methodName ) do
1080
+ # pass if & else
1081
+ stmt_if[1][:then][1][:stmts] + ( stmt_if[1][:else] ? stmt_if[1][:else][1][:stmts] : [] )
1082
+ end
1083
+
1084
+ # add method to recurse into then/else block to label
1085
+ # statements within these blocks
1086
+ setLabelBlockStatements( stmt_if, stmt_if[1][:then][1][:stmts] )
1087
+ setLabelBlockStatements( stmt_if, stmt_if[1][:else][1][:stmts], :labelBlockStatementsElse ) if elseStatement
1088
+
1089
+ stmt_if
1090
+ end
1091
+
1092
+ def gen_IF( expression, thenExpr, elseExpr )
1093
+ sym = :TLA_IF
1094
+ addPartial(
1095
+ sym,
1096
+
1097
+ "IF {|#EVAL|}cond{|/EVAL|} THEN "\
1098
+ '{|#EVAL|}then{|/EVAL|} '\
1099
+ 'ELSE '\
1100
+ '{|#EVAL|}else{|/EVAL|}'
1101
+ )
1102
+
1103
+ stla( sym, {
1104
+ :cond => expression.is_a?( String) ? gen_plainname(expression) : expression,
1105
+ # :then => gen_stmts( thenExpr.length == 1 ? s(stmt) : thenExpr ),
1106
+ :then => thenExpr.is_a?( String) ? gen_plainname(thenExpr) : thenExpr,
1107
+ :else => elseExpr.is_a?( String) ? gen_plainname(elseExpr) : elseExpr,
1108
+ })
1109
+ end
1110
+
1111
+ def gen_EXCEPT( variable, excepts )
1112
+ sym = :TLA_EXCEPT
1113
+ addPartial(
1114
+ sym,
1115
+ "[ {|#EVAL|}variable{|/EVAL|} "\
1116
+ "EXCEPT "\
1117
+ '{|#excepts|}{|#EVAL|}except{|/EVAL|}{|_comma|}{|/excepts|} '\
1118
+ ']'
1119
+ )
1120
+
1121
+ # if signleton given
1122
+ excepts = excepts[0].is_a?(Symbol) ? s(excepts) : excepts
1123
+ stla( sym, {
1124
+ :variable => variable.is_a?( String ) ? gen_plainname(variable ) : variable,
1125
+ # :then => gen_stmts( thenExpr.length == 1 ? s(stmt) : thenExpr ),
1126
+ :excepts => excepts.any? ?
1127
+ excepts.map.with_index do |p,i|
1128
+ { :_comma => (i+1) == excepts.length ? '' : ',',
1129
+ :except => p
1130
+ }
1131
+ end :
1132
+ false,
1133
+ })
1134
+ end
1135
+
1136
+
1137
+ def gen_skip()
1138
+ sym = :TLA_skip
1139
+ addPartial( sym, "skip" )
1140
+ # tlaStatementTypeRequiresLabel( sym )
1141
+ stla( sym, {})
1142
+ end
1143
+
1144
+ ##
1145
+ # skip statement, but reqeuires label infront
1146
+ def gen_label
1147
+ sym = :TLA_label
1148
+ # tlaLabelCurrentStatement( sym )
1149
+ addPartial( sym, "skip" )
1150
+ stmt = stla( sym, {})
1151
+
1152
+ # make it add label infront
1153
+ stmt.define_singleton_method( :labelCurrentStatement ) do
1154
+ true
1155
+ end
1156
+ stmt
1157
+
1158
+ end
1159
+
1160
+
1161
+
1162
+ # ------------------------------------------------------------------
1163
+ # NOT USED
1164
+
1165
+
1166
+ # Generate element for a lval to set a +return_variable+ of +function_name+
1167
+ def lval_function_return( function_name, return_variable )
1168
+
1169
+ sym = :TLA_lval_function_return
1170
+ addPartial( sym,
1171
+ "{|function_name|}_{|return_variable|}" )
1172
+
1173
+ stla( sym,
1174
+ {
1175
+ :function_name =>function_name,
1176
+ :return_variable =>return_variable,
1177
+ })
1178
+
1179
+ end
1180
+
1181
+ # TODO: remove
1182
+ def rval_todo_remove
1183
+
1184
+ sym = :TLA_rval_todo_remove
1185
+ addPartial( sym, "{|demo|}" )
1186
+
1187
+ stla( sym,
1188
+ {
1189
+ :demo =>1,
1190
+ })
1191
+ end
1192
+
1193
+ # Usefull as a placeholder
1194
+ def gen_operator_dummy( metatype, name )
1195
+ gen_operator(
1196
+ gen_specname( metatype, name ),
1197
+ s(),
1198
+ gen_free_text( "TRUE" ),
1199
+ )
1200
+ end
1201
+
1202
+ def gen_operator_call( operatorName, parameters=[] )
1203
+ sym = :TLA_operator_call
1204
+ addPartial(
1205
+ sym,
1206
+ "{|#EVAL|}operatorName{|/EVAL|}"\
1207
+ "{|#parameters.length|}( {|/parameters.length|}"\
1208
+ "{|#parameters|}{|#EVAL|}parameter{|/EVAL|}{|_comma|}{|/parameters|}"\
1209
+ "{|#parameters.length|} ){|/parameters.length|}"
1210
+ )
1211
+
1212
+ parameters = parameters[0].is_a?( Symbol ) ? s(parameters) : parameters
1213
+
1214
+ stla( sym, {
1215
+ :operatorName => operatorName.is_a?( String ) ? gen_plainname(operatorName ) : operatorName,
1216
+ :parameters => parameters.any? ?
1217
+ parameters.map.with_index do |p,i|
1218
+ { :_comma => (i+1) == parameters.length ? '' : ',',
1219
+ :parameter => p
1220
+ }
1221
+ end :
1222
+ false,
1223
+ })
1224
+
1225
+ end
1226
+
1227
+ ##
1228
+ # Like operator call, but allways use ()
1229
+ def gen_macro_call( macroName, parameters=[] )
1230
+ sym = :TLA_macro_call
1231
+ addPartial(
1232
+ sym,
1233
+ "{|#EVAL|}macroName{|/EVAL|}("\
1234
+ "{|#parameters|}{|#EVAL|}parameter{|/EVAL|}{|_comma|}{|/parameters|}"\
1235
+ ")"
1236
+ )
1237
+
1238
+ parameters = parameters[0].is_a?( Symbol ) ? s(parameters) : parameters
1239
+
1240
+ stla( sym, {
1241
+ :macroName => macroName.is_a?( String ) ? gen_plainname(macroName ) : macroName,
1242
+ :parameters => parameters.any? ?
1243
+ parameters.map.with_index do |p,i|
1244
+ { :_comma => (i+1) == parameters.length ? '' : ',',
1245
+ :parameter => p
1246
+ }
1247
+ end :
1248
+ false,
1249
+ })
1250
+
1251
+ end
1252
+
1253
+
1254
+
1255
+ def gen_goto( label )
1256
+ sym = :TLA_goto
1257
+ tlaStatementTypeRequiresLabel( sym )
1258
+ addPartial( sym, "goto {|#EVAL|}goto{|/EVAL|}" )
1259
+
1260
+ stla( sym, {
1261
+ :goto => label.is_a?( String) ? gen_plainname(label) : label,
1262
+ })
1263
+
1264
+ end
1265
+
1266
+
1267
+
1268
+ #
1269
+ def gen_operator( operatorName, parameters, operatorBody )
1270
+ sym = :TLA_operator
1271
+ addPartial(
1272
+ sym,
1273
+ "{|#EVAL|}operatorName{|/EVAL|}"\
1274
+ "{|#parameters.length|}({|/parameters.length|}{|#parameters|}"\
1275
+ "{|#EVAL|}parameter{|/EVAL|}{|_comma|}{|/parameters|}"\
1276
+ "{|#parameters.length|}){|/parameters.length|}"\
1277
+ " == {|#EVAL|}operatorBody{|/EVAL|}"\
1278
+ "\n"
1279
+ )
1280
+
1281
+ stla( sym,
1282
+ {
1283
+ :operatorName => operatorName,
1284
+ :parameters => parameters.any? ?
1285
+ parameters.map.with_index do |p,i|
1286
+ { :_comma => (i+1) == parameters.length ? '' : ',',
1287
+ :parameter => p.is_a?(String) ? gen_plainname( p ): p,
1288
+ }
1289
+ end :
1290
+ false,
1291
+ :operatorBody => operatorBody.is_a?( String ) ? gen_plainname( operatorBody ) : operatorBody,
1292
+ })
1293
+ end
1294
+
1295
+ def return_stmt( functionNameDef )
1296
+ sym = :TLA_return_stmt
1297
+ addPartial( sym, "goto #{spec_name('return_from')}_exit; \\* return contract '{| return_from |}'" )
1298
+
1299
+ stla( sym,
1300
+ {
1301
+ :return_from =>functionNameDef
1302
+ })
1303
+ end
1304
+
1305
+
1306
+ # NOT USED
1307
+ # ------------------------------------------------------------------
1308
+
1309
+
1310
+
1311
+ # ------------------------------------------------------------------
1312
+ # @!group common idioms
1313
+
1314
+ ##
1315
+ # Mustache SPEC_NAME lamba call for +metatype+ & +appName+
1316
+ # access unique name for +metatype+/+appName+ combination in
1317
+ # specification code.
1318
+ #
1319
+ # @param [Hash] specNameDef name in tla generate element
1320
+ # @option specNameDef [Symbol] :metatype
1321
+ # @option specNameDef [Symbol] :appName
1322
+ #
1323
+ # @return [String] string wrapped within SPEC_NAME mustache
1324
+ # lambda call
1325
+ def spec_name( specNameDef )
1326
+ # "{{#SPEC_NAME}}#{specNameDef[:metatype]}.#{specNameDef[:appName]}{{/SPEC_NAME}}"
1327
+ # "{{#SPEC_NAME}}{{#{specNameDef}.metatype}}.{{#{specNameDef}.appName}}{{/SPEC_NAME}}"
1328
+ # "{{#SPEC_NAME}}{{metatype}}.{{appName}}{{/SPEC_NAME}}"
1329
+ "{{#SPEC_NAME}}{|#{specNameDef}.metatype|}.{|#{specNameDef}.appName|}{{/SPEC_NAME}}"
1330
+ end
1331
+
1332
+ def appName( specNameDef )
1333
+ "{|#{specNameDef}.metatype|}.{|#{specNameDef}.appName|}"
1334
+ end
1335
+
1336
+
1337
+ def exit_label( funcNameDef )
1338
+ end
1339
+
1340
+
1341
+
1342
+ # @!endgroup
1343
+
1344
+
1345
+ end
1346
+ end
1347
+ end
1348
+ end