Skip to content

Commit

Permalink
Add support for relations with aggregates and variables in pyrflx
Browse files Browse the repository at this point in the history
Ref: #964
  • Loading branch information
rssen committed May 5, 2022
1 parent 1807e83 commit 744b8f8
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 11 deletions.
24 changes: 22 additions & 2 deletions rflx/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -1604,6 +1604,15 @@ def simplified(self) -> Expr:
def length(self) -> Expr:
return Number(len(self.elements))

def to_bytes(self) -> bytes:
if not all((isinstance(element, Number) for element in self.elements)):
return NotImplemented
return b"".join(
element.value.to_bytes((element.value.bit_length() + 7) // 8, "big")
for element in self.elements
if isinstance(element, Number)
)

def ada_expr(self) -> ada.Expr:
return ada.Aggregate(*[e.ada_expr() for e in self.elements])

Expand Down Expand Up @@ -1710,8 +1719,19 @@ def _simplified(self, relation_operator: Callable[[Expr, Expr], bool]) -> Expr:
}
if (relation_operator, left, right) in mapping:
return mapping[(relation_operator, left, right)]
if isinstance(left, Number) and isinstance(right, Number):
return TRUE if relation_operator(left, right) else FALSE
if isinstance(left, (Number, Aggregate)) and isinstance(right, (Number, Aggregate)):
left_number = (
Number(int.from_bytes(left.to_bytes(), "big"))
if isinstance(left, Aggregate)
else left
)
right_number = (
Number(int.from_bytes(right.to_bytes(), "big"))
if isinstance(right, Aggregate)
else right
)
assert isinstance(left_number, Number) and isinstance(right_number, Number)
return TRUE if relation_operator(left_number, right_number) else FALSE
return self.__class__(left, right)

@property
Expand Down
22 changes: 14 additions & 8 deletions rflx/pyrflx/typevalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -1161,14 +1161,6 @@ def _calculate_checksum(self, checksum: "MessageValue.Checksum") -> int:
expr_tuple.evaluated_expression.lower.value,
expr_tuple.evaluated_expression.upper.value,
)
elif isinstance(expr_tuple.evaluated_expression, Variable):
assert (
expr_tuple.evaluated_expression.name in self.fields
and self._fields[expr_tuple.evaluated_expression.name].set
)
val = self._fields[expr_tuple.evaluated_expression.name].typeval.value
assert isinstance(val, bytes)
arguments[str(expr_tuple.expression)] = val
else:
assert isinstance(expr_tuple.evaluated_expression, Number)
arguments[str(expr_tuple.expression)] = expr_tuple.evaluated_expression.value
Expand Down Expand Up @@ -1364,6 +1356,20 @@ def subst(expression: Expr) -> Expr:
if expression in self._parameters:
assert isinstance(expression, Name)
return self._parameters[expression]
if isinstance(expression, Variable) and expression.name in self.fields:
if self._fields[expression.identifier.flat].set:
exp_value = self._fields[expression.identifier.flat].typeval.value
if isinstance(exp_value, bytes):
return Number(int.from_bytes(exp_value, "big"))
if (
isinstance(exp_value, list)
and len(exp_value) > 0
and isinstance(exp_value[0], IntegerValue)
):
return Number(
int.from_bytes(b"".join([bytes(v.bitstring) for v in exp_value]), "big")
)
return NotImplemented
return expression

return expr.substituted(func=subst).substituted(func=subst).simplified()
Expand Down
8 changes: 7 additions & 1 deletion tests/data/fixtures/pyrflx.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def fixture_pyrflx() -> pyrflx.PyRFLX:
f"{SPEC_DIR}/parameterized.rflx",
f"{SPEC_DIR}/endianness.rflx",
f"{SPEC_DIR}/low_order.rflx",
f"{SPEC_DIR}/aggregate_in_relation.rflx",
],
skip_model_verification=True,
)
Expand Down Expand Up @@ -235,5 +236,10 @@ def fixture_endianness_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package:


@pytest.fixture(name="low_order_package", scope="session")
def fixure_low_order_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package:
def fixture_low_order_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package:
return pyrflx_.package("Low_Order")


@pytest.fixture(name="aggregate_in_relation_package", scope="session")
def fixture_aggregate_in_relation_package(pyrflx_: pyrflx.PyRFLX) -> pyrflx.Package:
return pyrflx_.package("Aggregate_In_Relation")
32 changes: 32 additions & 0 deletions tests/data/specs/aggregate_in_relation.rflx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package Aggregate_In_Relation is

