Skip to content

Commit

Permalink
Add check of element type of array
Browse files Browse the repository at this point in the history
Ref. #311
  • Loading branch information
treiher committed Jul 23, 2020
1 parent cf119b4 commit 0e1b735
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 17 deletions.
24 changes: 21 additions & 3 deletions rflx/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,14 +405,30 @@ def __init__(self, identifier: StrID, element_type: Type, location: Location = N
super().__init__(identifier, location)
self.element_type = element_type

if not isinstance(element_type, Scalar) and not (
isinstance(element_type, Message) and element_type.structure
):
self.error.append(
f'invalid element type of array "{self.name}"',
Subsystem.MODEL,
Severity.ERROR,
location,
)
self.error.append(
f'type "{element_type.name}" must be scalar or non-null message',
Subsystem.MODEL,
Severity.INFO,
element_type.location,
)

@property
def element_size(self) -> Expr:
return Length(self.element_type.name)


class Opaque(Composite):
def __init__(self) -> None:
super().__init__(INTERNAL_PACKAGE * "Opaque")
def __init__(self, location: Location = None) -> None:
super().__init__(INTERNAL_PACKAGE * "Opaque", location)

@property
def element_size(self) -> Expr:
Expand Down Expand Up @@ -1721,8 +1737,10 @@ def qualified_type_name(name: ID, package: ID) -> ID:
return name


OPAQUE = Opaque(location=Location((0, 0), Path(str(BUILTINS_PACKAGE)), (0, 0)))

INTERNAL_TYPES = {
Opaque().identifier: Opaque(),
OPAQUE.identifier: OPAQUE,
}

BOOLEAN = Enumeration(
Expand Down
16 changes: 12 additions & 4 deletions rflx/parser/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ def __init__(self, items: List[StrID]) -> None:
self.items = list(map(ID, items))


class ReferenceSpec(Type):
pass


class ArraySpec(Type):
def __init__(
self, identifier: StrID, element_type: ReferenceSpec, location: Location = None
) -> None:
super().__init__(identifier, location)
self.element_type = element_type


class MessageSpec(Type):
def __init__(
self, identifier: StrID, components: List[Component], location: Location = None
Expand Down Expand Up @@ -98,10 +110,6 @@ def __init__(
)


class ReferenceSpec(Type):
pass


class Specification(SyntaxTree):
def __init__(self, context: ContextSpec, package: PackageSpec) -> None:
self.context = context
Expand Down
5 changes: 3 additions & 2 deletions rflx/parser/grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,10 @@
Variable,
)
from rflx.identifier import ID
from rflx.model import Array, Enumeration, ModularInteger, RangeInteger, Type, qualified_type_name
from rflx.model import Enumeration, ModularInteger, RangeInteger, Type, qualified_type_name

from .ast import (
ArraySpec,
Component,
ContextSpec,
DerivationSpec,
Expand Down Expand Up @@ -616,7 +617,7 @@ def parse_type(string: str, location: int, tokens: ParseResults) -> Type:
if tokens[3] == "new":
return DerivationSpec(identifier, tokens[4], locn)
if tokens[3] == "array of":
return Array(
return ArraySpec(
identifier,
ReferenceSpec(qualified_type_name(tokens[4], package), tokens[4].location),
locn,
Expand Down
18 changes: 13 additions & 5 deletions rflx/parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
pop_source,
push_source,
)
from rflx.expression import UNDEFINED, Number
from rflx.expression import UNDEFINED
from rflx.identifier import ID
from rflx.model import (
BUILTIN_TYPES,
Expand All @@ -38,7 +38,15 @@
)

from . import grammar
from .ast import Component, DerivationSpec, MessageSpec, PackageSpec, RefinementSpec, Specification
from .ast import (
ArraySpec,
Component,
DerivationSpec,
MessageSpec,
PackageSpec,
RefinementSpec,
Specification,
)

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -165,7 +173,7 @@ def __evaluate_types(self, spec: Specification, error: RecordFluxError) -> None:
if isinstance(t, Scalar):
new_type = t

elif isinstance(t, Array):
elif isinstance(t, ArraySpec):
new_type = create_array(t, self.__types)

elif isinstance(t, MessageSpec):
Expand All @@ -191,7 +199,7 @@ def message_types(types: Mapping[ID, Type]) -> Mapping[ID, Message]:
return {n: m for n, m in types.items() if isinstance(m, Message)}


def create_array(array: Array, types: Mapping[ID, Type]) -> Array:
def create_array(array: ArraySpec, types: Mapping[ID, Type]) -> Array:
array.element_type.identifier = ID(
array.element_type.full_name.replace("__PACKAGE__", str(array.package)), array.location
)
Expand Down Expand Up @@ -222,7 +230,7 @@ def create_array(array: Array, types: Mapping[ID, Type]) -> Array:
)
error.propagate()

return Array(array.identifier, element_type)
return Array(array.identifier, element_type, array.location)


def create_message(message: MessageSpec, types: Mapping[ID, Type]) -> Message:
Expand Down
19 changes: 19 additions & 0 deletions tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
BUILTIN_TYPES,
FINAL,
INITIAL,
OPAQUE,
Array,
DerivedMessage,
Enumeration,
Expand Down Expand Up @@ -326,6 +327,24 @@ def test_enumeration_invalid_literal() -> None:
)


def test_array_invalid_element_type() -> None:
assert_type_error(
Array("P.A", Array("P.B", MODULAR_INTEGER, Location((3, 4))), Location((5, 4))),
r'^<stdin>:5:4: model: error: invalid element type of array "A"\n'
r'<stdin>:3:4: model: info: type "B" must be scalar or non-null message$',
)
assert_type_error(
Array("P.A", Message("P.B", [], {}, Location((3, 4))), Location((5, 4))),
r'^<stdin>:5:4: model: error: invalid element type of array "A"\n'
r'<stdin>:3:4: model: info: type "B" must be scalar or non-null message$',
)
assert_type_error(
Array("P.A", OPAQUE, Location((5, 4))),
r'^<stdin>:5:4: model: error: invalid element type of array "A"\n'
r'__BUILTINS__:0:0: model: info: type "Opaque" must be scalar or non-null message$',
)


def test_message_incorrect_name() -> None:
with pytest.raises(
RecordFluxError, match='^<stdin>:10:8: model: error: unexpected format of type name "M"$'
Expand Down
6 changes: 3 additions & 3 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
from rflx.model import (
FINAL,
INITIAL,
Array,
DerivedMessage,
Enumeration,
Field,
Expand All @@ -43,6 +42,7 @@
)
from rflx.parser import grammar, parser
from rflx.parser.ast import (
ArraySpec,
ContextSpec,
DerivationSpec,
MessageSpec,
Expand Down Expand Up @@ -815,7 +815,7 @@ def test_array_type_spec() -> None:
"Array_Type",
[
ModularInteger("__PACKAGE__.Byte", Number(256)),
Array("__PACKAGE__.Bytes", ReferenceSpec("__PACKAGE__.Byte")),
ArraySpec("__PACKAGE__.Bytes", ReferenceSpec("__PACKAGE__.Byte")),
MessageSpec(
"__PACKAGE__.Foo",
[
Expand All @@ -827,7 +827,7 @@ def test_array_type_spec() -> None:
Component("Bytes", "Bytes"),
],
),
Array("__PACKAGE__.Bar", ReferenceSpec("__PACKAGE__.Foo")),
ArraySpec("__PACKAGE__.Bar", ReferenceSpec("__PACKAGE__.Foo")),
],
),
)
Expand Down

0 comments on commit 0e1b735

Please sign in to comment.