Commit ba92837a authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Work on the stg backend

parent 65a2f3dc
...@@ -5,6 +5,13 @@ ...@@ -5,6 +5,13 @@
OpenGEODE - A tiny SDL Editor for TASTE - StringTemplate backend OpenGEODE - A tiny SDL Editor for TASTE - StringTemplate backend
This module uses the StringTemplate library to ease writing new backends This module uses the StringTemplate library to ease writing new backends
To use this module:
import StgBackend as stg
stg.prepare_model(ogAST.process) # give your process AST as input
for each stg_file:
stg.initialize_stg(simu=True/False, each)
stg.generate(ogAST.process)
Copyright (c) 2012-2015 European Space Agency Copyright (c) 2012-2015 European Space Agency
...@@ -46,24 +53,16 @@ UNICODE_SEP = u'\u00dc' ...@@ -46,24 +53,16 @@ UNICODE_SEP = u'\u00dc'
# STG group file, avoid passing a parameter as it is a module-level shared var # STG group file, avoid passing a parameter as it is a module-level shared var
STG = None STG = None
@singledispatch # Shortcut function to instantiate a STG template
def generate(*args, **kwargs): new = lambda inst: STG.getInstanceOf(inst)
''' Generate the code for an item of the AST '''
raise TypeError('[StringTemplate backend] Unsupported AST construct')
return [], []
# Processing of the AST def initialize_stg(simu=False, stgfile='ada_body.st'):
@generate.register(ogAST.Process) ''' Load the STG backend and set gobal STG pointer '''
def _process(process, simu=False, stgfile='ada_source.st', **kwargs):
''' Generate the code for a complete process (AST Top level) '''
global TYPES
global STG global STG
global SHARED_LIB global SHARED_LIB
process_name = process.processName
if not stg: if not stg:
LOG.error('Library missing. As root: pip install stringtemplate3') LOG.error('Library missing. Use pip install stringtemplate3')
return return
# Load the file containing a group of templates # Load the file containing a group of templates
...@@ -72,58 +71,65 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs): ...@@ -72,58 +71,65 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs):
LOG.error('Could not load StringTemplate group') LOG.error('Could not load StringTemplate group')
return return
# Get the template corresponding to a SDL PROCESS if simu:
process_template = STG.getInstanceOf("process") SHARED_LIB = True
process_template['name'] = process_name
# Set Simulation/DLL mode
process_template['simu'] = simu
def prepare_model(process):
''' Do some AST transformations: flatten model, etc. '''
global TYPES
TYPES = process.dataview TYPES = process.dataview
del OUT_SIGNALS[:] del OUT_SIGNALS[:]
del PROCEDURES[:] del PROCEDURES[:]
OUT_SIGNALS.extend(process.output_signals) OUT_SIGNALS.extend(process.output_signals)
PROCEDURES.extend(process.procedures) PROCEDURES.extend(process.procedures)
if simu:
SHARED_LIB = True
LOG.info('Generating code for process {}'.format(process_name))
# In case model has nested states, flatten everything # In case model has nested states, flatten everything (in-place)
Helper.flatten(process, sep=UNICODE_SEP) Helper.flatten(process, sep=UNICODE_SEP)
# Make an maping {input: {state: transition...}} in order to easily # Add an maping {input: {state: transition...}} in order to easily
# generate the lookup tables for the state machine runtime # generate the lookup tables for the state machine runtime
mapping = Helper.map_input_state(process) process.map_inp_states = Helper.map_input_state(process)
VARIABLES.update(process.variables) VARIABLES.update(process.variables)
# Initialize array of strings containing all local declarations @singledispatch
process_decl, process_vars = dcl(process, simu) def generate(*args, **kwargs):
''' Generate the code for an item of the AST '''
raise TypeError('[StringTemplate backend] Unsupported AST construct')
return [], []
# Set the DCL declarations variable in the process template
process_template['dcl'] = process_decl
process_template['vars'] = process_vars
# Processing of the AST
@generate.register(ogAST.Process)
def _process(process, **kwargs):
''' Generate the code for a complete process (AST Top level) '''
assert stg and STG
process_name = process.processName
LOG.info('Generating code for process {}'.format(process.processName))
# Get the template corresponding to a SDL PROCESS
tpl = new("process")
# Initialize array of strings containing all local declarations
tpl['arrsDcl'], process_decl, process_vars = dcl(process, simu)
# Set the list of SDL states # Set the list of SDL states
process_template['states'] = (name for name in process.mapping.iterkeys() tpl['states'] = (name for name in process.mapping.viewkeys()
if not name.endswith(u'START')) if not name.endswith(u'START'))
# Set constants corresponding to substate start transitions # Set constants corresponding to substate start transitions
constants = [] constants = []
for name, val in process.mapping.viewitems(): for name, val in process.mapping.viewitems():
if name.endswith(u'START') and name != u'START': if name.endswith(u'START') and name != u'START':
const_template = STG.getInstanceOf("constant") const_template = new("constant")
const_template['var'] = name const_template['var'] = name
const_template['val'] = str(val) const_template['val'] = str(val)
constants.append(str(const_template)) constants.append(str(const_template))
process_template['constants'] = constants tpl['constants'] = constants
try: try:
process_template['asn1_mod'] = (dv.replace('-', '_') tpl['asn1_mod'] = (dv.replace('-', '_') for dv in process.asn1Modules)
for dv in process.asn1Modules)
except TypeError: except TypeError:
pass # No ASN.1 module pass # No ASN.1 module
...@@ -136,15 +142,15 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs): ...@@ -136,15 +142,15 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs):
inner_procedures_code.extend(proc_code) inner_procedures_code.extend(proc_code)
# Generate the code for the process-level variable declarations # Generate the code for the process-level variable declarations
process_template['pdecl'] = inner_procedure_decl tpl['pdecl'] = inner_procedure_decl
process_template['pcode'] = inner_procedures_code tpl['pcode'] = inner_procedures_code
# Generate the code for each input signal (provided interface) and timers # Generate the code for each input signal (provided interface) and timers
pi_code = [] pi_code = []
for signal in process.input_signals + [ for signal in process.input_signals + [
{'name': timer.lower()} for timer in process.timers]: {'name': timer.lower()} for timer in process.timers]:
sig_template = STG.getInstanceOf('pi_signature') sig_template = new('pi_signature')
if signal.get('name', u'START') == u'START': if signal.get('name', u'START') == u'START':
continue continue
sig_template['name'] = signal['name'] sig_template['name'] = signal['name']
...@@ -153,7 +159,7 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs): ...@@ -153,7 +159,7 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs):
sig_template['param_name'] = signal.get('param_name') sig_template['param_name'] = signal.get('param_name')
sig_template['param_sort'] = type_name(signal['type']) sig_template['param_sort'] = type_name(signal['type'])
pi_template = STG.getInstanceOf('input_signal') pi_template = new('input_signal')
pi_template['header'] = str(sig_template) pi_template['header'] = str(sig_template)
pi_template['name'] = signal['name'] pi_template['name'] = signal['name']
pi_template['process'] = process_name pi_template['process'] = process_name
...@@ -164,9 +170,9 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs): ...@@ -164,9 +170,9 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs):
for state in process.mapping.viewkeys(): for state in process.mapping.viewkeys():
if state.endswith(u'START'): if state.endswith(u'START'):
continue continue
case_template = STG.getInstanceOf('case_state') case_template = new('case_state')
case_template['name'] = state case_template['name'] = state
input_def = mapping[signal['name']].get(state) input_def = process.map_inp_state[signal['name']].get(state)
# Check for nested states to call optional exit procedure # Check for nested states to call optional exit procedure
sep = UNICODE_SEP sep = UNICODE_SEP
state_tree = state.split(sep) state_tree = state.split(sep)
...@@ -195,7 +201,7 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs): ...@@ -195,7 +201,7 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs):
for inp in input_def.parameters: for inp in input_def.parameters:
# Assign the (optional and unique) parameter # Assign the (optional and unique) parameter
# to the corresponding process variable # to the corresponding process variable
assig_template = STG.getInstanceOf('assign_param') assig_template = new('assign_param')
assig_template['local_var'] = inp assig_template['local_var'] = inp
assig_template['param_name'] = param_name assig_template['param_name'] = param_name
params_lst.append(str(assig_template)) params_lst.append(str(assig_template))
...@@ -209,25 +215,25 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs): ...@@ -209,25 +215,25 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs):
# Generate the template amd add it to a list # Generate the template amd add it to a list
pi_code.append(str(pi_template)) pi_code.append(str(pi_template))
process_template['arrs_inp'] = pi_code tpl['arrs_inp'] = pi_code
# for the .ads file, generate the declaration of the required interfaces # for the .ads file, generate the declaration of the required interfaces
# output signals are the asynchronous RI - only one parameter # output signals are the asynchronous RI - only one parameter
required_interfaces = [] required_interfaces = []
for signal in process.output_signals: for signal in process.output_signals:
# TODO, pass the type and name of the parameter (for the Ads) # TODO, pass the type and name of the parameter (for the Ads)
ri_template = STG.getInstanceOf("required_interface") ri_template = new("required_interface")
ri_template['name'] = signal['name'] ri_template['name'] = signal['name']
ri_template['simu'] = simu ri_template['simu'] = simu
required_interfaces.append(str(ri_template)) required_interfaces.append(str(ri_template))
process_template['arrs_async_ri'] = required_interfaces tpl['arrs_async_ri'] = required_interfaces
external_proc = [] external_proc = []
# Generate the declaration of the external procedures # Generate the declaration of the external procedures
for proc in (proc for proc in process.procedures if proc.external): for proc in (proc for proc in process.procedures if proc.external):
signature_template = STG.getInstanceOf("ext_procedure_signature") signature_template = new("ext_procedure_signature")
signature_template['name'] = proc.inputString signature_template['name'] = proc.inputString
fpar = [] fpar = []
...@@ -236,20 +242,20 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs): ...@@ -236,20 +242,20 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs):
'sort': type_name(fpar.get('type'))}) 'sort': type_name(fpar.get('type'))})
signature_template['fpar'] = fpar signature_template['fpar'] = fpar
declaration_template = STG.getInstanceOf("ext_procedure_declaration") declaration_template = new("ext_procedure_declaration")
declaration_template['header'] = str(signature_template) declaration_template['header'] = str(signature_template)
declaration_template['name'] = proc.inputString declaration_template['name'] = proc.inputString
declaration_template['ctxt'] = process_name declaration_template['ctxt'] = process_name
external_proc.append(str(declaration_template)) external_proc.append(str(declaration_template))
process_template['ext_proc'] = external_proc tpl['ext_proc'] = external_proc
# for the .ads file, generate the declaration of timers set/reset functions # for the .ads file, generate the declaration of timers set/reset functions
timer_decls, timer_defs = [], [] timer_decls, timer_defs = [], []
for timer in process.timers: for timer in process.timers:
timer_decl_template = STG.getInstanceOf("timer_declaration") timer_decl_template = new("timer_declaration")
timer_def_template = STG.getInstanceOf("timer_definition") timer_def_template = new("timer_definition")
timer_decl_template['name'] = timer timer_decl_template['name'] = timer
timer_def_template['name'] = timer timer_def_template['name'] = timer
timer_decl_template['ctxt'] = process_name timer_decl_template['ctxt'] = process_name
...@@ -259,8 +265,8 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs): ...@@ -259,8 +265,8 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs):
timer_decls.append(str(timer_decl_template)) timer_decls.append(str(timer_decl_template))
timer_defs.append(str(timer_de_template)) timer_defs.append(str(timer_de_template))
process_template['timer_decls'] = timer_decls tpl['timer_decls'] = timer_decls
process_template['timer_defs'] = timer_defs tpl['timer_defs'] = timer_defs
# Transform inner labels to floating labels # Transform inner labels to floating labels
...@@ -282,8 +288,8 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs): ...@@ -282,8 +288,8 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs):
local_decl_transitions.extend(label_decl) local_decl_transitions.extend(label_decl)
code_labels.extend(code_label) code_labels.extend(code_label)
process_template['tr_locals'] = set(local_decl_transitions) tpl['tr_locals'] = set(local_decl_transitions)
process_template['flab_code'] = code_labels tpl['flab_code'] = code_labels
# Generate a loop that ends when a next state is reached # Generate a loop that ends when a next state is reached
# (there can be chained transition when entering a nested state) # (there can be chained transition when entering a nested state)
...@@ -294,15 +300,15 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs): ...@@ -294,15 +300,15 @@ def _process(process, simu=False, stgfile='ada_source.st', **kwargs):
tr_code = [] tr_code = []
for idx, val in enumerate(code_transitions): for idx, val in enumerate(code_transitions):
tr_template = STG.getInstanceOf("transition_code") tr_template = new("transition_code")
tr_template['idx'] = idx tr_template['idx'] = idx
val = [u'{line}'.format(line=l) for l in val] val = [u'{line}'.format(line=l) for l in val]
tr_template['code'] = val tr_template['code'] = val
tr_code.append(str(tr_template)) tr_code.append(str(tr_template))
process_template['tr_code'] = tr_code tpl['tr_code'] = tr_code
result = str(process_template).encode('latin1') result = str(tpl).encode('latin1')
# with open(process_name + '.adb', 'w') as ada_file: # with open(process_name + '.adb', 'w') as ada_file:
# ada_file.write(taste_template.encode('latin1')) # ada_file.write(taste_template.encode('latin1'))
...@@ -1484,7 +1490,7 @@ def _label(lab, **kwargs): ...@@ -1484,7 +1490,7 @@ def _label(lab, **kwargs):
''' Transition following labels are generated in a separate section ''' Transition following labels are generated in a separate section
for visibility reasons (see Ada scope) for visibility reasons (see Ada scope)
''' '''
template = STG.getInstanceOf("label") template = new("label")
template['name'] = lab.inputString template['name'] = lab.inputString
return str(template), [] return str(template), []
...@@ -1561,7 +1567,7 @@ def _transition(tr, **kwargs): ...@@ -1561,7 +1567,7 @@ def _transition(tr, **kwargs):
def _floating_label(label, **kwargs): def _floating_label(label, **kwargs):
''' Generate the code for a floating label (Ada label + transition) ''' ''' Generate the code for a floating label (Ada label + transition) '''
local_decl = [] local_decl = []
template = STG.getInstanceOf("floating_label") template = new("floating_label")
template['traceability']= traceability(label) template['traceability']= traceability(label)
template['name'] = label.inputString template['name'] = label.inputString
if label.transition: if label.transition:
...@@ -1581,20 +1587,20 @@ def _inner_procedure(proc, **kwargs): ...@@ -1581,20 +1587,20 @@ def _inner_procedure(proc, **kwargs):
for var in proc.fpar: for var in proc.fpar:
VARIABLES.update({var['name']: (var['type'], None)}) VARIABLES.update({var['name']: (var['type'], None)})
signature_template = STG.getInstanceOf("inner_procedure_signature") signature_template = new("inner_procedure_signature")
fpar = [] fpar = []
for each in proc.fpar: for each in proc.fpar:
fpar.append({'name': fpar.get('name'), fpar.append({'name': fpar.get('name'),
'direction': str(STG.getInstanceOf("direction_out")) 'direction': str(new("direction_out"))
if fpar.get('direction') == 'out' if fpar.get('direction') == 'out'
else str(STG.getInstanceOf("direction_in")), else str(new("direction_in")),
'sort': type_name(fpar.get('type'))}) 'sort': type_name(fpar.get('type'))})
signature_template['name'] = proc.inputString signature_template['name'] = proc.inputString
signature_template['external'] = proc.external signature_template['external'] = proc.external
signature_template['fpar'] = fpar signature_template['fpar'] = fpar
declaration_template = STG.getInstanceOf("inner_procedure_declaration") declaration_template = new("inner_procedure_declaration")
declaration_template['header'] = str(signature_template) declaration_template['header'] = str(signature_template)
declaration_template['name'] = proc.inputString declaration_template['name'] = proc.inputString
declaration_template['external'] = proc.external declaration_template['external'] = proc.external
...@@ -1603,7 +1609,7 @@ def _inner_procedure(proc, **kwargs): ...@@ -1603,7 +1609,7 @@ def _inner_procedure(proc, **kwargs):
if not proc.external: if not proc.external:
# Generate the code for the procedure itself # Generate the code for the procedure itself
definition_template = STG.getInstanceOf("inner_procedure_definition") definition_template = new("inner_procedure_definition")
definition_template['header'] = str(signature_template) definition_template['header'] = str(signature_template)
definition_template['name'] = proc.inputString definition_template['name'] = proc.inputString
definition_template['dcl'], _ = dcl(proc, simu=False) definition_template['dcl'], _ = dcl(proc, simu=False)
...@@ -1633,7 +1639,7 @@ def dcl(entity, simu): ...@@ -1633,7 +1639,7 @@ def dcl(entity, simu):
''' Generate the code to declare variables ''' ''' Generate the code to declare variables '''
decl, arr_vars = [], [] decl, arr_vars = [], []
for var_name, (var_type, def_value) in entity.variables.viewitems(): for var_name, (var_type, def_value) in entity.variables.viewitems():
dcl_template = STG.getInstanceOf("dcl") dcl_template = new("dcl")
dcl_template['simu'] = simu dcl_template['simu'] = simu
if def_value: if def_value:
# Expression must be a ground expression, i.e. must not # Expression must be a ground expression, i.e. must not
...@@ -1804,7 +1810,7 @@ def path_type(path): ...@@ -1804,7 +1810,7 @@ def path_type(path):
def traceability(symbol): def traceability(symbol):
''' Return a string with code-to-model traceability ''' ''' Return a string with code-to-model traceability '''
template = STG.getInstanceOf("comment") template = new("comment")
template['lines'] = symbol.trace().split('\n') template['lines'] = symbol.trace().split('\n')
result = [str(template)] result = [str(template)]
if hasattr(symbol, 'comment') and symbol.comment: if hasattr(symbol, 'comment') and symbol.comment:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment