Skip to content

Commit

Permalink
Fix interpretation of function calls without arguments
Browse files Browse the repository at this point in the history
Ref. #763
  • Loading branch information
treiher committed Sep 22, 2021
1 parent a43d7b9 commit 02ab55d
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 0 deletions.
8 changes: 8 additions & 0 deletions rflx/generator/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,14 @@ def _declare(
) -> EvaluatedDeclaration:
result = EvaluatedDeclaration()

if expression:
for e in expression.findall(lambda x: isinstance(x, expr.Call)):
fail(
"initialization using function call not yet supported",
Subsystem.GENERATOR,
location=e.location,
)

if type_ == rty.OPAQUE:
initialization = expression
object_type: Expr = Variable(const.TYPES_BYTES)
Expand Down
34 changes: 34 additions & 0 deletions rflx/model/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ def __init__(
**mty.enum_literals(self.types.values(), self.package),
}

self._resolve_function_calls()

self.error.propagate()

def __repr__(self) -> str:
Expand All @@ -185,6 +187,38 @@ def __str__(self) -> str:
f"is\n{indent(declarations, 3)}begin\n{indent(states, 3)}\nend {self.identifier.name}"
)

def _resolve_function_calls(self) -> None:
"""
Replace variables by function calls where necessary.
The distinction between function calls without arguments and variables is not possible in
the parser, as both are syntactically identical.
"""
functions = [
p.identifier
for p in self.parameters.values()
if isinstance(p, decl.FunctionDeclaration)
]

def substitution(expression: expr.Expr) -> expr.Expr:
if isinstance(expression, expr.Variable) and expression.identifier in functions:
return expr.Call(expression.identifier, location=expression.location)
return expression

for d in self.declarations.values():
if isinstance(d, decl.VariableDeclaration) and d.expression:
d.expression = d.expression.substituted(substitution)

for state in self.states:
for d in state.declarations.values():
if isinstance(d, decl.VariableDeclaration) and d.expression:
d.expression = d.expression.substituted(substitution)
for s in state.actions:
if isinstance(s, stmt.Assignment):
s.expression = s.expression.substituted(substitution)
if isinstance(s, stmt.AttributeStatement):
s.parameters = [p.substituted(substitution) for p in s.parameters]


class Session(AbstractSession):
# pylint: disable=too-many-arguments
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/generator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,12 @@ def test_session_declare(
@pytest.mark.parametrize(
"type_, expression, error_type, error_msg",
[
(
rty.Integer("T"),
expr.Call("F", location=Location((10, 20))),
RecordFluxError,
r"initialization using function call not yet supported",
),
(
rty.Message("T"),
expr.BooleanTrue(location=Location((10, 20))),
Expand Down
48 changes: 48 additions & 0 deletions tests/unit/model/session_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2102,3 +2102,51 @@ def test_unnecessary_exception_transition() -> None:
types=[],
regex=r'^<stdin>:10:20: model: error: unnecessary exception transition in state "Start"$',
)


def test_resolving_of_function_calls() -> None:
session = Session(
identifier="P::S",
initial=ID("Start"),
final=ID("End"),
states=[
State(
"Start",
declarations=[
decl.VariableDeclaration("Local", "Boolean", expr.Variable("Func")),
],
actions=[
stmt.Assignment("Global", expr.Variable("Func")),
],
transitions=[
Transition(
target=ID("End"),
condition=expr.And(expr.Variable("Global"), expr.Variable("Local")),
),
Transition(
target=ID("End"),
),
],
),
State("End"),
],
declarations=[
decl.VariableDeclaration("Global", "Boolean", expr.Variable("Func")),
],
parameters=[
decl.FunctionDeclaration("Func", [], "Boolean"),
],
types=[BOOLEAN, OPAQUE, TLV_MESSAGE],
)

global_decl = session.declarations[ID("Global")]
assert isinstance(global_decl, decl.VariableDeclaration)
assert global_decl.expression == expr.Call("Func")

local_decl = session.states[0].declarations[ID("Local")]
assert isinstance(local_decl, decl.VariableDeclaration)
assert local_decl.expression == expr.Call("Func")

local_stmt = session.states[0].actions[0]
assert isinstance(local_stmt, stmt.Assignment)
assert local_stmt.expression == expr.Call("Func")

0 comments on commit 02ab55d

Please sign in to comment.