Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
TASTE
OpenGEODE
Commits
d1c60c7a
Commit
d1c60c7a
authored
Oct 21, 2015
by
Maxime Perrotin
Browse files
Merge pull request #32 from maxime-esa/state_aggregation
State aggregation
parents
391e344a
0fa743fe
Changes
24
Expand all
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
d1c60c7a
...
...
@@ -148,6 +148,9 @@ The fonts are the fonts from Ubuntu, check licence in file FONT-LICENSE.TXT
Changelog
=========
1.
3.0 (10/2015)
-
Support for State Aggregations (parallel states)
1.
2.10 (10/2015)
-
Better support of renamePolicy
-
Better handling of models without CIF coordinates
...
...
opengeode/AdaGenerator.py
View file @
d1c60c7a
...
...
@@ -72,6 +72,7 @@
import
logging
import
traceback
import
os
from
itertools
import
chain
from
singledispatch
import
singledispatch
import
ogAST
...
...
@@ -181,6 +182,16 @@ LD_LIBRARY_PATH=. taste-gui -l
# In case model has nested states, flatten everything
Helper
.
flatten
(
process
,
sep
=
UNICODE_SEP
)
# Process State aggregations (Parallel states)
# Find recursively in the AST all state aggregations
# Format: {'aggregation_name' : [list of ogAST.CompositeState]
aggregates
=
Helper
.
state_aggregations
(
process
)
# Extract the list of parallel states names inside the composite states
# of state aggregations XXX add to C generator
parallel_states
=
Helper
.
parallel_states
(
aggregates
)
# Make an maping {input: {state: transition...}} in order to easily
# generate the lookup tables for the state machine runtime
mapping
=
Helper
.
map_input_state
(
process
)
...
...
@@ -189,19 +200,28 @@ LD_LIBRARY_PATH=. taste-gui -l
process_level_decl
=
[]
# Establish the list of states (excluding START states)
statelist
=
', '
.
join
(
name
for
name
in
process
.
mapping
.
iterkeys
()
if
not
name
.
endswith
(
u
'START'
))
or
'No_State'
if
statelist
:
states_decl
=
u
'type States is ({});'
.
format
(
statelist
)
process_level_decl
.
append
(
states_decl
)
# Establish the list of states (excluding START states) XXX update C backend
full_statelist
=
list
(
chain
(
aggregates
.
viewkeys
(),
(
name
for
name
in
process
.
mapping
.
iterkeys
()
if
not
name
.
endswith
(
u
'START'
))))
reduced_statelist
=
[
s
for
s
in
full_statelist
if
s
not
in
parallel_states
]
if
full_statelist
:
process_level_decl
.
append
(
u
'type States is ({});'
.
format
(
u
', '
.
join
(
full_statelist
)
or
u
'No_State'
))
# Generate the code to declare process-level context
process_level_decl
.
extend
([
'type {}_Ty is'
.
format
(
LPREFIX
),
'record'
])
if
statelist
:
if
full_
statelist
:
process_level_decl
.
append
(
'state : States;'
)
# State aggregation: add list of substates (XXX to be added in C generator)
for
substates
in
aggregates
.
viewvalues
():
for
each
in
substates
:
process_level_decl
.
append
(
u
'{}{}state: States;'
.
format
(
each
.
statename
,
UNICODE_SEP
))
for
var_name
,
(
var_type
,
def_value
)
in
process
.
variables
.
viewitems
():
if
def_value
:
# Expression must be a ground expression, i.e. must not
...
...
@@ -226,6 +246,22 @@ LD_LIBRARY_PATH=. taste-gui -l
process_level_decl
.
append
(
u
'{name} : constant := {val};'
.
format
(
name
=
name
,
val
=
str
(
val
)))
# Declare start procedure for aggregate states XXX add in C generator
# should create one START per "via" clause, TODO later
aggreg_start_proc
=
[]
for
name
,
substates
in
aggregates
.
viewitems
():
proc_name
=
u
'procedure {}{}START'
.
format
(
name
,
UNICODE_SEP
)
process_level_decl
.
append
(
u
'{};'
.
format
(
proc_name
))
aggreg_start_proc
.
extend
([
u
'{} is'
.
format
(
proc_name
),
'begin'
])
aggreg_start_proc
.
extend
(
u
'runTransition({sub}{sep}START);'
.
format
(
sub
=
subname
.
statename
,
sep
=
UNICODE_SEP
)
for
subname
in
substates
)
aggreg_start_proc
.
extend
([
u
'end {}{}START;'
.
format
(
name
,
UNICODE_SEP
),
'
\n
'
])
# Add the declaration of the runTransition procedure, if needed
if
process
.
transitions
:
process_level_decl
.
append
(
'procedure runTransition(Id: Integer);'
)
...
...
@@ -322,7 +358,6 @@ package {process_name} is'''.format(process_name=process_name,
dll_api
.
append
(
'end dll_set_l_{};'
.
format
(
var_name
))
dll_api
.
append
(
''
)
# Generate the the code of the procedures
inner_procedures_code
=
[]
for
proc
in
process
.
content
.
inner_procedures
:
...
...
@@ -336,17 +371,23 @@ package {process_name} is'''.format(process_name=process_name,
# Add the code of the procedures definitions
taste_template
.
extend
(
inner_procedures_code
)
# Generate the code of the START procedures of state aggregations
# XXX to be added to C generator
taste_template
.
extend
(
aggreg_start_proc
)
# Add the code of the DLL interface
taste_template
.
extend
(
dll_api
)
# Generate the code for each input signal (provided interface) and timers
print
process
.
input_signals
for
signal
in
process
.
input_signals
+
[
{
'name'
:
timer
.
lower
()}
for
timer
in
process
.
timers
]:
if
signal
.
get
(
'name'
,
u
'START'
)
==
u
'START'
:
signame
=
signal
.
get
(
'name'
,
u
'START'
)
if
name
==
u
'START'
:
continue
pi_header
=
u
'procedure {sig_name}'
.
format
(
sig_name
=
signa
l
[
'name'
]
)
pi_header
=
u
'procedure {sig_name}'
.
format
(
sig_name
=
signa
me
)
param_name
=
signal
.
get
(
'param_name'
)
\
or
u
'{}_param'
.
format
(
signal
[
'
name
'
]
)
or
u
'{}_param'
.
format
(
name
)
# Add (optional) PI parameter (only one is possible in TASTE PI)
if
'type'
in
signal
:
typename
=
type_name
(
signal
[
'type'
])
...
...
@@ -354,29 +395,27 @@ package {process_name} is'''.format(process_name=process_name,
pName
=
param_name
,
sort
=
typename
)
# Add declaration of the provided interface in the .ads file
ads_template
.
append
(
u
'-- Provided interface "
'
+
signal
[
'name'
]
+
'"'
)
ads_template
.
append
(
u
'-- Provided interface "
{}"'
.
format
(
signame
)
)
ads_template
.
append
(
pi_header
+
';'
)
ads_template
.
append
(
u
'pragma export(C, {name}, "{proc}_{name}");'
.
format
(
name
=
signa
l
[
'name'
]
,
proc
=
process_name
))
.
format
(
name
=
signa
me
,
proc
=
process_name
))
if
simu
:
# Generate code for the mini-cv template
params
=
[(
param_name
,
type_name
(
signal
[
'type'
],
use_prefix
=
False
),
'IN'
)]
if
'type'
in
signal
else
[]
minicv
.
append
(
aadl_template
(
signa
l
[
'name'
]
,
params
,
'RI'
))
minicv
.
append
(
aadl_template
(
signa
me
,
params
,
'RI'
))
pi_header
+=
' is'
taste_template
.
append
(
pi_header
)
taste_template
.
append
(
'begin'
)
taste_template
.
append
(
'case {ctxt}.state is'
.
format
(
ctxt
=
LPREFIX
))
for
state
in
process
.
mapping
.
viewkeys
():
if
state
.
endswith
(
u
'START'
):
continue
taste_template
.
append
(
u
'when {state} =>'
.
format
(
state
=
state
))
input_def
=
mapping
[
signal
[
'name'
]].
get
(
state
)
def
execute_transition
(
state
):
''' Generate the code that triggers the transition for the current
state/input combination '''
input_def
=
mapping
[
signame
].
get
(
state
)
# Check for nested states to call optional exit procedure
sep
=
UNICODE_SEP
state_tree
=
state
.
split
(
sep
)
state_tree
=
state
.
split
(
UNICODE_SEP
)
context
=
process
exitlist
=
[]
current
=
''
...
...
@@ -388,13 +427,13 @@ package {process_name} is'''.format(process_name=process_name,
if
comp
.
exit_procedure
:
exitlist
.
append
(
current
)
context
=
comp
current
=
current
+
sep
current
=
current
+
UNICODE_SEP
break
for
each
in
reversed
(
exitlist
):
if
trans
and
all
(
each
.
startswith
(
trans_st
)
for
trans_st
in
trans
.
possible_states
):
taste_template
.
append
(
u
'p{sep}{ref}{sep}exit;'
.
format
(
ref
=
each
,
sep
=
sep
))
.
format
(
ref
=
each
,
sep
=
UNICODE_SEP
))
if
input_def
:
for
inp
in
input_def
.
parameters
:
...
...
@@ -412,10 +451,48 @@ package {process_name} is'''.format(process_name=process_name,
taste_template
.
append
(
'null;'
)
else
:
taste_template
.
append
(
'null;'
)
taste_template
.
append
(
'case {ctxt}.state is'
.
format
(
ctxt
=
LPREFIX
))
def
case_state
(
state
):
''' Recursive function (in case of state aggregation) to generate
the code that calls the proper transition according
to the current state
The input name is in signame
'''
if
state
.
endswith
(
u
'START'
):
return
taste_template
.
append
(
u
'when {state} =>'
.
format
(
state
=
state
))
if
state
in
aggregates
.
viewkeys
():
# State aggregation:
# - find which substate manages this input
# - add a swich case on the corresponding substate
taste_template
.
append
(
u
'-- this is a state aggregation'
)
for
sub
in
aggregates
[
state
]:
for
par
in
sub
.
mapping
.
viewkeys
():
if
par
in
mapping
[
signame
].
viewkeys
():
taste_template
.
append
(
u
'case '
u
'{ctxt}.{sub}{sep}state is'
.
format
(
ctxt
=
LPREFIX
,
sub
=
sub
.
statename
,
sep
=
UNICODE_SEP
))
case_state
(
par
)
taste_template
.
append
(
'when others =>'
)
taste_template
.
append
(
'null;'
)
taste_template
.
append
(
'end case;'
)
break
else
:
# Input is not managed in the state aggregation
taste_template
.
append
(
'null;'
)
else
:
execute_transition
(
state
)
map
(
case_state
,
reduced_statelist
)
# XXX update C generator
taste_template
.
append
(
'when others =>'
)
taste_template
.
append
(
'null;'
)
taste_template
.
append
(
'end case;'
)
taste_template
.
append
(
u
'end {};'
.
format
(
signa
l
[
'name'
]
))
taste_template
.
append
(
u
'end {};'
.
format
(
signa
me
))
taste_template
.
append
(
'
\n
'
)
# for the .ads file, generate the declaration of the required interfaces
...
...
@@ -680,7 +757,6 @@ def write_statement(param, newline):
code
,
string
,
local
=
expression
(
param
)
if
type_kind
==
'OctetStringType'
:
# Octet string -> convert to Ada string
sep
=
UNICODE_SEP
last_it
=
u
""
if
isinstance
(
param
,
ogAST
.
PrimSubstring
):
range_str
=
u
"{}'Range"
.
format
(
string
)
...
...
@@ -696,7 +772,7 @@ def write_statement(param, newline):
last_it
=
u
"({})"
.
format
(
range_str
)
code
.
extend
([
u
"for i in {} loop"
.
format
(
range_str
),
u
"Put(Character'Val({st}(i)));"
.
format
(
st
=
string
,
sep
=
sep
,
it
=
iterator
),
.
format
(
st
=
string
),
u
"end loop;"
])
else
:
code
.
append
(
"Put({});"
.
format
(
string
))
...
...
@@ -1878,23 +1954,47 @@ def _transition(tr, **kwargs):
code
.
append
(
'<<{label}>>'
.
format
(
label
=
tr
.
terminator
.
label
.
inputString
))
if
tr
.
terminator
.
kind
==
'next_state'
:
if
tr
.
terminator
.
inputString
.
strip
()
!=
'-'
:
history
=
tr
.
terminator
.
inputString
.
strip
()
==
'-'
if
tr
.
terminator
.
next_is_aggregation
and
not
history
:
# XXX add to C generator
code
.
append
(
u
'-- Entering state aggregation {}'
.
format
(
tr
.
terminator
.
inputString
))
# Call the START function of the state aggregation
code
.
append
(
u
'{};'
.
format
(
tr
.
terminator
.
next_id
))
code
.
append
(
u
'{ctxt}.state := {nextState};'
.
format
(
ctxt
=
LPREFIX
,
nextState
=
tr
.
terminator
.
inputString
))
code
.
append
(
u
'trId := -1;'
)
elif
not
history
:
# tr.terminator.inputString.strip() != '-':
code
.
append
(
u
'trId := '
+
unicode
(
tr
.
terminator
.
next_id
)
+
u
';'
)
if
tr
.
terminator
.
next_id
==
-
1
:
code
.
append
(
u
'{ctxt}.state := {nextState};'
.
format
(
ctxt
=
LPREFIX
,
if
not
tr
.
terminator
.
substate
:
# XXX add to C generator
code
.
append
(
u
'{ctxt}.state := {nextState};'
.
format
(
ctxt
=
LPREFIX
,
nextState
=
tr
.
terminator
.
inputString
))
else
:
code
.
append
(
u
'{ctxt}.{sub}{sep}state :='
u
' {nextState};'
.
format
(
ctxt
=
LPREFIX
,
sub
=
tr
.
terminator
.
substate
,
sep
=
UNICODE_SEP
,
nextState
=
tr
.
terminator
.
inputString
))
else
:
# "nextstate -": switch case to re-run the entry transition
# in case of a composite state or state aggregation
if
any
(
next_id
for
next_id
in
tr
.
terminator
.
candidate_id
.
viewkeys
()
if
next_id
!=
-
1
):
code
.
append
(
'case {}.state is'
.
format
(
LPREFIX
))
for
nid
,
sta
in
tr
.
terminator
.
candidate_id
.
viewitems
():
if
nid
!=
-
1
:
for
each
in
sta
:
code
.
extend
([
u
'when {} =>'
.
format
(
each
),
u
'trId := {};'
.
format
(
nid
)])
if
tr
.
terminator
.
next_is_aggregation
:
statement
=
u
'{};'
.
format
(
nid
)
else
:
statement
=
u
'tdId := {};'
.
format
(
nid
)
code
.
extend
([
u
'when {} =>'
.
format
(
u
'|'
.
join
(
sta
)),
statement
])
code
.
extend
([
'when others =>'
,
'trId := -1;'
,
...
...
opengeode/Helper.py
View file @
d1c60c7a
...
...
@@ -13,8 +13,11 @@
input-state-transition
sorted_fields(SEQ/CHOICE) : returns the ordered list of fields
of an ASN.1 SEQUENCE or CHOICE type
state_aggregations: enrich AST with state aggregation flags,
and return the list of substates of aggregations
parallel_states: return a list of strings naming all parallel states
Copyright (c) 2012-201
4
European Space Agency
Copyright (c) 2012-201
5
European Space Agency
Designed and implemented by Maxime Perrotin
...
...
@@ -33,7 +36,55 @@ import ogAST
LOG
=
logging
.
getLogger
(
__name__
)
__all__
=
[
'flatten'
,
'rename_everything'
,
'inner_labels_to_floating'
,
'map_input_state'
,
'sorted_fields'
]
'map_input_state'
,
'sorted_fields'
,
'state_aggregations'
,
'parallel_states'
]
def
state_aggregations
(
process
):
''' Explore recursively the AST to find all state aggregations, and
return the composite states inside them
input: ogAST.Process element
output: {state_aggregation: {list of ogAST.CompositeState}
'''
# { aggregate_name : [list of parallel states] }
aggregates
=
defaultdict
(
list
)
def
do_composite
(
comp
,
aggregate
=
''
):
''' Recursively find all state aggregations in order to create
variables to store the state of each parallel state '''
for
each
in
comp
.
composite_states
:
# CHECKME
pre
=
comp
.
statename
if
isinstance
(
comp
,
ogAST
.
StateAggregation
)
\
else
''
do_composite
(
each
,
pre
)
if
isinstance
(
each
,
ogAST
.
StateAggregation
):
for
term
in
comp
.
terminators
:
if
term
.
inputString
.
lower
()
==
each
.
statename
.
lower
():
term
.
next_is_aggregation
=
True
if
aggregate
and
not
isinstance
(
comp
,
ogAST
.
StateAggregation
):
# Composite state inside a state aggregation
aggregates
[
aggregate
].
append
(
comp
)
# Here, all the terminators inside the composite states must
# be flagged with the name of the substate so that the NEXTSTATE
# will not be using the main "context.state" variable but will
# use the parallel substate name when generating code.
for
each
in
comp
.
terminators
:
each
.
substate
=
comp
.
statename
for
each
in
process
.
composite_states
:
do_composite
(
each
)
for
each
in
process
.
terminators
:
if
each
.
inputString
.
lower
()
in
aggregates
:
each
.
next_is_aggregation
=
True
return
aggregates
def
parallel_states
(
aggregates
):
''' Given a mapping obtained with state_aggregation(process), extract
all parallel states and return a list of state names '''
parallel_states
=
[]
for
name
,
comp
in
aggregates
.
viewitems
():
for
each
in
comp
:
parallel_states
.
extend
(
name
for
name
in
each
.
mapping
.
viewkeys
()
if
not
name
.
endswith
(
u
'START'
))
return
parallel_states
def
map_input_state
(
process
):
...
...
@@ -89,16 +140,20 @@ def flatten(process, sep=u'_'):
term
.
next_id
=
u
'{term}{sep}{entry}_START'
.
format
(
term
=
term
.
inputString
,
entry
=
term
.
entrypoint
,
sep
=
sep
)
elif
term
.
inputString
.
strip
()
==
'-'
:
term
.
candidate_id
=
defaultdict
(
list
)
#
term.candidate_id = defaultdict(list)
for
each
in
term
.
possible_states
:
if
each
.
lower
()
in
(
st
.
statename
.
lower
()
for
st
in
context
.
composite_states
):
term
.
candidate_id
[
each
+
sep
+
u
'START'
]
=
\
term
.
candidate_id
[
-
1
].
append
(
each
)
for
comp
in
context
.
composite_states
:
if
each
.
lower
()
==
comp
.
statename
.
lower
():
if
isinstance
(
comp
,
ogAST
.
StateAggregation
):
term
.
next_is_aggregation
=
True
term
.
candidate_id
[
each
+
sep
+
u
'START'
]
=
[
each
]
else
:
term
.
candidate_id
[
each
+
sep
+
u
'START'
]
=
\
[
st
for
st
in
process
.
mapping
.
viewkeys
()
if
st
.
startswith
(
each
)
and
not
st
.
endswith
(
u
'START'
)]
else
:
term
.
candidate_id
[
-
1
].
append
(
each
)
continue
def
update_composite_state
(
state
,
process
):
''' Rename inner states, recursively, and add inner transitions
...
...
@@ -171,8 +226,12 @@ def flatten(process, sep=u'_'):
# Go recursively in inner composite states
inner
.
statename
=
prefix
+
inner
.
statename
update_composite_state
(
inner
,
process
)
propagate_inputs
(
inner
,
process
.
mapping
[
inner
.
statename
])
del
process
.
mapping
[
inner
.
statename
]
propagate_inputs
(
inner
,
process
)
try
:
del
process
.
mapping
[
inner
.
statename
]
except
KeyError
:
# KeyError in case of state aggregation
pass
for
each
in
state
.
terminators
:
# Give prefix to terminators
if
each
.
label
:
...
...
@@ -200,7 +259,7 @@ def flatten(process, sep=u'_'):
each
.
inputString
=
prefix
+
each
.
inputString
process
.
content
.
inner_procedures
.
extend
(
state
.
content
.
inner_procedures
)
def
propagate_inputs
(
nested_state
,
inputlis
t
):
def
propagate_inputs
(
nested_state
,
contex
t
):
''' 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
...
...
@@ -208,12 +267,14 @@ def flatten(process, sep=u'_'):
'''
for
_
,
val
in
nested_state
.
mapping
.
viewitems
():
try
:
inputlist
=
context
.
mapping
[
nested_state
.
statename
]
val
.
extend
(
inputlist
)
except
AttributeError
:
except
(
AttributeError
,
KeyError
):
# KeyError in case of StateAggregation
pass
for
each
in
nested_state
.
composite_states
:
# do the same recursively
propagate_inputs
(
each
,
nested_state
.
mapping
[
each
.
statename
]
)
propagate_inputs
(
each
,
nested_state
)
#del nested_state.mapping[each.statename]
def
set_terminator_states
(
context
,
prefix
=
''
):
...
...
@@ -238,7 +299,7 @@ def flatten(process, sep=u'_'):
for
each
in
process
.
composite_states
:
update_composite_state
(
each
,
process
)
propagate_inputs
(
each
,
process
.
mapping
[
each
.
statename
]
)
propagate_inputs
(
each
,
process
)
del
process
.
mapping
[
each
.
statename
]
# Update terminators at process level
...
...
opengeode/Pr.py
View file @
d1c60c7a
...
...
@@ -332,7 +332,8 @@ def _state(symbol, recursive=True, nextstate=True, composite=False, cpy=False,
else
:
# Generate code for a nested state
result
=
Indent
()
result
.
append
(
'STATE {};'
.
format
(
unicode
(
symbol
).
split
()[
0
]))
agg
=
' AGGREGATION'
if
not
list
(
symbol
.
nested_scene
.
start
)
else
''
result
.
append
(
'STATE{} {};'
.
format
(
agg
,
unicode
(
symbol
).
split
()[
0
]))
result
.
append
(
'SUBSTRUCTURE'
)
Indent
.
indent
+=
1
entry_points
,
exit_points
=
[],
[]
...
...
opengeode/icons.py
View file @
d1c60c7a
...
...
@@ -2,7 +2,7 @@
# Resource object code
#
# Created: T
h
u Oct
8 22:16:37
2015
# Created: Tu
e
Oct
20 15:17:00
2015
# by: The Resource Compiler for PySide (Qt v4.8.6)
#
# WARNING! All changes made in this file will be lost!
opengeode/ogAST.py
View file @
d1c60c7a
...
...
@@ -35,6 +35,7 @@
"""
import
logging
from
collections
import
defaultdict
LOG
=
logging
.
getLogger
(
__name__
)
...
...
@@ -463,6 +464,17 @@ class Terminator(object):
self
.
possible_states
=
[]
# optional composite state content (type CompositeState)
self
.
composite
=
None
# Flag to indicate if the nextstate is a state aggregation
self
.
next_is_aggregation
=
False
# If this terminator is within a state aggregation, store the name
# of the parallel substate (set by Helper.state_aggregations)
self
.
substate
=
''
# candidate_id: {transition_id: [states]}
# field is set by Helper.py/flatten, in case of "nextstate -"
# there is a list of states that set transition_id to -1 : the standard
# states ; and there are the composite states, that set a different
# id corresponding to the start transition of the state.
self
.
candidate_id
=
defaultdict
(
list
)
def
trace
(
self
):
''' Debug output for terminators '''
...
...
@@ -716,7 +728,7 @@ class TextArea(object):
class
Automaton
(
object
):
''' Elements contained in a process
or a
procedure '''
''' Elements contained in a process
,
procedure
or composite state
'''
def
__init__
(
self
,
parent
=
None
):
''' AST grouping the elements that can be rendered graphically '''
self
.
parent
=
parent
...
...
@@ -879,6 +891,28 @@ class CompositeState(Process):
l
=
self
.
line
,
c
=
self
.
charPositionInLine
)
class
StateAggregation
(
CompositeState
):
'''
State Aggregation (Parallel states) are supported since SDL2000
These states can only contain (in the self.content field):
text areas
procedure definitions
composite states (including sub-state aggregations)
But no state machine definition
'''
def
__init__
(
self
):
super
(
StateAggregation
,
self
).
__init__
()
# List of partition connections:
# [{'outer': {'state_part_id': str, 'point': str},
# 'inner': {'state_part_id': str, 'point': str}}]
self
.
state_partition_connections
=
[]
def
trace
(
self
):
''' Debug output for state aggregation '''
return
u
'STATE AGGREGATION {exp} ({l},{c})'
.
format
(
exp
=
self
.
statename
,
l
=
self
.
line
,
c
=
self
.
charPositionInLine
)
class
Block
(
object
):
''' AST for a BLOCK entity '''
def
__init__
(
self
):
...
...
opengeode/ogParser.py
View file @
d1c60c7a
...
...
@@ -1893,8 +1893,11 @@ def fpar(root):
def
composite_state
(
root
,
parent
=
None
,
context
=
None
):
''' Parse a composite state definition '''
comp
=
ogAST
.
CompositeState
()
''' Parse a composite state (incl. state aggregation) definition '''
if
root
.
type
==
lexer
.
COMPOSITE_STATE
:
comp
=
ogAST
.
CompositeState
()
elif
root
.
type
==
lexer
.
STATE_AGGREGATION
:
comp
=
ogAST
.
StateAggregation
()
errors
,
warnings
=
[],
[]
# Create a list of all inherited data
try
:
...
...
@@ -1948,7 +1951,7 @@ def composite_state(root, parent=None, context=None):
comp
.
content
.
inner_procedures
.
append
(
new_proc
)
# Add procedure to the context, to make it visible at scope level
context
.
procedures
.
append
(
new_proc
)
elif
child
.
type
==
lexer
.
COMPOSITE_STATE
:
elif
child
.
type
in
(
lexer
.
COMPOSITE_STATE
,
lexer
.
STATE_AGGREGATION
)
:
inner_composite
.
append
(
child
)
elif
child
.
type
==
lexer
.
STATE
:
states
.
append
(
child
)
...
...
@@ -1956,6 +1959,10 @@ def composite_state(root, parent=None, context=None):
floatings
.
append
(
child
)
elif
child
.
type
==
lexer
.
START
:
starts
.
append
(
child
)
elif
child
.
type
==
lexer
.
STATE_PARTITION_CONNECTION
:
# TODO (see section 11.11.2)
warnings
.
append
([
'Ignoring state partition connections'
,
[
0
,
0
],
[]])
else
:
warnings
.
append
([
'Unsupported construct in nested state, type: {}'
'- line {} - State name: {}'
...
...
@@ -1964,11 +1971,18 @@ def composite_state(root, parent=None, context=None):
str
(
comp
.
statename
)),
[
0
,
0
],
# No graphical position
[]])
if
(
floatings
or
starts
)
and
isinstance
(
comp
,
ogAST
.
StateAggregation
):
errors
.
append
([
'State aggregation can only contain composite state(s)'
,
[
0
,
0
],
[]])
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
)
if
isinstance
(
comp
,
ogAST
.
StateAggregation
):
# State aggregation contain only composite states, so we must
# add empty mapping information since there are no transitions
comp
.
mapping
[
inner
.
statename
.
lower
()]
=
[]
errors
.
extend
(
err
)
warnings
.
extend
(
warn
)
comp
.
composite_states
.
append
(
inner
)
...
...
@@ -2701,7 +2715,7 @@ def process_definition(root, parent=None, context=None):
errors
.
extend
(
err
)
warnings
.
extend
(
warn
)
process
.
content
.
floating_labels
.
append
(
lab
)
elif
child
.
type
==
lexer
.
COMPOSITE_STATE
:
elif
child
.
type
in
(
lexer
.
COMPOSITE_STATE
,
lexer
.
STATE_AGGREGATION
)
:
comp
,
err
,
warn
=
composite_state
(
child
,
parent
=
None
,
context
=
process
)
...
...
@@ -3574,7 +3588,8 @@ def nextstate(root, context):
try
:
composite
,
=
(
comp
for
comp
in
context
.
composite_states
if
comp
.
statename
.
lower
()
==
next_state_id
.
lower
())
if
not
composite
.
content
.
start
:
if
not
isinstance
(
composite
,
ogAST
.
StateAggregation
)
\
and
not
composite
.
content
.
start
:
errors
.
append
(
'Composite state "{}" has no unnamed '
'START symbol'
.
format
(
composite
.
statename
))
except
ValueError
:
...
...
opengeode/opengeode.py
View file @
d1c60c7a
...
...
@@ -116,7 +116,7 @@ except ImportError:
__all__
=
[
'opengeode'
,
'SDL_Scene'
,
'SDL_View'
,
'parse'
]
__version__
=
'1.
2.1
0'
__version__
=
'1.
3.
0'
if
hasattr
(
sys
,
'frozen'
):
# Detect if we are running on Windows (py2exe-generated)