type B is mod 2 ** 8;
type C is mod 2 ** 8;
type D is mod 2 ** 8;

type E is mod 2 ** 8;
type S is sequence of E;

type Aggregate_In_Relation_Msg is
message
Fld_A : Opaque
with Size => 16
then Fld_B
if Fld_A = [16#AA#, 16#AA#]
then null
if [16#AA#, 16#AA#] /= Fld_A;
Fld_B : B
then Fld_C
if [16#01#, 16#02#] = [16#01#, 16#02#]
then null
if [16#01#, 16#02#] /= [16#01#, 16#02#];
Fld_C : S
with Size => 16
then Fld_D
if Fld_C = [16#CC#, 16#CC#]
then null
if Fld_C /= [16#CC#, 16#CC#];
Fld_D : D;
end message;

end Aggregate_In_Relation;
26 changes: 26 additions & 0 deletions tests/unit/expression_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,26 @@ def test_relation_simplified() -> None:
Equal(Add(Number(1), Number(1)), Add(Number(1), Number(1))).simplified(),
TRUE,
)
assert_equal(Equal(String("Foo Bar"), String("Foo Bar")).simplified(), TRUE)
assert_equal(
Equal(String("Foo"), Aggregate(Number(70), Number(111), Number(111))).simplified(), TRUE
)
assert_equal(
Equal(
Aggregate(Number(0), Number(1), Number(2)), Aggregate(Number(0), Number(1), Number(2))
).simplified(),
TRUE,
)
assert_equal(
Equal(
Aggregate(Number(1), Number(2), Number(3)), Aggregate(Number(4), Number(5), Number(6))
).simplified(),
FALSE,
)
assert_equal(Equal(Number(0), Aggregate(Number(0), Number(1), Number(2))).simplified(), FALSE)
assert_equal(Equal(Aggregate(Number(0), Number(1), Number(2)), Number(0)).simplified(), FALSE)
assert_equal(NotEqual(Number(4), Aggregate(Number(0), Number(1), Number(2))).simplified(), TRUE)
assert_equal(NotEqual(Number(0), Aggregate(Number(0), Number(1), Number(2))).simplified(), TRUE)
assert Equal(TRUE, TRUE).simplified() == TRUE
assert Equal(TRUE, FALSE).simplified() == FALSE
assert NotEqual(TRUE, TRUE).simplified() == FALSE
Expand Down Expand Up @@ -1573,6 +1593,12 @@ def test_string_simplified() -> None:
assert String("Test").simplified() == String("Test")


def test_string_substituted() -> None:
assert String("Test").substituted(
lambda x: String("TestSub") if x == String("Test") else x
) == String("TestSub")


def test_string_elements() -> None:
assert String("Test").elements == [Number(84), Number(101), Number(115), Number(116)]

Expand Down
22 changes: 22 additions & 0 deletions tests/unit/pyrflx_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def test_pyrflx_iterator(pyrflx_: PyRFLX) -> None:
"Message_Type_Size_Condition",
"Always_Valid_Aspect",
"Low_Order",
"Aggregate_In_Relation",
}


Expand Down Expand Up @@ -1515,3 +1516,24 @@ def test_low_order(low_order_package: Package) -> None:

assert m1.valid_message
assert m1.bytestring == b"\x01\x00"


def test_aggregate_in_relation_invalid(aggregate_in_relation_package: Package) -> None:
msg = aggregate_in_relation_package.new_message("Aggregate_In_Relation_Msg")
with pytest.raises(
PyRFLXError,
match=(
"^"
"pyrflx: error: Bitstring representing the message is too short"
" - stopped while parsing field: Fld_B"
"$"
),
):
msg.parse(b"\xAA\xAA")


def test_aggregate_in_relation_valid(aggregate_in_relation_package: Package) -> None:
msg = aggregate_in_relation_package.new_message("Aggregate_In_Relation_Msg")
msg.parse(b"\xAA\xAA\xBB\xCC\xCC\xDD")
assert msg.bytestring == b"\xAA\xAA\xBB\xCC\xCC\xDD"
assert msg.valid_message

0 comments on commit 744b8f8

Please sign in to comment.