Skip to content

Commit

Permalink
Replace SubprogramCall by Call
Browse files Browse the repository at this point in the history
Ref. #47
  • Loading branch information
Alexander Senier authored and treiher committed Aug 19, 2020
1 parent 3d9fba9 commit 78f3c07
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 211 deletions.
223 changes: 98 additions & 125 deletions rflx/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -1115,10 +1115,10 @@ def substituted(


class Call(Name):
def __init__(self, name: StrID, args: Sequence[Expr] = None, negative: bool = False) -> None:
self.name = name
def __init__(self, name: StrID, args: Sequence[Expr] = None, negative: bool = False, location: Location = None) -> None:
super().__init__(negative, location)
self.name = ID(name)
self.args = args or []
super().__init__(negative)

def __neg__(self) -> "Call":
return self.__class__(self.name, self.args, not self.negative)
Expand All @@ -1134,6 +1134,101 @@ def representation(self) -> str:
def z3expr(self) -> z3.ExprRef:
raise NotImplementedError

def __validate_channel(
self, declarations: Mapping[ID, "Declaration"], error: RecordFluxError
) -> None:
if len(self.args) < 1:
fail(
f'no channel argument in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)
channel_id = self.args[0]
if not isinstance(channel_id, Variable):
fail(
f'invalid channel ID type in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)
assert isinstance(channel_id, Variable)
if channel_id.identifier not in declarations:
fail(
f'undeclared channel "{channel_id}" in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)

assert isinstance(channel_id, Variable)
channel = declarations[channel_id.identifier]
if not isinstance(channel, Channel):
fail(
f'invalid channel type in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)

assert isinstance(channel, Channel)
channel.reference()
if self.name in map(ID, ["Write", "Call"]) and not channel.writable:
error.append(
f'channel "{channel_id}" not writable in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)
if self.name in map(ID, ["Call", "Read", "Data_Available"]) and not channel.readable:
error.append(
f'channel "{channel_id}" not readable in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)
for a in self.args[1:]:
a.validate(declarations)

def validate(self, declarations: Mapping[ID, "Declaration"]) -> None:
error = RecordFluxError()
if self.name in map(ID, ["Read", "Write", "Call", "Data_Available"]):
self.__validate_channel(declarations, error)
else:
if self.name not in map(ID, ["Append", "Extend"]):
if self.name not in declarations:
fail(
f'undeclared subprogram "{self.name}" called',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)
declarations[self.name].reference()
for a in self.args:
try:
a.validate(declarations)
except RecordFluxError as e:
error.extend(e)
error.propagate()

def variables(self) -> List["Variable"]:
result = []
for t in self.args:
result.extend(t.variables())
return result

@require(lambda func, mapping: (func and mapping is None) or (not func and mapping is not None))
def substituted(
self, func: Callable[[Expr], Expr] = None, mapping: Mapping[Name, Expr] = None
) -> Expr:
func = substitution(mapping or {}, func)
expr = func(self)
if isinstance(expr, Call):
return expr.__class__(
expr.name, [a.substituted(func) for a in expr.args], expr.location
)
return expr


class Slice(Name):
def __init__(self, prefix: Expr, first: Expr, last: Expr) -> None:
Expand Down Expand Up @@ -1786,128 +1881,6 @@ def validate(self, declarations: Mapping[ID, "Declaration"]) -> None:
pass


class SubprogramCall(Expr):
def __init__(self, name: StrID, arguments: List[Expr], location: Location = None) -> None:
super().__init__(location)
self.name = ID(name)
self.arguments = arguments
self.error = RecordFluxError()

def __str__(self) -> str:
arguments = ", ".join([f"{a}" for a in self.arguments])
return f"{self.name} ({arguments})"

def __neg__(self) -> Expr:
raise NotImplementedError

def simplified(self) -> Expr:
first = [a if isinstance(a, ID) else a.simplified() for a in self.arguments[0:1]]
arguments = [
*first,
*[a.simplified() for a in self.arguments[1:]],
]
return SubprogramCall(self.name, arguments, self.location)

@require(lambda func, mapping: (func and mapping is None) or (not func and mapping is not None))
def substituted(
self, func: Callable[[Expr], Expr] = None, mapping: Mapping[Name, Expr] = None
) -> Expr:
func = substitution(mapping or {}, func)
expr = func(self)
if isinstance(expr, SubprogramCall):
return expr.__class__(
expr.name, [a.substituted(func) for a in expr.arguments], expr.location
)
return expr

@property
def precedence(self) -> Precedence:
return Precedence.undefined

def z3expr(self) -> z3.ExprRef:
raise NotImplementedError

def __validate_channel(self, declarations: Mapping[ID, Declaration]) -> None:
if len(self.arguments) < 1:
fail(
f'no channel argument in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)
channel_id = self.arguments[0]
if not isinstance(channel_id, Variable):
fail(
f'invalid channel ID type in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)
assert isinstance(channel_id, Variable)
if channel_id.identifier not in declarations:
fail(
f'undeclared channel "{channel_id}" in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)

assert isinstance(channel_id, Variable)
channel = declarations[channel_id.identifier]
if not isinstance(channel, Channel):
fail(
f'invalid channel type in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)

assert isinstance(channel, Channel)
channel.reference()
if self.name.name in map(ID, ["Write", "Call"]) and not channel.writable:
self.error.append(
f'channel "{channel_id}" not writable in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)
if self.name.name in map(ID, ["Call", "Read", "Data_Available"]) and not channel.readable:
self.error.append(
f'channel "{channel_id}" not readable in call to "{self.name}"',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)
for a in self.arguments[1:]:
a.validate(declarations)

def validate(self, declarations: Mapping[ID, Declaration]) -> None:
if self.name in map(ID, ["Read", "Write", "Call", "Data_Available"]):
self.__validate_channel(declarations)
else:
if self.name not in map(ID, ["Append", "Extend"]):
if self.name not in declarations:
fail(
f'undeclared subprogram "{self.name}" called',
Subsystem.SESSION,
Severity.ERROR,
self.location,
)
declarations[self.name].reference()
for a in self.arguments:
try:
a.validate(declarations)
except RecordFluxError as e:
self.error.extend(e)
self.error.propagate()

def variables(self) -> List["Variable"]:
result = []
for t in self.arguments:
result.extend(t.variables())
return result


class Conversion(Expr):
def __init__(self, name: StrID, argument: Expr, location: Location = None) -> None:
super().__init__(location)
Expand Down
10 changes: 4 additions & 6 deletions rflx/parser/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
And,
Argument,
Binding,
Call,
Comprehension,
Conversion,
Div,
Expand All @@ -49,7 +50,6 @@
String,
Sub,
Subprogram,
SubprogramCall,
Valid,
Variable,
VariableDeclaration,
Expand Down Expand Up @@ -79,13 +79,13 @@ def __parse_comprehension(tokens: List[Expr]) -> Expr:

def __parse_call(tokens: List[Expr]) -> Expr:
assert isinstance(tokens[0], ID)
return SubprogramCall(tokens[0], tokens[1:])
return Call(tokens[0], tokens[1:])


def __parse_conversion(tokens: List[Expr]) -> Expr:
assert isinstance(tokens[0], ID)
if tokens[0] in map(ID, ["Read", "Write", "Call", "Data_Available"]):
return SubprogramCall(tokens[0], tokens[1:])
return Call(tokens[0], tokens[1:])
return Conversion(tokens[0], tokens[1])


Expand Down Expand Up @@ -312,9 +312,7 @@ def action() -> Token:
list_operation = (
unqualified_identifier() + Literal("'").suppress() + attribute_designator + parameters
)
list_operation.setParseAction(
lambda t: Assignment(t[0], SubprogramCall(t[1], [Variable(t[0]), t[2]]))
)
list_operation.setParseAction(lambda t: Assignment(t[0], Call(t[1], [Variable(t[0]), t[2]])))

list_reset = unqualified_identifier() + Literal("'").suppress() + Keyword("Reset")
list_reset.setParseAction(lambda t: Reset(t[0]))
Expand Down
8 changes: 4 additions & 4 deletions tests/test_session_action.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from rflx.expression import String, SubprogramCall, Variable
from rflx.expression import Call, String, Variable
from rflx.parser.session import action
from rflx.statement import Assignment, Erase, Reset

Expand All @@ -10,21 +10,21 @@ def test_simple_assignment() -> None:

def test_simple_subprogram_call() -> None:
result = action().parseString("Sub (Arg)")[0]
expected = SubprogramCall("Sub", [Variable("Arg")])
expected = Call("Sub", [Variable("Arg")])
assert result == expected


def test_list_append() -> None:
result = action().parseString("Extensions_List'Append (Foo)")[0]
expected = Assignment(
"Extensions_List", SubprogramCall("Append", [Variable("Extensions_List"), Variable("Foo")]),
"Extensions_List", Call("Append", [Variable("Extensions_List"), Variable("Foo")]),
)
assert result == expected


def test_subprogram_string_argument() -> None:
result = action().parseString('Sub (Arg1, "String arg", Arg2)')[0]
expected = SubprogramCall("Sub", [Variable("Arg1"), String("String arg"), Variable("Arg2")])
expected = Call("Sub", [Variable("Arg1"), String("String arg"), Variable("Arg2")])
assert result == expected


Expand Down
14 changes: 6 additions & 8 deletions tests/test_session_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Add,
And,
Binding,
Call,
Comprehension,
Conversion,
Div,
Expand All @@ -26,7 +27,6 @@
Selected,
String,
Sub,
SubprogramCall,
Valid,
Variable,
)
Expand Down Expand Up @@ -199,7 +199,7 @@ def test_type_conversion_simple() -> None:

def test_field_simple() -> None:
result = expression().parseString("Bar (Foo).Fld")[0]
assert result == Selected(SubprogramCall("Bar", [Variable("Foo")]), "Fld")
assert result == Selected(Call("Bar", [Variable("Foo")]), "Fld")


def test_field_variable() -> None:
Expand All @@ -209,7 +209,7 @@ def test_field_variable() -> None:

def test_field_length() -> None:
result = expression().parseString("Bar (Foo).Fld'Length")[0]
assert result == Length(Selected(SubprogramCall("Bar", [Variable("Foo")]), "Fld"))
assert result == Length(Selected(Call("Bar", [Variable("Foo")]), "Fld"))


def test_type_conversion() -> None:
Expand Down Expand Up @@ -257,9 +257,7 @@ def test_length_lt() -> None:

def test_field_length_lt() -> None:
result = expression().parseString("Bar (Foo).Fld'Length < 100")[0]
assert result == Less(
Length(Selected(SubprogramCall("Bar", [Variable("Foo")]), "Fld")), Number(100)
)
assert result == Less(Length(Selected(Call("Bar", [Variable("Foo")]), "Fld")), Number(100))


def test_list_comprehension() -> None:
Expand Down Expand Up @@ -375,13 +373,13 @@ def test_complex_aggregate() -> None:

def test_simple_function_call() -> None:
result = expression().parseString("Fun (Parameter)")[0]
expected = SubprogramCall("Fun", [Variable("Parameter")])
expected = Call("Fun", [Variable("Parameter")])
assert result == expected


def test_complex_function_call() -> None:
result = expression().parseString("Complex_Function (Param1, Param2, Param3)")[0]
expected = SubprogramCall(
expected = Call(
"Complex_Function", [Variable("Param1"), Variable("Param2"), Variable("Param3")],
)
assert result == expected
Expand Down
Loading

0 comments on commit 78f3c07

Please sign in to comment.