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

Minor fixes

parent f1746aa7
...@@ -69,6 +69,8 @@ ...@@ -69,6 +69,8 @@
import logging import logging
from itertools import chain
from singledispatch import singledispatch from singledispatch import singledispatch
import ogAST import ogAST
...@@ -98,7 +100,6 @@ def generate(ast): ...@@ -98,7 +100,6 @@ def generate(ast):
def _process(process): def _process(process):
''' Generate the code for a complete process (AST Top level) ''' ''' Generate the code for a complete process (AST Top level) '''
process_name = process.processName process_name = process.processName
VARIABLES.update(process.variables)
global TYPES global TYPES
TYPES = process.dataview TYPES = process.dataview
del OUT_SIGNALS[:] del OUT_SIGNALS[:]
...@@ -106,24 +107,9 @@ def _process(process): ...@@ -106,24 +107,9 @@ def _process(process):
del INNER_PROCEDURES[:] del INNER_PROCEDURES[:]
OUT_SIGNALS.extend(process.output_signals) OUT_SIGNALS.extend(process.output_signals)
PROCEDURES.extend(process.procedures) PROCEDURES.extend(process.procedures)
INNER_PROCEDURES.extend(process.content.inner_procedures)
LOG.info('Generating Ada code for process ' + str(process_name)) LOG.info('Generating Ada code for process ' + str(process_name))
# Generate the code to declare process-level variables
process_level_decl = []
for var_name, (var_type, def_value) in process.variables.viewitems():
if def_value:
# Expression must be a ground expression, i.e. must not
# require temporary variable to store computed result
dst, dstr, dlocal = generate(def_value)
assert not dst and not dlocal, 'DCL: Expecting a ground expression'
process_level_decl.append(
'l_{n} : aliased asn1Scc{t}{default};'.format(
n=var_name,
t=var_type.ReferencedTypeName.replace('-','_'),
default=' := ' + dstr if def_value else ''))
# Flatten the nested states, add states to the process state list # Flatten the nested states, add states to the process state list
# Requires renaming of nested states and setting chaining of transitions # Requires renaming of nested states and setting chaining of transitions
def update_terminator(context, term, process): def update_terminator(context, term, process):
...@@ -144,9 +130,18 @@ def _process(process): ...@@ -144,9 +130,18 @@ def _process(process):
to process, updating indexes, and update terminators to process, updating indexes, and update terminators
''' '''
trans_idx = len(process.transitions) trans_idx = len(process.transitions)
state.mapping = {state.statename + '_' + key:state.mapping.pop(key) prefix = state.statename + '_'
state.mapping = {prefix + key:state.mapping.pop(key)
for key in state.mapping.keys()} for key in state.mapping.keys()}
process.transitions.extend(state.transitions) process.transitions.extend(state.transitions)
# Add prefix to local variable names and push them at process level
for dcl in state.variables.viewkeys():
rename_everything(state.content, dcl, prefix + dcl)
state.variables = {prefix + key: state.variables.pop(key)
for key in state.variables.keys()}
process.variables.update(state.variables)
for key, value in state.mapping.viewitems(): for key, value in state.mapping.viewitems():
# Update transition indices # Update transition indices
if isinstance(value, int): if isinstance(value, int):
...@@ -155,17 +150,60 @@ def _process(process): ...@@ -155,17 +150,60 @@ def _process(process):
for inp in value: for inp in value:
inp.transition_id += trans_idx inp.transition_id += trans_idx
process.mapping.update(state.mapping) process.mapping.update(state.mapping)
# If composite state has entry procedures, add the call
if state.entry_procedure:
for each in (trans for trans in state.mapping.viewvalues()
if isinstance(trans, int)):
call_entry = ogAST.ProcedureCall()
call_entry.inputString = 'entry'
call_entry.output = [{'outputName': prefix + 'entry',
'params': [], 'tmpVars': []}]
process.transitions[each].actions.insert(0, call_entry)
# If composite state has exit procedure, add the call
if state.exit_procedure:
for each in chain(state.transitions, (lab.transition for lab in
state.content.floating_labels)):
if each.terminator.kind == 'return':
call_exit = ogAST.ProcedureCall()
call_exit.inputString = 'exit'
call_exit.output = [{'outputName': prefix + 'exit',
'params': [], 'tmpVars': []}]
each.actions.append(call_exit)
for inner in state.composite_states: for inner in state.composite_states:
inner.statename = state.statename + '_' + inner.statename # Go recursively in inner composite states
inner.statename = prefix + inner.statename
update_composite_state(inner, process) update_composite_state(inner, process)
for each in state.terminators: for each in state.terminators:
# Update state names in terminators and set next transition id # Give prefix to terminators
if each.label:
each.label.inputString = prefix + each.label.inputString
if each.kind == 'next_state': if each.kind == 'next_state':
each.inputString = state.statename + '_' + each.inputString each.inputString = prefix + each.inputString
# Set next transition id
update_terminator(state, each, process) update_terminator(state, each, process)
elif each.kind == 'join':
rename_everything(state.content,
each.inputString,
prefix + each.inputString)
for each in state.labels:
# Give prefix to labels in transitions
rename_everything(state.content,
each.inputString,
prefix + each.inputString)
# Add prefixed floating labels of the composite state to the process
process.content.floating_labels.extend(state.content.floating_labels)
# Rename inner procedures
for each in state.content.inner_procedures:
rename_everything(state.content, each.inputString,
prefix + each.inputString)
each.inputString = prefix + each.inputString
process.content.inner_procedures.extend(state.content.inner_procedures)
def propagate_inputs(nested_state, inputlist): def propagate_inputs(nested_state, inputlist):
''' Nested states: Inputs at level N but be handled at level N-1 ''' Nested states: Inputs at level N must be handled at level N-1
that is, all inputs of a composite states (the ones that allow that is, all inputs of a composite states (the ones that allow
to exit the composite state from the outer scope) must be to exit the composite state from the outer scope) must be
processed by each of the substates. processed by each of the substates.
...@@ -190,6 +228,24 @@ def _process(process): ...@@ -190,6 +228,24 @@ def _process(process):
if each.kind == 'next_state': if each.kind == 'next_state':
update_terminator(process, each, process) update_terminator(process, each, process)
VARIABLES.update(process.variables)
INNER_PROCEDURES.extend(process.content.inner_procedures)
# Generate the code to declare process-level variables
process_level_decl = []
for var_name, (var_type, def_value) in process.variables.viewitems():
if def_value:
# Expression must be a ground expression, i.e. must not
# require temporary variable to store computed result
dst, dstr, dlocal = generate(def_value)
assert not dst and not dlocal, 'DCL: Expecting a ground expression'
process_level_decl.append(
'l_{n} : aliased asn1Scc{t}{default};'.format(
n=var_name,
t=var_type.ReferencedTypeName.replace('-','_'),
default=' := ' + dstr if def_value else ''))
# Add the process states list to the process-level variables # Add the process states list to the process-level variables
states_decl = 'type states is (' states_decl = 'type states is ('
states_decl += ', '.join(name for name in process.mapping.iterkeys() states_decl += ', '.join(name for name in process.mapping.iterkeys()
...@@ -437,7 +493,7 @@ package {process_name} is'''.format(process_name=process_name, ...@@ -437,7 +493,7 @@ package {process_name} is'''.format(process_name=process_name,
# Add the code for the floating labels # Add the code for the floating labels
taste_template.extend(code_labels) taste_template.extend(code_labels)
if code_labels: #if code_labels:
taste_template.append('<<next_transition>>') taste_template.append('<<next_transition>>')
taste_template.append('null;') taste_template.append('null;')
taste_template.append('end loop;') taste_template.append('end loop;')
...@@ -587,14 +643,17 @@ def _call_external_function(output): ...@@ -587,14 +643,17 @@ def _call_external_function(output):
code.extend(p_code) code.extend(p_code)
local_decl.extend(p_local) local_decl.extend(p_local)
# Create a temporary variable for input parameters only # Create a temporary variable for input parameters only
if param_direction == 'in': # (If needed, i.e. if argument is not a local variable)
if param_direction == 'in' \
and (not (isinstance(param, ogAST.PrimVariable) and
p_id.startswith('l_')) or isinstance(param, ogAST.PrimFPAR)):
tmp_id = out['tmpVars'][idx] tmp_id = out['tmpVars'][idx]
local_decl.append('tmp{idx} : aliased asn1Scc{oType};' local_decl.append('tmp{idx} : aliased asn1Scc{oType};'
.format(idx=tmp_id, oType=typename)) .format(idx=tmp_id, oType=typename))
code.append('tmp{idx} := {p_id};'.format( code.append('tmp{idx} := {p_id};'
idx=tmp_id, p_id=p_id)) .format(idx=tmp_id, p_id=p_id))
list_of_params.append("tmp{idx}'access". list_of_params.append("tmp{idx}'access"
format(idx=out['tmpVars'][idx])) .format(idx=out['tmpVars'][idx]))
else: else:
# Output parameters - no need for a temp variable # Output parameters - no need for a temp variable
list_of_params.append("{var}'access".format(var=p_id)) list_of_params.append("{var}'access".format(var=p_id))
...@@ -1228,10 +1287,9 @@ def _decision(dec): ...@@ -1228,10 +1287,9 @@ def _decision(dec):
actual_type = getattr( actual_type = getattr(
question_type, 'ReferencedTypeName', None) or question_type.kind question_type, 'ReferencedTypeName', None) or question_type.kind
actual_type = actual_type.replace('-', '_') actual_type = actual_type.replace('-', '_')
basic = False basic = find_basic_type(question_type).kind in ('IntegerType',
if actual_type in ('IntegerType', 'Integer32Type', 'BooleanType', 'Integer32Type', 'BooleanType',
'RealType', 'EnumeratedType', 'ChoiceEnumeratedType'): 'RealType', 'EnumeratedType', 'ChoiceEnumeratedType')
basic = True
# for ASN.1 types, declare a local variable # for ASN.1 types, declare a local variable
# to hold the evaluation of the question # to hold the evaluation of the question
if not basic: if not basic:
...@@ -1246,6 +1304,7 @@ def _decision(dec): ...@@ -1246,6 +1304,7 @@ def _decision(dec):
code.append('tmp{idx} := {q};'.format(idx=dec.tmpVar, q=q_str)) code.append('tmp{idx} := {q};'.format(idx=dec.tmpVar, q=q_str))
sep = 'if ' sep = 'if '
for a in dec.answers: for a in dec.answers:
code.extend(traceability(a))
if a.kind in ('open_range', 'constant'): if a.kind in ('open_range', 'constant'):
# Note: removed and a.transition here because empty transitions # Note: removed and a.transition here because empty transitions
# have a different meaning, and a "null;" statement has to be # have a different meaning, and a "null;" statement has to be
...@@ -1261,7 +1320,7 @@ def _decision(dec): ...@@ -1261,7 +1320,7 @@ def _decision(dec):
exp = 'not ' + exp exp = 'not ' + exp
else: else:
exp = 'tmp{idx} {op} {ans}'.format(idx=dec.tmpVar, exp = 'tmp{idx} {op} {ans}'.format(idx=dec.tmpVar,
op=OPERANDS[a.openRangeOp], ans=ans_str) op=a.openRangeOp.operand, ans=ans_str)
else: else:
exp = '{q} {op} {ans}'.format(q=q_str, exp = '{q} {op} {ans}'.format(q=q_str,
op=a.openRangeOp.operand, op=a.openRangeOp.operand,
...@@ -1334,6 +1393,7 @@ def _transition(tr): ...@@ -1334,6 +1393,7 @@ def _transition(tr):
if tr.terminator.next_id == -1: if tr.terminator.next_id == -1:
code.append('state := {nextState};'.format( code.append('state := {nextState};'.format(
nextState=tr.terminator.inputString)) nextState=tr.terminator.inputString))
code.append('goto next_transition;')
elif tr.terminator.kind == 'join': elif tr.terminator.kind == 'join':
code.append('goto {label};'.format( code.append('goto {label};'.format(
label=tr.terminator.inputString)) label=tr.terminator.inputString))
...@@ -1348,9 +1408,11 @@ def _transition(tr): ...@@ -1348,9 +1408,11 @@ def _transition(tr):
(tr.terminator.return_expr) (tr.terminator.return_expr)
code.extend(stmts) code.extend(stmts)
local_decl.extend(local) local_decl.extend(local)
code.append('return{};'.format(' ' + string if string else '')) code.append('return{};'
.format(' ' + string if string else ''))
else: else:
code.append('trId := ' + str(tr.terminator.next_id) + ';') code.append('trId := ' + str(tr.terminator.next_id) + ';')
code.append('goto next_transition;')
if empty_transition: if empty_transition:
# If transition does not have any statement, generate an Ada 'null;' # If transition does not have any statement, generate an Ada 'null;'
code.append('null;') code.append('null;')
...@@ -1578,6 +1640,144 @@ def find_labels(trans): ...@@ -1578,6 +1640,144 @@ def find_labels(trans):
yield new_fl yield new_fl
@singledispatch
def rename_everything(ast, from_name, to_name):
'''
Rename in all symbols all occurences of name_ref into new_name.
This is used to avoid name clashes in nested diagrams when they get
flattened. For example rename all accesses to a variable declared
in the scope of a composite state, so that they do not overwrite
a variable with the same name declared at a higher scope.
'''
pass
@rename_everything.register(ogAST.Automaton)
def _rename_automaton(ast, from_name, to_name):
''' Renaming at Automaton top level (content of digragrams) '''
transitions = []
if ast.start:
rename_everything(ast.start.transition, from_name, to_name)
for each in ast.named_start:
rename_everything(each.transition, from_name, to_name)
for each in ast.floating_labels:
rename_everything(each, from_name, to_name)
for each in ast.states:
for inp in each.inputs:
rename_everything(inp.transition, from_name, to_name)
for idx, param in enumerate(inp.parameters):
if param.lower() == from_name.lower():
inp.parameter[idx] = to_name
if each.composite:
rename_everything(each.composite.content, from_name, to_name)
for each in ast.inner_procedures:
# Check that from_name is not a redefined variable in the procedure
for varname in each.variables.viewkeys():
if varname.lower() == from_name:
break
else:
rename_everything(each.content, from_name, to_name)
@rename_everything.register(ogAST.Output)
@rename_everything.register(ogAST.ProcedureCall)
def _rename_output(ast, from_name, to_name):
''' Rename actual parameter names in output/procedure calls
and possibly the procedure name '''
for each in ast.output:
if each['outputName'].lower() == from_name.lower():
each['outputName'] = to_name
for param in each['params']:
rename_everything(param, from_name, to_name)
@rename_everything.register(ogAST.Label)
def _rename_label(ast, from_name, to_name):
''' Rename elements in the transition following a label '''
if ast.inputString.lower() == from_name.lower():
ast.inputString = to_name
rename_everything(ast.transition, from_name, to_name)
@rename_everything.register(ogAST.Decision)
def _rename_decision(ast, from_name, to_name):
''' Rename elements in decision '''
if ast.kind == 'question':
rename_everything(ast.question)
for each in ast.answers:
rename_everything(each, from_name, to_name)
@rename_everything.register(ogAST.Answer)
def _rename_answer(ast, from_name, to_name):
''' Rename elements in an answer branch '''
if ast.kind in ('constant', 'open_range'):
rename_everything(ast.constant, from_name, to_name)
elif ast.kind == 'closed_range':
pass # TODO when supported
rename_everything(ast.transition, from_name, to_name)
@rename_everything.register(ogAST.Transition)
def _rename_transition(ast, from_name, to_name):
''' Rename in all symbols of a transition '''
for each in chain(ast.actions, ast.terminators):
# Label, output, task, decision, terminators
rename_everything(each, from_name, to_name)
@rename_everything.register(ogAST.Terminator)
def _rename_terminator(ast, from_name, to_name):
''' Rename terminators: join/labels, next_state '''
if ast.inputString.lower() == from_name.lower():
ast.inputString = to_name
rename_everything(ast.label, from_name, to_name)
rename_everything(ast.return_expr, from_name, to_name)
@rename_everything.register(ogAST.TaskAssign)
def _rename_task_assign(ast, from_name, to_name):
''' List of assignments '''
for each in ast.elems:
rename_everything(each, from_name, to_name)
@rename_everything.register(ogAST.TaskForLoop)
def _rename_forloop(ast, from_name, to_name):
''' List of FOR loops '''
for each in ast.elems:
rename_everything(each['list'], from_name, to_name)
rename_everything(each['range']['start'], from_name, to_name)
rename_everything(each['range']['stop'], from_name, to_name)
rename_everything(each['transition'], from_name, to_name)
@rename_everything.register(ogAST.ExprPlus)
@rename_everything.register(ogAST.ExprMul)
@rename_everything.register(ogAST.ExprMinus)
@rename_everything.register(ogAST.ExprEq)
@rename_everything.register(ogAST.ExprNeq)
@rename_everything.register(ogAST.ExprGt)
@rename_everything.register(ogAST.ExprGe)
@rename_everything.register(ogAST.ExprLt)
@rename_everything.register(ogAST.ExprLe)
@rename_everything.register(ogAST.ExprDiv)
@rename_everything.register(ogAST.ExprMod)
@rename_everything.register(ogAST.ExprRem)
@rename_everything.register(ogAST.ExprAssign)
@rename_everything.register(ogAST.ExprOr)
@rename_everything.register(ogAST.ExprIn)
@rename_everything.register(ogAST.ExprAnd)
@rename_everything.register(ogAST.ExprXor)
@rename_everything.register(ogAST.ExprAppend)
@rename_everything.register(ogAST.ExprAssign)
def _rename_expr(ast, from_name, to_name):
''' Two-sided expressions '''
rename_everything(ast.left, from_name, to_name)
rename_everything(ast.right, from_name, to_name)
@rename_everything.register(ogAST.PrimPath)
@rename_everything.register(ogAST.PrimVariable)
def _rename_path(ast, from_name, to_name):
''' Ultimate seek point for the renaming: primary path/variables '''
if ast.value[0].lower() == from_name.lower():
ast.value[0] = to_name
def format_ada_code(stmts): def format_ada_code(stmts):
''' Indent properly the Ada code ''' ''' Indent properly the Ada code '''
indent = 0 indent = 0
......
...@@ -90,7 +90,8 @@ def copy_branch(top_level_item): ...@@ -90,7 +90,8 @@ def copy_branch(top_level_item):
(term.pos_x, term.pos_y, term.width, term.height).center()) (term.pos_x, term.pos_y, term.width, term.height).center())
for symbol in symbols: for symbol in symbols:
if (isinstance(symbol, sdlSymbols.State) and [c for c in if (isinstance(symbol, sdlSymbols.State) and [c for c in
symbol.childSymbols() if isinstance(c, sdlSymbols.Input)]): symbol.childSymbols() if isinstance(c, (sdlSymbols.Input,
sdlSymbols.Connect))]):
term_branch, term_inators = copy_branch(symbol) term_branch, term_inators = copy_branch(symbol)
branch.extend(term_branch) branch.extend(term_branch)
res_terminators.extend(term_inators) res_terminators.extend(term_inators)
......
This diff is collapsed.
...@@ -101,18 +101,19 @@ def _automaton(ast, scene): ...@@ -101,18 +101,19 @@ def _automaton(ast, scene):
top_level_symbols.append(render(label, scene, ast.states)) top_level_symbols.append(render(label, scene, ast.states))
# Render floating states # Render floating states
nested_states = {} nested_states = []
for state in ast.states: for state in ast.states:
# Create only floating states # Create only floating states
try: try:
new_state = render(state, scene=scene, states=ast.states, new_state = render(state, scene=scene, states=ast.states,
terminators=ast.parent.terminators) terminators=ast.parent.terminators)
if new_state.nested_scene: if new_state.nested_scene:
if str(new_state).lower() in nested_states.viewkeys(): if str(new_state).lower() in nested_states: #.viewkeys():
new_state.nested_scene = None new_state.nested_scene = None
else: else:
nested_states[str(new_state).lower()] = \ nested_states.append(str(new_state).lower())
new_state.nested_scene #nested_states[str(new_state).lower()] = \
# new_state.nested_scene
except TypeError: except TypeError:
# Discard terminators (see _state function for explanation) # Discard terminators (see _state function for explanation)
pass pass
...@@ -171,8 +172,7 @@ def _start(ast, scene, states, parent=None): ...@@ -171,8 +172,7 @@ def _start(ast, scene, states, parent=None):
start_symbol = sdlSymbols.Start(ast) start_symbol = sdlSymbols.Start(ast)
scene.addItem(start_symbol) scene.addItem(start_symbol)
if ast.transition: if ast.transition:
render(ast.transition, render(ast.transition, scene=scene, parent=start_symbol, states=states)
scene=scene, parent=start_symbol, states=states)
return start_symbol return start_symbol
...@@ -195,8 +195,7 @@ def _procedure_start(ast, scene, states, parent=None): ...@@ -195,8 +195,7 @@ def _procedure_start(ast, scene, states, parent=None):
start_symbol = sdlSymbols.ProcedureStart(ast) start_symbol = sdlSymbols.ProcedureStart(ast)
scene.addItem(start_symbol) scene.addItem(start_symbol)
if ast.transition: if ast.transition:
render(ast.transition, render(ast.transition, scene=scene, parent=start_symbol, states=states)
scene=scene, parent=start_symbol, states=states)
return start_symbol return start_symbol
...@@ -209,24 +208,19 @@ def _floating_label(ast, scene, states, parent=None): ...@@ -209,24 +208,19 @@ def _floating_label(ast, scene, states, parent=None):
scene.addItem(lab) scene.addItem(lab)
lab.setPos(ast.pos_x, ast.pos_y) lab.setPos(ast.pos_x, ast.pos_y)
if ast.transition: if ast.transition:
render(ast.transition, render(ast.transition, scene=scene, parent=lab, states=states)
scene=scene,
parent=lab,
states=states)
return lab return lab
@render.register(ogAST.Transition) @render.register(ogAST.Transition)
def _transition(ast, scene, parent, states): def _transition(ast, scene, parent, states):
''' Add a transition to a scene ''' ''' Add a transition to a scene '''
for action_symbol in ast.actions: for each in ast.actions:
# pylint: disable=E1111 # pylint: disable=E1111
parent = render(action_symbol, parent = render(each, scene=scene, parent=parent, states=states)
scene=scene, parent=parent, states=states)
if ast.terminator: if ast.terminator:
render(ast.terminator, render(ast.terminator, scene=scene, parent=parent, states=states)
scene=scene, parent=parent, states=states)
@render.register(ogAST.Comment) @render.register(ogAST.Comment)
...@@ -296,8 +290,7 @@ def _terminator(ast, scene, parent, states): ...@@ -296,8 +290,7 @@ def _terminator(ast, scene, parent, states):
''' Create a TERMINATOR symbol ''' ''' Create a TERMINATOR symbol '''
if ast.label: if ast.label:
# pylint: disable=E1111 # pylint: disable=E1111
parent = render(ast.label, parent = render(ast.label,scene=scene, parent=parent, states=states)
scene=scene, parent=parent, states=states)
if ast.kind == 'next_state': if ast.kind == 'next_state':
LOG.debug('ADDING NEXT_STATE ' + ast.inputString) LOG.debug('ADDING NEXT_STATE ' + ast.inputString)
# Create a new state symbol # Create a new state symbol
...@@ -309,9 +302,11 @@ def _terminator(ast, scene, parent, states): ...@@ -309,9 +302,11 @@ def _terminator(ast, scene, parent, states):
state_ast.pos_x == ast.pos_x and state_ast.pos_x == ast.pos_x and
state_ast.pos_y == ast.pos_y): state_ast.pos_y == ast.pos_y):
LOG.debug('MERGING TERMINATOR "' + ast.inputString + '"') LOG.debug('MERGING TERMINATOR "' + ast.inputString + '"')
for input_ast in state_ast.inputs: symbol.nested_scene = state_ast.composite or \
render(input_ast, ogAST.CompositeState()
scene=scene, parent=symbol, states=states) for each in chain(state_ast.inputs, state_ast.connects):
render(each, scene=scene, parent=symbol, states=states)
break
elif ast.kind == 'join': elif ast.kind == 'join':
symbol = sdlSymbols.Join(parent, ast) symbol = sdlSymbols.Join(parent, ast)
elif ast.kind in ('return', 'stop'): elif ast.kind in ('return', 'stop'):