Commit 39c9516f authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Minor fixes on the handling of scopes when using composite states

parent 533f4d8a
...@@ -140,21 +140,25 @@ def _process(process): ...@@ -140,21 +140,25 @@ def _process(process):
process_level_decl.append(states_decl) process_level_decl.append(states_decl)
process_level_decl.append('state : states;') process_level_decl.append('state : states;')
for name, val in process.mapping.viewitems():
if name.endswith('START') and name != 'START':
process_level_decl.append('{name} : constant := {val};'
.format(name=name, val=str(val)))
# Add function allowing to trace current state as a string # Add function allowing to trace current state as a string
process_level_decl.append('function get_state return String;') #process_level_decl.append('function get_state return String;')
process_level_decl.append('pragma export(C, get_state, "{}_state");' #process_level_decl.append('pragma export(C, get_state, "{}_state");'
.format(process_name)) # .format(process_name))
# Add the declaration of the runTransition procedure # Add the declaration of the runTransition procedure
process_level_decl.append('procedure runTransition(Id: Integer);') process_level_decl.append('procedure runTransition(Id: Integer);')
process_level_decl.append('procedure start;') process_level_decl.append('procedure state_start;')
process_level_decl.append('pragma export(C, start, "{}_start");' #process_level_decl.append('pragma export(C, start, "{}_start");'
.format(process_name)) # .format(process_name))
# Generate the code of the start transition: # Generate the code of the start transition:
start_transition = ['begin'] start_transition = ['begin',
start_transition.append('start;') 'runTransition(0);']
mapping = {} mapping = {}
# Generate the code for the transitions in a mapping input-state # Generate the code for the transitions in a mapping input-state
...@@ -216,10 +220,10 @@ package {process_name} is'''.format(process_name=process_name, ...@@ -216,10 +220,10 @@ package {process_name} is'''.format(process_name=process_name,
# Generate the code for the start procedure # Generate the code for the start procedure
taste_template.extend([ taste_template.extend([
'procedure start is', 'procedure state_start is',
'begin', 'begin',
'runTransition(0);', 'null;',
'end start;', '']) 'end state_start;', ''])
# Add the code of the procedures definitions # Add the code of the procedures definitions
taste_template.extend(inner_procedures_code) taste_template.extend(inner_procedures_code)
...@@ -380,11 +384,11 @@ package {process_name} is'''.format(process_name=process_name, ...@@ -380,11 +384,11 @@ package {process_name} is'''.format(process_name=process_name,
taste_template.append('\n') taste_template.append('\n')
# Code of the function allowing to trace current state # Code of the function allowing to trace current state
taste_template.append('function get_state return String is') #taste_template.append('function get_state return String is')
taste_template.append('begin') #taste_template.append('begin')
taste_template.append("return states'Image(state);") #taste_template.append("return states'Image(state);")
taste_template.append('end get_state;') #taste_template.append('end get_state;')
taste_template.append('\n') #taste_template.append('\n')
taste_template.extend(start_transition) taste_template.extend(start_transition)
...@@ -1273,12 +1277,27 @@ def _transition(tr): ...@@ -1273,12 +1277,27 @@ def _transition(tr):
code.append('<<{label}>>'.format( code.append('<<{label}>>'.format(
label=tr.terminator.label.inputString)) label=tr.terminator.label.inputString))
if tr.terminator.kind == 'next_state': if tr.terminator.kind == 'next_state':
code.append('trId := ' + str(tr.terminator.next_id) + ';')
if tr.terminator.inputString.strip() != '-': if tr.terminator.inputString.strip() != '-':
# discard the dash state (remain in the same state) code.append('trId := ' + str(tr.terminator.next_id) + ';')
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))
else:
if any(next_id
for next_id in tr.terminator.candidate_id.viewkeys()
if next_id != -1):
code.append('case state is')
for nid, sta in tr.terminator.candidate_id.viewitems():
if nid != -1:
for each in sta:
code.extend(['when {} =>'.format(each),
'trId := {};'.format(nid)])
code.extend(['when others =>',
'trId := -1;',
'end case;'])
else:
code.append('trId := -1;')
code.append('goto next_transition;') 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(
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
import logging import logging
from itertools import chain from itertools import chain
from collections import defaultdict
from singledispatch import singledispatch from singledispatch import singledispatch
...@@ -59,13 +60,26 @@ def flatten(process): ...@@ -59,13 +60,26 @@ def flatten(process):
if term.inputString.lower() in (st.statename.lower() if term.inputString.lower() in (st.statename.lower()
for st in context.composite_states): for st in context.composite_states):
if not term.via: if not term.via:
term.next_id = process.mapping \ term.next_id = term.inputString.lower() + '_START'
[term.inputString.lower() + '_START'] #process.mapping \
# [term.inputString.lower() + '_START']
else: else:
term.next_id = process.mapping[term.inputString.lower() term.next_id = '{term}_{entry}_START'.format(
+ '_' term=term.inputString, entry=term.entrypoint)
+ term.entrypoint.lower() #process.mapping[term.inputString.lower()
+ '_START'] # + '_'
# + term.entrypoint.lower()
# + '_START']
elif term.inputString.strip() == '-':
term.candidate_id = defaultdict(list)
for each in term.possible_states:
if each in (st.statename.lower()
for st in context.composite_states):
term.candidate_id[each + '_START'] = \
[st for st in process.mapping.viewkeys()
if st.startswith(each) and not st.endswith('_START')]
else:
term.candidate_id[-1].append(each)
def update_composite_state(state, process): def update_composite_state(state, process):
''' Rename inner states, recursively, and add inner transitions ''' Rename inner states, recursively, and add inner transitions
...@@ -73,6 +87,7 @@ def flatten(process): ...@@ -73,6 +87,7 @@ def flatten(process):
''' '''
trans_idx = len(process.transitions) trans_idx = len(process.transitions)
prefix = state.statename + '_' prefix = state.statename + '_'
set_terminator_states(state, prefix)
state.mapping = {prefix + key:state.mapping.pop(key) 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)
...@@ -118,14 +133,15 @@ def flatten(process): ...@@ -118,14 +133,15 @@ def flatten(process):
# Go recursively in inner composite states # Go recursively in inner composite states
inner.statename = prefix + inner.statename inner.statename = prefix + inner.statename
update_composite_state(inner, process) update_composite_state(inner, process)
propagate_inputs(inner, process.mapping[inner.statename]) # TESTME propagate_inputs(inner, process.mapping[inner.statename])
del process.mapping[inner.statename] del process.mapping[inner.statename]
for each in state.terminators: for each in state.terminators:
# Give prefix to terminators # Give prefix to terminators
if each.label: if each.label:
each.label.inputString = prefix + each.label.inputString each.label.inputString = prefix + each.label.inputString
if each.kind == 'next_state': if each.kind == 'next_state':
each.inputString = prefix + each.inputString if each.inputString.strip() != '-':
each.inputString = prefix + each.inputString
# Set next transition id # Set next transition id
update_terminator(context=state, term=each, process=process) update_terminator(context=state, term=each, process=process)
elif each.kind == 'join': elif each.kind == 'join':
...@@ -162,6 +178,17 @@ def flatten(process): ...@@ -162,6 +178,17 @@ def flatten(process):
propagate_inputs(each, nested_state.mapping[each.statename]) propagate_inputs(each, nested_state.mapping[each.statename])
#del nested_state.mapping[each.statename] #del nested_state.mapping[each.statename]
def set_terminator_states(context, prefix=''):
''' Associate state to terminators, needed to process properly
a history nextstates (dash nextstate) in code generators '''
for each in context.content.states:
for inp in each.inputs:
for term in inp.terminators:
term.possible_states.extend(prefix + name.lower()
for name in each.statelist)
set_terminator_states(process)
for each in process.composite_states: for each in process.composite_states:
update_composite_state(each, process) update_composite_state(each, process)
propagate_inputs(each, process.mapping[each.statename]) propagate_inputs(each, process.mapping[each.statename])
...@@ -198,7 +225,7 @@ def _rename_automaton(ast, from_name, to_name): ...@@ -198,7 +225,7 @@ def _rename_automaton(ast, from_name, to_name):
rename_everything(inp.transition, from_name, to_name) rename_everything(inp.transition, from_name, to_name)
for idx, param in enumerate(inp.parameters): for idx, param in enumerate(inp.parameters):
if param.lower() == from_name.lower(): if param.lower() == from_name.lower():
inp.parameter[idx] = to_name inp.parameters[idx] = to_name
if each.composite: if each.composite:
rename_everything(each.composite.content, from_name, to_name) rename_everything(each.composite.content, from_name, to_name)
for each in ast.inner_procedures: for each in ast.inner_procedures:
...@@ -310,6 +337,11 @@ def _rename_path(ast, from_name, to_name): ...@@ -310,6 +337,11 @@ def _rename_path(ast, from_name, to_name):
if ast.value[0].lower() == from_name.lower(): if ast.value[0].lower() == from_name.lower():
ast.value[0] = to_name ast.value[0] = to_name
@rename_everything.register(ogAST.PrimIfThenElse)
def _rename_ifhthenelse(ast, from_name, to_name):
''' Rename expressions in If-Then-Else-Fi construct '''
for expr in ('if', 'then', 'else'):
rename_everything(ast.value[expr], from_name, to_name)
def find_labels(trans): def find_labels(trans):
''' '''
......
...@@ -398,6 +398,9 @@ class Terminator(object): ...@@ -398,6 +398,9 @@ class Terminator(object):
self.entrypoint = None self.entrypoint = None
# some transitions can be chained, when entering/leaving nested states # some transitions can be chained, when entering/leaving nested states
self.next_id = -1 self.next_id = -1
# List of State that can contain the current state
# There can be several if terminator follows a floating label
self.possible_states = []
def __repr__(self): def __repr__(self):
''' Debug output for terminators ''' ''' Debug output for terminators '''
...@@ -467,7 +470,6 @@ class Transition(object): ...@@ -467,7 +470,6 @@ class Transition(object):
# All Terminators of this transition # All Terminators of this transition
self.terminators = [] self.terminators = []
def __repr__(self): def __repr__(self):
''' Debug output: display all actions ''' ''' Debug output: display all actions '''
data = [repr(action) for action in self.actions] data = [repr(action) for action in self.actions]
......
...@@ -31,7 +31,7 @@ import os ...@@ -31,7 +31,7 @@ import os
import importlib import importlib
import logging import logging
import traceback import traceback
from itertools import chain from itertools import chain, permutations
import antlr3 import antlr3
import antlr3.tree import antlr3.tree
...@@ -437,7 +437,7 @@ def check_type_compatibility(primary, typeRef, context): ...@@ -437,7 +437,7 @@ def check_type_compatibility(primary, typeRef, context):
if corr_type: if corr_type:
return return
else: else:
err = ('Value "' + primary.value[0] + err = ('Value "' + primary.inputString +
'" not in this enumeration: ' + '" not in this enumeration: ' +
str(actual_type.EnumValues.keys())) str(actual_type.EnumValues.keys()))
raise TypeError(err) raise TypeError(err)
...@@ -781,7 +781,7 @@ def fix_expression_types(expr, context): ...@@ -781,7 +781,7 @@ def fix_expression_types(expr, context):
# If a side of the expression is of Enumerated of Choice type, check if # If a side of the expression is of Enumerated of Choice type, check if
# the other side is a literal of that sort, and change type accordingly # the other side is a literal of that sort, and change type accordingly
for side in (('left', 'right'), ('right', 'left')): for side in permutations(('left', 'right')):
side_type = find_basic_type(getattr(expr, side[0]).exprType).kind side_type = find_basic_type(getattr(expr, side[0]).exprType).kind
if side_type == 'EnumeratedType': if side_type == 'EnumeratedType':
prim = ogAST.PrimEnumeratedValue(primary=getattr(expr, side[1])) prim = ogAST.PrimEnumeratedValue(primary=getattr(expr, side[1]))
...@@ -796,7 +796,7 @@ def fix_expression_types(expr, context): ...@@ -796,7 +796,7 @@ def fix_expression_types(expr, context):
pass pass
# If a side type remains unknown, check if it is an ASN.1 constant # If a side type remains unknown, check if it is an ASN.1 constant
for side in (('left', 'right'), ('right', 'left')): for side in permutations(('left', 'right')):
value = getattr(expr, side[0]) value = getattr(expr, side[0])
if value.exprType == UNKNOWN_TYPE and is_constant(value): if value.exprType == UNKNOWN_TYPE and is_constant(value):
setattr(expr, side[0], ogAST.PrimConstant(primary=value)) setattr(expr, side[0], ogAST.PrimConstant(primary=value))
...@@ -873,8 +873,8 @@ def fix_expression_types(expr, context): ...@@ -873,8 +873,8 @@ def fix_expression_types(expr, context):
expr.right.value['value'] = check_expr.right expr.right.value['value'] = check_expr.right
elif isinstance(expr.right, ogAST.PrimIfThenElse): elif isinstance(expr.right, ogAST.PrimIfThenElse):
for det in ('then', 'else'): for det in ('then', 'else'):
if expr.right.value[det].exprType == UNKNOWN_TYPE: #if expr.right.value[det].exprType == UNKNOWN_TYPE:
expr.right.value[det].exprType = expr.left.exprType # expr.right.value[det].exprType = expr.left.exprType
# Recursively fix possibly missing types in the expression # Recursively fix possibly missing types in the expression
check_expr = ogAST.ExprAssign() check_expr = ogAST.ExprAssign()
check_expr.left = ogAST.PrimPath() check_expr.left = ogAST.PrimPath()
...@@ -1356,8 +1356,10 @@ def composite_state(root, parent=None, context=None): ...@@ -1356,8 +1356,10 @@ def composite_state(root, parent=None, context=None):
except AttributeError: except AttributeError:
LOG.debug('Procedure context is undefined') LOG.debug('Procedure context is undefined')
# Gather the list of states defined in the composite state # Gather the list of states defined in the composite state
# and map a list of transitions to each state # and map a list of transitionsi to each state
comp.mapping = {name: [] for name in get_state_list(root)} comp.mapping = {name: [] for name in get_state_list(root)}
inner_composite = []
states = []
for child in root.getChildren(): for child in root.getChildren():
if child.type == lexer.ID: if child.type == lexer.ID:
comp.line = child.getLine() comp.line = child.getLine()
...@@ -1388,18 +1390,9 @@ def composite_state(root, parent=None, context=None): ...@@ -1388,18 +1390,9 @@ def composite_state(root, parent=None, context=None):
comp.exit_procedure = new_proc comp.exit_procedure = new_proc
comp.content.inner_procedures.append(new_proc) comp.content.inner_procedures.append(new_proc)
elif child.type == lexer.COMPOSITE_STATE: elif child.type == lexer.COMPOSITE_STATE:
inner_comp, err, warn = composite_state(child, inner_composite.append(child)
parent=None,
context=comp)
errors.extend(err)
warnings.extend(warn)
comp.composite_states.append(inner_comp)
elif child.type == lexer.STATE: elif child.type == lexer.STATE:
# STATE - fills up the 'mapping' structure. states.append(child)
newstate, err, warn = state(child, parent=None, context=comp)
errors.extend(err)
warnings.extend(warn)
comp.content.states.append(newstate)
elif child.type == lexer.FLOATING_LABEL: elif child.type == lexer.FLOATING_LABEL:
lab, err, warn = floating_label(child, parent=None, context=comp) lab, err, warn = floating_label(child, parent=None, context=comp)
errors.extend(err) errors.extend(err)
...@@ -1421,6 +1414,22 @@ def composite_state(root, parent=None, context=None): ...@@ -1421,6 +1414,22 @@ def composite_state(root, parent=None, context=None):
'Unsupported construct in nested state, type: ' + 'Unsupported construct in nested state, type: ' +
str(child.type) + ' - line ' + str(child.getLine()) + str(child.type) + ' - line ' + str(child.getLine()) +
' - state name: ' + str(comp.statename)) ' - state name: ' + str(comp.statename))
for each in inner_composite:
# Parse inner composite states after the text areas to make sure
# that all variables are propagated to the the inner scope
inner, err, warn = composite_state(each, parent=None,
context=comp)
errors.extend(err)
warnings.extend(warn)
comp.composite_states.append(inner)
for each in states:
# And parse the states after inner states to make sure all CONNECTS
# are properly defined.
# Fill up the 'mapping' structure.
newstate, err, warn = state(each, parent=None, context=comp)
errors.extend(err)
warnings.extend(warn)
comp.content.states.append(newstate)
return comp, errors, warnings return comp, errors, warnings
...@@ -2347,9 +2356,8 @@ def decision(root, parent, context): ...@@ -2347,9 +2356,8 @@ def decision(root, parent, context):
warnings.extend(warn) warnings.extend(warn)
dec.answers.append(ans) dec.answers.append(ans)
elif child.type == lexer.ELSE: elif child.type == lexer.ELSE:
dec.kind = child.toString()
a = ogAST.Answer() a = ogAST.Answer()
a.inputString = 'ELSE' a.inputString = child.toString()
for c in child.getChildren(): for c in child.getChildren():
if c.type == lexer.CIF: if c.type == lexer.CIF:
a.pos_x, a.pos_y, a.width, a.height = cif(c) a.pos_x, a.pos_y, a.width, a.height = cif(c)
......
EXAMPLES=test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 EXAMPLES=test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 test12
all: all:
for v in $(EXAMPLES) ; do make -C $$v clean all || exit 1 ; done for v in $(EXAMPLES) ; do make -C $$v clean all || exit 1 ; done
......
...@@ -8,8 +8,9 @@ compile: generate-code ...@@ -8,8 +8,9 @@ compile: generate-code
asn1.exe -Ada dataview-uniq.asn -typePrefix asn1Scc -equal asn1.exe -Ada dataview-uniq.asn -typePrefix asn1Scc -equal
gnatmake -c *.adb gnatmake -c *.adb
gcc -c test.c gcc -c test.c
gcc -o test test.o og.o taste_dataview.o -lgnat gnatbind -n og.ali
./test > result gnatlink -o testcase test.o og.ali -lgnat
./testcase > result
diff result expected && echo 'All OK!' diff result expected && echo 'All OK!'
......
...@@ -6,3 +6,5 @@ ...@@ -6,3 +6,5 @@
5 5
6 6
end of test end of test
All done
bonjour entry
/* CIF PROCESS (252, 216), (150, 75) */
PROCESS og; PROCESS og;
/* CIF TEXT (0, 0), (229, 98) */ STATE all_done;
SUBSTRUCTURE
STATE Bonjour;
SUBSTRUCTURE
/* CIF PROCEDURE (72, 110), (73, 35) */
PROCEDURE entry;
/* CIF START (258, 113), (70, 35) */
START;
/* CIF PROCEDURECALL (202, 163), (181, 35) */
CALL writeln('bonjour entry');
/* CIF RETURN (275, 213), (35, 35) */
RETURN ;
ENDPROCEDURE;
/* CIF START (190, 52), (70, 35) */
START;
/* CIF NEXTSTATE (189, 102), (72, 35) */
NEXTSTATE dumb;
/* CIF STATE (312, 42), (72, 35) */
STATE dumb;
ENDSTATE;
ENDSUBSTRUCTURE;
/* CIF TEXT (118, 110), (298, 140) */
-- Declare your variables
-- Syntax: DCL <variable name> <type name>;
dcl tmp my_OctStr;
/* CIF ENDTEXT */
/* CIF PROCEDURE (298, 308), (73, 35) */
PROCEDURE entry;
/* CIF START (171, 108), (70, 35) */
START;
/* CIF PROCEDURECALL (131, 158), (149, 35) */
CALL writeln('All done');
/* CIF RETURN (188, 208), (35, 35) */
RETURN ;
ENDPROCEDURE;
/* CIF START (473, 144), (70, 35) */
START;
/* CIF NEXTSTATE (466, 194), (84, 35) */
NEXTSTATE Bonjour;
/* CIF STATE (564, 193), (125, 35) */
STATE Dumb, Bonjour;
/* CIF INPUT (583, 248), (87, 35) */
INPUT go(tmp);
/* CIF NEXTSTATE (592, 298), (70, 35) */
NEXTSTATE -;
ENDSTATE;
/* CIF STATE (459, 366), (84, 35) */
STATE Bonjour;
ENDSTATE;
ENDSUBSTRUCTURE;
/* CIF TEXT (0, 0), (229, 98) */
-- Testing decisions -- Testing decisions
dcl test myInteger := 3; dcl test myInteger := 3;
/* CIF ENDTEXT */ /* CIF ENDTEXT */
/* CIF START (482, 35), (70, 35) */ /* CIF START (342, 35), (70, 35) */
START; START;
/* CIF LABEL (474, 85), (85, 35) */ /* CIF LABEL (334, 85), (85, 35) */
again: again:
/* CIF DECISION (482, 135), (70, 50) */ /* CIF DECISION (342, 135), (70, 50) */
DECISION test; DECISION test;
/* CIF ANSWER (85, 205), (70, 23) */ /* CIF ANSWER (-6, 205), (70, 23) */
(3): (3):
/* CIF PROCEDURECALL (67, 243), (106, 35) */ /* CIF PROCEDURECALL (-24, 243), (106, 35) */
CALL writeln('1'); CALL writeln('1');
/* CIF TASK (77, 293), (87, 35) */ /* CIF TASK (-14, 293), (87, 35) */
TASK test := 5; TASK test := 5;
/* CIF JOIN (103, 343), (35, 35) */ /* CIF JOIN (11, 343), (35, 35) */
JOIN again; JOIN again;
/* CIF ANSWER (582, 205), (70, 23) */ /* CIF ANSWER (486, 205), (70, 23) */
(>5): (>5):
/* CIF PROCEDURECALL (561, 243), (113, 35) */ /* CIF PROCEDURECALL (465, 243), (113, 35) */
CALL writeln('4'); CALL writeln('4');
/* CIF TASK (574, 293), (87, 35) */ /* CIF TASK (478, 293), (87, 35) */
TASK test := 7; TASK test := 7;
/* CIF LABEL (568, 343), (98, 35) */ /* CIF LABEL (472, 343), (98, 35) */
and_again: and_again:
/* CIF DECISION (582, 393), (70, 50) */ /* CIF DECISION (486, 393), (70, 50) */
DECISION test; DECISION test;
/* CIF ANSWER (681, 463), (70, 23) */ /* CIF ANSWER (585, 463), (70, 23) */
(<=5): (<=5):
/* CIF DECISION (681, 501), (70, 50) */ /* CIF DECISION (585, 501), (70, 50) */
DECISION test; DECISION test;
/* CIF ANSWER (576, 571), (70, 23) */ /* CIF ANSWER (480, 571), (70, 23) */
(>=5): (>=5):
/* CIF PROCEDURECALL (545, 609), (132, 35) */ /* CIF PROCEDURECALL (449, 609), (132, 35) */
CALL writeln('6'); CALL writeln('6');
/* CIF ANSWER (746, 571), (70, 23) */ /* CIF ANSWER (650, 571), (70, 23) */
else: else:
/* CIF PROCEDURECALL (707, 609), (148, 35) */ /* CIF PROCEDURECALL (611, 609), (148, 35) */
CALL writeln('[ERROR]'); CALL writeln('[ERROR]');
ENDDECISION; ENDDECISION;