Commit 84cbb9a6 authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Add support for parameters in input/output expressions

observers can use syntax:
input signalname(param) to blah
param shall not be declared in that case
parent 5ccda33c
......@@ -124,6 +124,9 @@ The background pattern was downloaded from www.subtlepatterns.com
Changelog
=========
**3.5.3 (04/2021)**
- Model checking observers: add support for parameters in input/output expressions
**3.5.2 (04/2021)**
- Model checking observers: run only one transition per call
- Support field names called "state"
......
......@@ -314,6 +314,29 @@ LD_LIBRARY_PATH=./lib:.:$LD_LIBRARY_PATH opengeode-simulator
# In case model has nested states, flatten everything
Helper.flatten(process, sep=SEPARATOR)
# Pre-processing of aliases: we must rename all references to the aliases
# when they point to a structure that contain a CHOICE field (we cannot
# use a rename clause in that case, since the field depends on a
# discriminant.
no_renames = []
for (alias, (sort, alias_expr)) in process.aliases.items():
def rec_detect_choice(expr: ogAST.Expression) -> bool:
if not isinstance(expr, ogAST.PrimSelector):
return False
receiver = expr.value[0]
bty = find_basic_type(receiver.exprType)
if bty.kind == 'ChoiceType':
return True
return rec_detect_choice(receiver)
is_choice = rec_detect_choice(alias_expr)
if is_choice:
LOG.debug(f"alias: {alias_expr.inputString} will replace {alias}")
Helper.rename_everything(process.content,
alias,
alias_expr.inputString)
no_renames.append(alias)
# Process State aggregations (Parallel states) XXX Add to C backend
# Find recursively in the AST all state aggregations
......@@ -331,6 +354,8 @@ LD_LIBRARY_PATH=./lib:.:$LD_LIBRARY_PATH opengeode-simulator
for (var_name, content) in process.variables.items():
# filter out the aliases and put them in the local variable pool
# to avoid unwanted prefixes when using them
if var_name in no_renames:
continue
if var_name in process.aliases.keys():
LOCAL_VAR[var_name] = content
else:
......@@ -500,6 +525,8 @@ LD_LIBRARY_PATH=./lib:.:$LD_LIBRARY_PATH opengeode-simulator
# Add aliases
for alias_name, (alias_sort, alias_expr) in process.aliases.items():
if alias_name in no_renames:
continue
_, qualified, _ = expression(alias_expr)
context_decl.append(f"{alias_name} : {type_name(alias_sort)} "
f"renames {qualified};")
......@@ -2104,7 +2131,7 @@ def _prim_selector(prim, **kwargs):
stmts, ada_string, local_decl = [], '', []
ro = kwargs.get("readonly", 0)
receiver = prim.value[0]
receiver = prim.value[0] # can be a PrimSelector
field_name = prim.value[1]
receiver_stms, receiver_string, receiver_decl = expression(receiver,
......
This diff is collapsed.
......@@ -1657,29 +1657,38 @@ def io_expression(root, context):
direction = "out"
string += event_kind.format(kind=kind)
param_name = ""
func = ""
for child in root.getChildren():
if child.type == lexer.ID:
msg = child.text
elif child.type == lexer.FROM:
src = child.getChild(0).text
func = child.getChild(0).text
string += target_option.format(kind=kind,
target="source",
function=src)
function=func)
elif child.type == lexer.TO:
dest = child.getChild(0).text
func = child.getChild(0).text
string += target_option.format(kind=kind,
target="dest",
function=dest)
function=func)
elif child.type == lexer.IOPARAM:
# optional parameter
# to find the type of th parameter, the easiest is to parse the
# path to the field as an expression. That will also detect errors.
# this will be done after the parsing of other elements, when
# destination field is known.
param_name = child.getChild(0).text
else:
raise NotImplementedError("In io_expression")
if msg:
string += msg_name.format(kind=kind,
function=dest if kind=="input" else src,
function=func,
direction=direction,
msg=msg)
......@@ -1691,6 +1700,61 @@ def io_expression(root, context):
expr, errors, warnings = expression(tree, context)
expr.inputString = inputString
# Now address the optional parameter: if set, we will create an implicit
# alias definition to the event structure where the parameter is actually
# present. If an alias of the same type already exists, raise an error
if param_name:
path=f"event.{kind}_event.event.{func}.msg_{direction}.{msg}"
parser = parser_init (string=path)
new_root = parser.expression()
tree = new_root.tree
tree.token_stream = parser.getTokenStream()
param_expr, errs, warns = expression(tree, context)
errors.extend(errs)
warnings.extend(warns)
# We then need to find the parameter name and get its type
# this is a SEQUENCE by construction (generated by kazoo)
# and since only one parameter is supported, it has only one field
if not errs:
try:
pname, ptype = list(find_basic_type(param_expr.exprType).Children.items())[0]
except IndexError:
errors.append(f"No parameter expected for message {msg}")
else:
path += f".{pname}"
# We must re-parse the expression with the field name
parser = parser_init (string=path)
new_root = parser.expression()
tree = new_root.tree
tree.token_stream = parser.getTokenStream()
param_expr, errs, warns = expression(tree, context)
errors.extend(errs)
warnings.extend(warns)
# The type of the parameter is:
ptype = ptype.type
#print(f"dcl {param_name} {type_name(ptype)} renames {path};")
for var, (sort, _) in context.variables.items():
if var.lower() == param_name.lower():
# variable already defined, does it have the same type?
if type_name(sort) != type_name(ptype):
errors.append(f"Duplicate/incompatible definition"
f" of variable {param_name}")
elif var.lower() in context.aliases.keys():
# Check if already defined variable is an alias,
# and if so, if it points to the same element
_, alias_expr = context.aliases[var.lower()]
if alias_expr.inputString != param_expr.inputString:
errors.append(f"Parameter name {param_name} is"
" used in another context, but not "
f"pointing to the same content")
else:
#print ("param/alias already defined")
break
else:
# not found a duplicate definition -> Add an alias
context.variables[param_name.lower()] = (ptype, None)
context.aliases[param_name.lower()] = (ptype, param_expr)
return expr, errors, warnings
def expression(root, context, pos='right'):
......@@ -2729,7 +2793,10 @@ def variables(root, ta_ast, context, monitor=False):
if monitor:
errors.append(f'{var[-1]}: aliasing on monitors is not allowed')
else:
context._aliases_ast.append((var[-1], asn1_sort, child.getChild(0), ta_ast))
context._aliases_ast.append((var[-1].lower(),
asn1_sort,
child.getChild(0),
ta_ast))
elif child.type == lexer.GROUND:
# Default value for a variable - needs to be a ground expression
def_value, err, warn = expression(child.getChild(0), context)
......@@ -3949,6 +4016,7 @@ def process_definition(root, parent=None, context=None):
errors.append([f"In alias '{alias_name}': type mismatch ({t1} vs {t2})",
[ta_ast.pos_x or 0, ta_ast.pos_y or 0], []])
else:
# alias_name is already in lowercase
process.aliases[alias_name] = (alias_sort, expr)
if not process.referenced and not process.instance_of_name \
......
......@@ -141,7 +141,7 @@ except ImportError:
__all__ = ['opengeode', 'SDL_Scene', 'SDL_View', 'parse']
__version__ = '3.5.2'
__version__ = '3.5.3'
if hasattr(sys, 'frozen'):
# Detect if we are running on Windows (py2exe-generated)
......
This diff is collapsed.
This diff is collapsed.
......@@ -83,6 +83,7 @@ tokens {
OUTPUT;
OUTPUT_BODY;
PARAM;
IOPARAM;
PARAMNAMES;
PARAMS;
PAREN;
......@@ -1113,20 +1114,22 @@ postfix_expression
// input and output expression allow observers (for model checking) to
// monitor the sending and receiving of messages with a nice syntax
// (e.g. event = output msg from foo)
// (e.g. event = output msg (p) from foo)
// the parameter is optional. It dooes not need to be declared as a variable,
// as it it implicit.
input_expression
: INPUT
-> ^(INPUT_EXPRESSION)
| INPUT (msg=ID)? (FROM src=ID)? TO dest=ID
-> ^(INPUT_EXPRESSION $msg? ^(FROM $src)? ^(TO $dest))
| INPUT (msg=ID ('(' param=ID ')')? )? (FROM src=ID)? TO dest=ID
-> ^(INPUT_EXPRESSION $msg? ^(IOPARAM $param)? ^(FROM $src)? ^(TO $dest))
;
output_expression
: OUTPUT
-> ^(OUTPUT_EXPRESSION)
| OUTPUT (msg=ID)? (FROM src=ID) (TO dest=ID)?
-> ^(OUTPUT_EXPRESSION $msg? ^(FROM $src) ^(TO $dest)?)
| OUTPUT (msg=ID ('(' param=ID ')')? )? (FROM src=ID) (TO dest=ID)?
-> ^(OUTPUT_EXPRESSION $msg? ^(IOPARAM $param)? ^(FROM $src) ^(TO $dest)?)
;
primary_expression
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment