Commit 418db90d authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Full support of composite states

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,9 +493,9 @@ package {process_name} is'''.format(process_name=process_name,
# Add the code for the floating labels
taste_template.extend(code_labels)
if code_labels:
taste_template.append('<<next_transition>>')
taste_template.append('null;')
#if code_labels:
taste_template.append('<<next_transition>>')
taste_template.append('null;')
taste_template.append('end loop;')
taste_template.append('end runTransition;')
taste_template.append('\n')
......@@ -1334,6 +1390,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 +1405,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 +1637,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
......
This diff is collapsed.
......@@ -603,8 +603,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:
......@@ -1349,7 +1349,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':
......
......@@ -133,8 +133,8 @@ ACTIONS = {
Comment, Label, Join, ProcedureStop],
'statechart': [],
'state': [StateStart, State, Input, Connect, Task, Decision,
DecisionAnswer, Output,
ProcedureCall, TextSymbol, Comment, Label, Join, ProcedureStop]
DecisionAnswer, Output, ProcedureCall, TextSymbol, Comment,
Label, Join, ProcedureStop, Procedure]
}
......@@ -351,21 +351,18 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
def render_process(self, process):
''' Render a process and its inner procedures in the scene '''
''' Render a process and its children scenes, recursively '''
self.process_name = process.processName or 'opengeode'
for top_level in Renderer.render(process, scene=self):
G_SYMBOLS.add(top_level)
# Render optional sub-scenes (procedures)
if top_level.nested_scene and not isinstance(
top_level.nested_scene, SDL_Scene):
subscene = SDL_Scene(
context=top_level.__class__.__name__.lower())
subscene.messages_window = self.messages_window
for sub_top_level in Renderer.render(
top_level.nested_scene.content,
scene=subscene):
G_SYMBOLS.add(sub_top_level)
top_level.nested_scene = subscene
def recursive_render(content, dest_scene):
for item in Renderer.render(content, dest_scene):
G_SYMBOLS.add(item)
if item.nested_scene:
subscene = SDL_Scene(
context=item.__class__.__name__.lower())
subscene.messages_window = self.messages_window
recursive_render(item.nested_scene.content, subscene)
item.nested_scene = subscene
recursive_render(process, self)
def refresh(self):
......@@ -1124,6 +1121,7 @@ class SDL_View(QtGui.QGraphicsView, object):
pen=QtGui.QPen(QtGui.QColor(0, 0, 0, 0)))
# Hide the rectangle so that it does not collide with the symbols
self.phantom_rect.hide()
self.refresh()
super(SDL_View, self).resizeEvent(event)
def about_og(self):
......@@ -1206,10 +1204,11 @@ class SDL_View(QtGui.QGraphicsView, object):
subscene = SDL_Scene(
context=item.__class__.__name__.lower())
subscene.messages_window = self.messages_window
if item.nested_scene:
for top_level in Renderer.render_process(
subscene, item.nested_scene):
G_SYMBOLS.add(top_level)
# if item.nested_scene:
# Removed - cannot happen, scenes are created by SDL_Scene.render_process
# for top_level in Renderer.render(
# item.nested_scene.content, subscene):
# G_SYMBOLS.add(top_level)
item.nested_scene = subscene
self.go_down(item.nested_scene)
else:
......
......@@ -693,8 +693,8 @@ actual_parameters
task
: cif?
hyperlink?
TASK task_body end
-> ^(TASK cif? hyperlink? end? task_body);
TASK task_body? end
-> ^(TASK cif? hyperlink? end? task_body?);
task_body
......
......@@ -45,7 +45,7 @@ SDL_BLACKBOLD = ['\\b{word}\\b'.format(word=word) for word in (
'EXPONENT', 'TRUE', 'FALSE', 'MOD', 'FI', 'WRITE', 'WRITELN',
'LENGTH', 'PRESENT', 'FPAR', 'TODO', 'FIXME', 'XXX',
'CHECKME', 'PROCEDURE', 'EXTERNAL', 'IN', 'OUT', 'TIMER',
'SET_TIMER', 'RESET_TIMER', 'VIA')]
'SET_TIMER', 'RESET_TIMER', 'VIA', 'ENTRY', 'EXIT')]
SDL_REDBOLD = ['\\b{word}\\b'.format(word=word) for word in (
'INPUT', 'OUTPUT', 'STATE', 'DECISION', 'NEXTSTATE',
......
......@@ -6,7 +6,11 @@ generate-code:
compile: generate-code
asn1.exe -Ada dataview-uniq.asn -typePrefix asn1Scc -equal
gnatgcc -c challenge.adb && echo 'All OK!'
gnatgcc -c *.adb
gcc -c test.c
gcc -o test test.o challenge.o taste_dataview.o taste_basictypes.o -lgnat -lm
./test && echo 'All OK!'
parse:
../../../opengeode.py challenge.pr system_structure.pr --check
......@@ -15,4 +19,4 @@ coverage:
coverage run -p ../../../opengeode.py challenge.pr system_structure.pr --toAda
clean:
rm -rf *.adb *.ads *.pyc runSpark.sh spark.idx *.o *.ali gnat.cfg examiner bin *.wrn *.gpr
rm -rf *.adb *.ads *.pyc runSpark.sh spark.idx *.o *.ali gnat.cfg examiner bin *.wrn *.gpr test
PROCESS challenge;
STATE on;
STATE ON;
SUBSTRUCTURE
in (via_toto);
out (ret0);
/* CIF TEXT (-642, 222), (334, 95) */
dcl myresult T_UInt8 := 4;
-- Use a variable with the same name as a variable
-- in the outer scope, to check that code generators
-- use the proper one
dcl result T_UInt32 := 9;
/* CIF ENDTEXT */
/* CIF PROCEDURE (-326, 114), (70, 35) */
PROCEDURE exit
/* CIF COMMENT (-236, 90), (157, 93) */
COMMENT 'Special exit
procedure - called
automatically when
leaving the nested
state';
/* CIF START (163, 119), (70, 35) */
START;
/* CIF PROCEDURECALL (68, 169), (259, 35) */
CALL writeln('LEAVING the nested state');
/* CIF RETURN (180, 219), (35, 35) */
RETURN ;
ENDPROCEDURE;
/* CIF PROCEDURE (-645, 493), (102, 35) */
PROCEDURE inner_proc;
/* CIF TEXT (31, 41), (364, 93) */
fpar in toto T_UInt8;
-- Redefine a variable declared in outer scope
dcl result t_Boolean := true;
/* CIF ENDTEXT */
/* CIF START (479, 97), (70, 35) */
START;
/* CIF LABEL (471, 147), (86, 35) */
hey_joe:
/* CIF PROCEDURECALL (369, 197), (289, 35) */
CALL writeln('Procedure call with toto=', toto);
/* CIF TASK (453, 247), (121, 35) */
TASK result := false;
/* CIF LABEL (473, 297), (82, 35) */
leaving:
/* CIF RETURN (496, 347), (35, 35) */
RETURN ;
ENDPROCEDURE;
/* CIF PROCEDURE (-330, 25), (73, 35) */
PROCEDURE entry
/* CIF COMMENT (-241, 2), (190, 83) */
COMMENT 'Special Entry
procedure - called
automatically upon
entrance to the nested
state';
/* CIF START (191, 78), (70, 35) */
START;
/* CIF PROCEDURECALL (100, 128), (251, 35) */
CALL writeln('ENTERING NESTED STATE');
/* CIF RETURN (208, 178), (35, 35) */
RETURN ;
ENDPROCEDURE;
/* CIF START (304, -22), (89, 35) */
START via_toto ;
/* CIF PROCEDURECALL (213, 28), (271, 35) */
......@@ -13,21 +71,59 @@ RETURN ret0;
START;
/* CIF PROCEDURECALL (-10, 29), (199, 35) */
CALL writeln('start without via');
/* CIF NEXTSTATE (54, 79), (70, 35) */
/* CIF LABEL (33, 79), (111, 35) */
inside_label:
/* CIF PROCEDURECALL (20, 129), (137, 35) */
CALL pow(3,3, result);
/* CIF DECISION (52, 179), (74, 50) */
DECISION result;
/* CIF ANSWER (-23, 249), (92, 23) */
(myresult):
/* CIF ANSWER (101, 249), (70, 23) */
ELSE:
/* CIF TASK (78, 287), (115, 35) */
TASK myresult := 5;
ENDDECISION;
/* CIF PROCEDURECALL (-47, 337), (272, 35) */
CALL writeln('going to internal hello state');
/* CIF LABEL (45, 387), (88, 35) */
nslabel:
/* CIF NEXTSTATE (54, 437), (70, 35) */
NEXTSTATE hello;
/* CIF STATE (-208, -29), (70, 35) */
/* CIF LABEL (-752, 295), (90, 35) */
CONNECTION to_label:
/* CIF JOIN (-725, 345), (35, 35) */
JOIN another_floating;
/* CIF End Label */
ENDCONNECTION;
/* CIF LABEL (-372, 443), (141, 35) */
CONNECTION another_floating:
/* CIF TASK (-359, 493), (115, 35) */
TASK myresult := 1;
/* CIF PROCEDURECALL (-383, 543), (164, 35) */
CALL inner_proc(myresult);
/* CIF RETURN (-319, 593), (35, 35) */
RETURN ;
/* CIF End Label */
ENDCONNECTION;
/* CIF STATE (-884, -29), (70, 35) */
STATE hello;
/* CIF INPUT (-208, 32), (70, 35) */
/* CIF INPUT (-884, 32), (70, 35) */
INPUT run;
/* CIF PROCEDURECALL (-349, 82), (353, 35) */
CALL writeln('Bye substate, leaving with no return value');
/* CIF RETURN (-190, 132), (35, 35) */
RETURN ;
/* CIF PROCEDURECALL (-1047, 82), (395, 35) */
CALL writeln('Bye substate, leaving with no return value', result);
/* CIF TASK (-912, 132), (125, 53) */
TASK myresult := 88,
result := 33;
/* CIF JOIN (-867, 200), (35, 35) */
JOIN to_label;
ENDSTATE;
ENDSUBSTRUCTURE;
/* CIF TEXT (-486, -40), (298, 56) */
-- This system tests nested states
dcl result T_uint32 :=0;
/* CIF ENDTEXT */
/* CIF PROCEDURE (-992, 214), (66, 35) */
PROCEDURE toto;
......@@ -54,9 +150,11 @@ ELSE:
RETURN ;
ENDDECISION;
ENDPROCEDURE;
/* CIF START (-842, 59), (68, 37) */
/* CIF START (-857, 59), (68, 37) */
START;
/* CIF NEXTSTATE (-843, 111), (70, 35) */
/* CIF PROCEDURECALL (-935, 111), (225, 35) */
CALL writeln('[STARTUP] Going OFF');
/* CIF NEXTSTATE (-858, 161), (70, 35) */
NEXTSTATE OFF;
......@@ -64,11 +162,15 @@ NEXTSTATE OFF;
STATE Safe;
/* CIF INPUT (-487, 293), (70, 35) */
INPUT *;
/* CIF NEXTSTATE (-487, 343), (70, 35) */
NEXTSTATE Off;
/* CIF INPUT (-406, 293), (88, 35) */
/* CIF PROCEDURECALL (-517, 343), (130, 35) */
CALL pow(2,2, result);
/* CIF PROCEDURECALL (-583, 393), (262, 35) */
CALL writeln('Result of pow(2,2) = ', result);
/* CIF NEXTSTATE (-487, 443), (70, 35) */
NEXTSTATE -;
/* CIF INPUT (-313, 293), (88, 35) */
INPUT any_one;
/* CIF NEXTSTATE (-397, 343), (70, 35) */