sbuilder-al 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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