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

Minor fixes

parent f1746aa7
......@@ -69,6 +69,8 @@
import logging
from itertools import chain
from singledispatch import singledispatch
import ogAST
......@@ -98,7 +100,6 @@ def generate(ast):
def _process(process):
''' Generate the code for a complete process (AST Top level) '''
process_name = process.processName
VARIABLES.update(process.variables)
global TYPES
TYPES = process.dataview
del OUT_SIGNALS[:]
......@@ -106,24 +107,9 @@ def _process(process):
del INNER_PROCEDURES[:]
OUT_SIGNALS.extend(process.output_signals)
PROCEDURES.extend(process.procedures)
INNER_PROCEDURES.extend(process.content.inner_procedures)
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
# Requires renaming of nested states and setting chaining of transitions
def update_terminator(context, term, process):
......@@ -144,9 +130,18 @@ def _process(process):
to process, updating indexes, and update terminators
'''
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()}
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():
# Update transition indices
if isinstance(value, int):
......@@ -155,17 +150,60 @@ def _process(process):
for inp in value:
inp.transition_id += trans_idx
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:
inner.statename = state.statename + '_' + inner.statename
# Go recursively in inner composite states
inner.statename = prefix + inner.statename
update_composite_state(inner, process)
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':
each.inputString = state.statename + '_' + each.inputString
each.inputString = prefix + each.inputString
# Set next transition id
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):
''' 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
to exit the composite state from the outer scope) must be
processed by each of the substates.
......@@ -190,6 +228,24 @@ def _process(process):
if each.kind == 'next_state':
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
states_decl = 'type states is ('
states_decl += ', '.join(name for name in process.mapping.iterkeys()
......@@ -437,7 +493,7 @@ package {process_name} is'''.format(process_name=process_name,
# Add the code for the floating labels
taste_template.extend(code_labels)
if code_labels:
#if code_labels:
taste_template.append('<<next_transition>>')
taste_template.append('null;')
taste_template.append('end loop;')
......@@ -587,14 +643,17 @@ def _call_external_function(output):
code.extend(p_code)
local_decl.extend(p_local)
# 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]
local_decl.append('tmp{idx} : aliased asn1Scc{oType};'
.format(idx=tmp_id, oType=typename))
code.append('tmp{idx} := {p_id};'.format(
idx=tmp_id, p_id=p_id))
list_of_params.append("tmp{idx}'access".
format(idx=out['tmpVars'][idx]))
code.append('tmp{idx} := {p_id};'
.format(idx=tmp_id, p_id=p_id))
list_of_params.append("tmp{idx}'access"
.format(idx=out['tmpVars'][idx]))
else:
# Output parameters - no need for a temp variable
list_of_params.append("{var}'access".format(var=p_id))
......@@ -1228,10 +1287,9 @@ def _decision(dec):
actual_type = getattr(
question_type, 'ReferencedTypeName', None) or question_type.kind
actual_type = actual_type.replace('-', '_')
basic = False
if actual_type in ('IntegerType', 'Integer32Type', 'BooleanType',
'RealType', 'EnumeratedType', 'ChoiceEnumeratedType'):
basic = True
basic = find_basic_type(question_type).kind in ('IntegerType',
'Integer32Type', 'BooleanType',
'RealType', 'EnumeratedType', 'ChoiceEnumeratedType')
# for ASN.1 types, declare a local variable
# to hold the evaluation of the question
if not basic:
......@@ -1246,6 +1304,7 @@ def _decision(dec):
code.append('tmp{idx} := {q};'.format(idx=dec.tmpVar, q=q_str))
sep = 'if '
for a in dec.answers:
code.extend(traceability(a))
if a.kind in ('open_range', 'constant'):
# Note: removed and a.transition here because empty transitions
# have a different meaning, and a "null;" statement has to be
......@@ -1261,7 +1320,7 @@ def _decision(dec):
exp = 'not ' + exp
else:
exp = 'tmp{idx} {op} {ans}'.format(idx=dec.tmpVar,
op=OPERANDS[a.openRangeOp], ans=ans_str)
op=a.openRangeOp.operand, ans=ans_str)
else:
exp = '{q} {op} {ans}'.format(q=q_str,
op=a.openRangeOp.operand,
......@@ -1334,6 +1393,7 @@ def _transition(tr):
if tr.terminator.next_id == -1:
code.append('state := {nextState};'.format(
nextState=tr.terminator.inputString))
code.append('goto next_transition;')
elif tr.terminator.kind == 'join':
code.append('goto {label};'.format(
label=tr.terminator.inputString))
......@@ -1348,9 +1408,11 @@ def _transition(tr):
(tr.terminator.return_expr)
code.extend(stmts)
local_decl.extend(local)
code.append('return{};'.format(' ' + string if string else ''))
code.append('return{};'
.format(' ' + string if string else ''))
else:
code.append('trId := ' + str(tr.terminator.next_id) + ';')
code.append('goto next_transition;')
if empty_transition:
# If transition does not have any statement, generate an Ada 'null;'
code.append('null;')
......@@ -1578,6 +1640,144 @@ def find_labels(trans):
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):
''' Indent properly the Ada code '''
indent = 0
......
......@@ -90,7 +90,8 @@ def copy_branch(top_level_item):
(term.pos_x, term.pos_y, term.width, term.height).center())
for symbol in symbols:
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)
branch.extend(term_branch)
res_terminators.extend(term_inators)
......
This diff is collapsed.
......@@ -101,18 +101,19 @@ def _automaton(ast, scene):
top_level_symbols.append(render(label, scene, ast.states))
# Render floating states
nested_states = {}
nested_states = []
for state in ast.states:
# Create only floating states
try:
new_state = render(state, scene=scene, states=ast.states,
terminators=ast.parent.terminators)
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
else:
nested_states[str(new_state).lower()] = \
new_state.nested_scene
nested_states.append(str(new_state).lower())
#nested_states[str(new_state).lower()] = \
# new_state.nested_scene
except TypeError:
# Discard terminators (see _state function for explanation)
pass
......@@ -171,8 +172,7 @@ def _start(ast, scene, states, parent=None):
start_symbol = sdlSymbols.Start(ast)
scene.addItem(start_symbol)
if ast.transition:
render(ast.transition,
scene=scene, parent=start_symbol, states=states)
render(ast.transition, scene=scene, parent=start_symbol, states=states)
return start_symbol
......@@ -195,8 +195,7 @@ def _procedure_start(ast, scene, states, parent=None):
start_symbol = sdlSymbols.ProcedureStart(ast)
scene.addItem(start_symbol)
if ast.transition:
render(ast.transition,
scene=scene, parent=start_symbol, states=states)
render(ast.transition, scene=scene, parent=start_symbol, states=states)
return start_symbol
......@@ -209,24 +208,19 @@ def _floating_label(ast, scene, states, parent=None):
scene.addItem(lab)
lab.setPos(ast.pos_x, ast.pos_y)
if ast.transition:
render(ast.transition,
scene=scene,
parent=lab,
states=states)
render(ast.transition, scene=scene, parent=lab, states=states)
return lab
@render.register(ogAST.Transition)
def _transition(ast, scene, parent, states):
''' Add a transition to a scene '''
for action_symbol in ast.actions:
for each in ast.actions:
# pylint: disable=E1111
parent = render(action_symbol,
scene=scene, parent=parent, states=states)
parent = render(each, scene=scene, parent=parent, states=states)
if ast.terminator:
render(ast.terminator,
scene=scene, parent=parent, states=states)
render(ast.terminator, scene=scene, parent=parent, states=states)
@render.register(ogAST.Comment)
......@@ -296,8 +290,7 @@ def _terminator(ast, scene, parent, states):
''' Create a TERMINATOR symbol '''
if ast.label:
# pylint: disable=E1111
parent = render(ast.label,
scene=scene, parent=parent, states=states)
parent = render(ast.label,scene=scene, parent=parent, states=states)
if ast.kind == 'next_state':
LOG.debug('ADDING NEXT_STATE ' + ast.inputString)
# Create a new state symbol
......@@ -309,9 +302,11 @@ def _terminator(ast, scene, parent, states):
state_ast.pos_x == ast.pos_x and
state_ast.pos_y == ast.pos_y):
LOG.debug('MERGING TERMINATOR "' + ast.inputString + '"')
for input_ast in state_ast.inputs:
render(input_ast,
scene=scene, parent=symbol, states=states)
symbol.nested_scene = state_ast.composite or \
ogAST.CompositeState()
for each in chain(state_ast.inputs, state_ast.connects):
render(each, scene=scene, parent=symbol, states=states)
break
elif ast.kind == 'join':
symbol = sdlSymbols.Join(parent, ast)
elif ast.kind in ('return', 'stop'):
......@@ -338,12 +333,11 @@ def _input(ast, scene, parent, states):
return inp
@render.register(ogAST.Connect)
def _input(ast, scene, parent, states):
def _connect(ast, scene, parent, states):
''' Add connect symbol from the AST to the scene '''
# Note: PROVIDED clause is not supported
conn = sdlSymbols.Connect(parent, ast=ast)
if conn not in scene.items():
scene.addItem(inp)
scene.addItem(conn)
if not parent:
conn.setPos(ast.pos_x, ast.pos_y)
if ast.transition:
......
......@@ -624,8 +624,7 @@ class Symbol(QObject, QGraphicsPathItem, object):
def is_composite(self):
''' Return True if nested scene has something in it '''
try:
return self.allow_nesting and any(item.isVisible()
for item in self._nested_scene.items())
return any(self._nested_scene.visible_symb)
except AttributeError:
return False
......@@ -979,6 +978,10 @@ class Symbol(QObject, QGraphicsPathItem, object):
else:
self.mode = 'Move'
def double_click(self):
''' Handle double click on symbol - redefined at symbol level '''
pass
def mouse_move(self, event):
''' Handle resizing of items - moving is handled in subclass '''
self.updateConnectionPoints()
......
......@@ -173,6 +173,7 @@ class PrimPath(Primary):
is_raw = False
class PrimVariable(PrimPath): pass # XXX should not be raw for codegen
class PrimFPAR(PrimVariable): pass
class PrimEnumeratedValue(Primary): pass
class PrimInteger(Primary): pass
class PrimReal(Primary): pass
......@@ -770,8 +771,12 @@ class CompositeState(Process):
self.exit_procedure = None
# Body can contain text areas, procedures, composite states,
# one nameless START, named START (one per entrypoint), states,
# and floating labels.
# XXX check what to do with local DCL and timers
# amd floating labels
def __repr__(self):
''' Debug output for composite state '''
return 'COMPOSITE STATE {exp} ({l},{c})'.format(exp=self.statename,
l=self.line, c=self.charPositionInLine)
class Block(object):
......
......@@ -320,6 +320,27 @@ def fix_special_operators(op_name, expr_list, context):
and not basic.kind.endswith('StringType'):
# Currently supported printable types
raise TypeError('Write operator does not support type')
elif op_name.lower() == 'set_timer':
if len(expr_list) != 2:
raise TypeError('SET_TIMER has 2 parameters: (int, timer_name)')
basic = find_basic_type(expr_list[0].exprType)
if not basic.kind.startswith('Integer'):
raise TypeError('SET_TIMER first parameter is not an integer')
timer = expr_list[1].inputString
for each in context.timers:
if each.lower() == timer.lower():
break
else:
raise TypeError('Timer {} is not defined'.format(timer))
elif op_name.lower == 'reset_timer':
if len(expr_list) != 1:
raise TypeError('RESET_TIMER has 1 parameter: timer_name')
timer = expr_list[0].inputString
for each in context.timers:
if each.lower() == timer.lower():
break
else:
raise TypeError('Timer {} is not defined'.format(timer))
else:
# TODO: other operators
return
......@@ -603,8 +624,8 @@ def find_variable(var, context):
result = UNKNOWN_TYPE
LOG.debug('[find_variable] checking if ' + str(var) + ' is defined')
# all DCL-variables
all_visible_variables = dict(context.variables)
all_visible_variables.update(context.global_variables)
all_visible_variables = dict(context.global_variables)
all_visible_variables.update(context.variables)
# First check locally, i.e. in FPAR
try:
for variable in context.fpar:
......@@ -746,7 +767,14 @@ def fix_expression_types(expr, context):
try:
#exprType = find_variable(uk_expr.inputString, context)
exprType = find_variable(uk_expr.value[0], context)
setattr(expr, side, ogAST.PrimVariable(primary=uk_expr))
# Differentiate DCL and FPAR variables
use_type = ogAST.PrimVariable
if isinstance(context, ogAST.Procedure):
for each in context.fpar:
if each['name'].lower() == uk_expr.value[0].lower():
use_type = ogAST.PrimFPAR
break
setattr(expr, side, use_type(primary=uk_expr))
getattr(expr, side).exprType = exprType
except AttributeError:
pass
......@@ -1349,7 +1377,7 @@ def composite_state(root, parent=None, context=None):
warnings.extend(warn)
comp.content.textAreas.append(textarea)
elif child.type == lexer.PROCEDURE:
new_proc, err, warn = procedure(child, context=proc)
new_proc, err, warn = procedure(child, context=comp)
errors.extend(err)
warnings.extend(warn)
if new_proc.inputString.strip().lower() == 'entry':
......@@ -1363,8 +1391,7 @@ def composite_state(root, parent=None, context=None):
context=comp)
errors.extend(err)
warnings.extend(warn)