def parameter_value(parameter: model.Field, parameter_type: model.Type) -> expr.Expr: if isinstance(parameter_type, model.Enumeration): if embedded: return expr.Call("To_Base_Integer", [expr.Variable(parameter.name)]) return expr.Call("To_Base_Integer", [expr.Variable("Ctx" * parameter.identifier)]) if isinstance(parameter_type, model.Scalar): if embedded: return expr.Variable(parameter.name) return expr.Variable("Ctx" * parameter.identifier) assert False, f'unexpected type "{type(parameter_type).__name__}"'
def field_value(field: model.Field) -> expr.Expr: if public: return expr.Call(f"Get_{field.name}", [expr.Variable("Ctx")]) return expr.Selected( expr.Indexed( expr.Variable(ID("Ctx") * "Cursors" if not embedded else "Cursors"), expr.Variable(field.affixed_name), ), "Value", )
def valid_predecessors_invariant() -> Expr: return AndThen( *[ If( [ ( Call( "Structural_Valid", [ Indexed( Variable("Cursors"), Variable(f.affixed_name), ) ], ), Or( *[ expr.AndThen( expr.Call( "Structural_Valid" if l.source in composite_fields else "Valid", [ expr.Indexed( expr.Variable("Cursors"), expr.Variable(l.source.affixed_name), ) ], ), expr.Equal( expr.Selected( expr.Indexed( expr.Variable("Cursors"), expr.Variable(f.affixed_name), ), "Predecessor", ), expr.Variable(l.source.affixed_name), ), l.condition.substituted( substitution(message, embedded=True, prefix=prefix) ), ) .simplified() .ada_expr() for l in message.incoming(f) ] ), ) ] ) for f in message.fields if f not in message.direct_successors(model.INITIAL) ] )
def field_value(field: model.Field, field_type: model.Type) -> expr.Expr: if isinstance(field_type, model.Enumeration): if public: return expr.Call( "To_Base_Integer", [expr.Call(f"Get_{field.name}", [expr.Variable("Ctx")])] ) return expr.Selected( expr.Indexed(cursors, expr.Variable(field.affixed_name)), "Value", ) if isinstance(field_type, model.Scalar): if public: return expr.Call(f"Get_{field.name}", [expr.Variable("Ctx")]) return expr.Selected( expr.Indexed(cursors, expr.Variable(field.affixed_name)), "Value", ) if isinstance(field_type, model.Composite): return expr.Variable(field.name) assert False, f'unexpected type "{type(field_type).__name__}"'
def field_size(field: model.Field) -> expr.Expr: if public: return expr.Call( "Field_Size", [expr.Variable("Ctx"), expr.Variable(field.affixed_name)] ) return expr.Add( expr.Sub( expr.Selected(expr.Indexed(cursors, expr.Variable(field.affixed_name)), "Last"), expr.Selected(expr.Indexed(cursors, expr.Variable(field.affixed_name)), "First"), ), expr.Number(1), )
def valid_message_condition(self, message: Message, structural: bool = False) -> Expr: return (expr.Or(*[ expr.AndThen( expr.Call( "Structural_Valid" if structural and isinstance( message.field_types[l.source], Composite) else "Valid", [ expr.Variable("Ctx"), expr.Variable(l.source.affixed_name, immutable=True), ], ), l.condition, ) for l in message.incoming(FINAL) if l.target == FINAL ]).substituted(common.substitution( message, self.prefix)).simplified().ada_expr())
def test_substitution_relation_aggregate( relation: Callable[[expr.Expr, expr.Expr], expr.Relation], left: expr.Expr, right: expr.Expr ) -> None: equal_call = expr.Call( "Equal", [ expr.Variable("Ctx"), expr.Variable("F_Value"), expr.Aggregate( expr.Val(expr.Variable("Types.Byte"), expr.Number(1)), expr.Val(expr.Variable("Types.Byte"), expr.Number(2)), ), ], ) assert_equal( relation(left, right).substituted(common.substitution(TLV_MESSAGE)), equal_call if relation == expr.Equal else expr.Not(equal_call), )
def test_consistency_specification_parsing_generation(tmp_path: Path) -> None: tag = Enumeration( "Test::Tag", [("Msg_Data", expr.Number(1)), ("Msg_Error", expr.Number(3))], expr.Number(8), always_valid=False, ) length = ModularInteger("Test::Length", expr.Pow(expr.Number(2), expr.Number(16))) message = Message( "Test::Message", [ Link(INITIAL, Field("Tag")), Link( Field("Tag"), Field("Length"), expr.Equal(expr.Variable("Tag"), expr.Variable("Msg_Data")), ), Link(Field("Tag"), FINAL, expr.Equal(expr.Variable("Tag"), expr.Variable("Msg_Error"))), Link( Field("Length"), Field("Value"), size=expr.Mul(expr.Variable("Length"), expr.Number(8)), ), Link(Field("Value"), FINAL), ], { Field("Tag"): tag, Field("Length"): length, Field("Value"): OPAQUE }, skip_proof=True, ) session = Session( "Test::Session", "A", "C", [ State( "A", declarations=[], actions=[stmt.Read("X", expr.Variable("M"))], transitions=[ Transition("B"), ], ), State( "B", declarations=[ decl.VariableDeclaration("Z", BOOLEAN.identifier, expr.Variable("Y")), ], actions=[], transitions=[ Transition( "C", condition=expr.And( expr.Equal(expr.Variable("Z"), expr.TRUE), expr.Equal(expr.Call("G", [expr.Variable("F")]), expr.TRUE), ), description="rfc1149.txt+45:4-47:8", ), Transition("A"), ], description="rfc1149.txt+51:4-52:9", ), State("C"), ], [ decl.VariableDeclaration("M", "Test::Message"), decl.VariableDeclaration("Y", BOOLEAN.identifier, expr.FALSE), ], [ decl.ChannelDeclaration("X", readable=True, writable=True), decl.TypeDeclaration(Private("Test::T")), decl.FunctionDeclaration("F", [], "Test::T"), decl.FunctionDeclaration("G", [decl.Argument("P", "Test::T")], BOOLEAN.identifier), ], [BOOLEAN, OPAQUE, tag, length, message], ) model = Model(types=[BOOLEAN, OPAQUE, tag, length, message], sessions=[session]) model.write_specification_files(tmp_path) p = parser.Parser() p.parse(tmp_path / "test.rflx") parsed_model = p.create_model() assert parsed_model.types == model.types assert parsed_model.sessions == model.sessions assert parsed_model == model
("X and Y", expr.And(expr.Variable("X"), expr.Variable("Y"))), ("X or Y", expr.Or(expr.Variable("X"), expr.Variable("Y"))), ("((X or Y))", expr.Or(expr.Variable("X"), expr.Variable("Y"))), ], ) def test_expression_boolean(string: str, expected: expr.Expr) -> None: actual = parse_bool_expression(string, extended=False) assert actual == expected assert actual.location @pytest.mark.parametrize( "string,expected", [ ("X + Y", expr.Add(expr.Variable("X"), expr.Variable("Y"))), ("X + Y (Z)", expr.Add(expr.Variable("X"), expr.Call("Y", [expr.Variable("Z")]))), ], ) def test_mathematical_expression(string: str, expected: expr.Expr) -> None: actual = parse_math_expression(string, extended=True) assert actual == expected assert actual.location @pytest.mark.parametrize( "string,error", [ ("42 > X", "Invalid math BinOp OpGt.*"), ("X and Y", "Invalid math BinOp OpAnd.*"), ], )
), ], ) assert_equal( relation(left, right).substituted(common.substitution(TLV_MESSAGE)), equal_call if relation == expr.Equal else expr.Not(equal_call), ) @pytest.mark.parametrize( "expressions,expected", [ ( (expr.Variable("Length"), expr.Number(1)), (expr.Call("Get_Length", [expr.Variable("Ctx")]), expr.Number(1)), ), ( (expr.Number(1), expr.Variable("Length")), (expr.Number(1), expr.Call("Get_Length", [expr.Variable("Ctx")])), ), ((expr.Number(1), expr.Variable("Unknown")), (expr.Number(1), expr.Variable("Unknown"))), ], ) @pytest.mark.parametrize( "relation", [expr.Less, expr.LessEqual, expr.Equal, expr.GreaterEqual, expr.Greater, expr.NotEqual], ) def test_substitution_relation_scalar( relation: Callable[[expr.Expr, expr.Expr], expr.Relation], expressions: Tuple[expr.Expr, expr.Expr],
def func(expression: expr.Expr) -> expr.Expr: # pylint: disable = too-many-branches def byte_aggregate(aggregate: expr.Aggregate) -> expr.Aggregate: return expr.Aggregate(*[expr.Val(const.TYPES_BYTE, e) for e in aggregate.elements]) if isinstance(expression, expr.Name) and expression in facts: return facts[expression] if isinstance(expression, expr.String): return expr.Aggregate(*expression.elements) if isinstance(expression, (expr.Equal, expr.NotEqual)): field = None aggregate = None if isinstance(expression.left, expr.Variable) and isinstance( expression.right, expr.Aggregate ): field = model.Field(expression.left.name) aggregate = byte_aggregate(expression.right) elif isinstance(expression.left, expr.Aggregate) and isinstance( expression.right, expr.Variable ): field = model.Field(expression.right.name) aggregate = byte_aggregate(expression.left) if field and field in message.fields and len(field.identifier.parts) == 1 and aggregate: if embedded: return expression.__class__( expr.Indexed( expr.Variable(ID("Buffer") * "all"), expr.ValueRange( expr.Call( const.TYPES_TO_INDEX, [ expr.Selected( expr.Indexed( expr.Variable("Cursors"), expr.Variable(field.affixed_name), ), "First", ) ], ), expr.Call( const.TYPES_TO_INDEX, [ expr.Selected( expr.Indexed( expr.Variable("Cursors"), expr.Variable(field.affixed_name), ), "Last", ) ], ), ), ), aggregate, ) equal_call = expr.Call( "Equal", [expr.Variable("Ctx"), expr.Variable(field.affixed_name), aggregate], ) return equal_call if isinstance(expression, expr.Equal) else expr.Not(equal_call) boolean_literal = None other = None if ( isinstance(expression.left, expr.Literal) and expression.left.identifier in model.BOOLEAN.literals ): boolean_literal = expression.left other = expression.right if ( isinstance(expression.right, expr.Literal) and expression.right.identifier in model.BOOLEAN.literals ): boolean_literal = expression.right other = expression.left if boolean_literal and other: return expression.__class__( other, type_conversion(expr.Call("To_Base_Integer", [boolean_literal])) ) def field_value(field: model.Field) -> expr.Expr: if public: return expr.Call(f"Get_{field.name}", [expr.Variable("Ctx")]) return expr.Selected( expr.Indexed( expr.Variable(ID("Ctx") * "Cursors" if not embedded else "Cursors"), expr.Variable(field.affixed_name), ), "Value", ) if isinstance(expression, expr.Relation): if ( isinstance(expression.left, expr.Variable) and model.Field(expression.left.name) in message.fields and isinstance(expression.right, expr.Number) ): return expression.__class__( field_value(model.Field(expression.left.name)), expression.right ) if ( isinstance(expression.right, expr.Variable) and model.Field(expression.right.name) in message.fields and isinstance(expression.left, expr.Number) ): return expression.__class__( expression.left, field_value(model.Field(expression.right.name)) ) return expression
def type_conversion(expression: expr.Expr) -> expr.Expr: return expr.Call(target_type, [expression]) if target_type else expression
def field_last(field: model.Field) -> expr.Expr: if public: return expr.Call( "Field_Last", [expr.Variable("Ctx"), expr.Variable(field.affixed_name)] ) return expr.Selected(expr.Indexed(cursors, expr.Variable(field.affixed_name)), "Last")
def substitution_facts( message: model.Message, prefix: str, embedded: bool = False, public: bool = False, target_type: Optional[ID] = const.TYPES_BASE_INT, ) -> Mapping[expr.Name, expr.Expr]: def prefixed(name: str) -> expr.Expr: return expr.Variable(ID("Ctx") * name) if not embedded else expr.Variable(name) first = prefixed("First") last = expr.Call("Written_Last", [expr.Variable("Ctx")]) if public else prefixed("Written_Last") cursors = prefixed("Cursors") def field_first(field: model.Field) -> expr.Expr: if public: return expr.Call( "Field_First", [expr.Variable("Ctx"), expr.Variable(field.affixed_name)] ) return expr.Selected(expr.Indexed(cursors, expr.Variable(field.affixed_name)), "First") def field_last(field: model.Field) -> expr.Expr: if public: return expr.Call( "Field_Last", [expr.Variable("Ctx"), expr.Variable(field.affixed_name)] ) return expr.Selected(expr.Indexed(cursors, expr.Variable(field.affixed_name)), "Last") def field_size(field: model.Field) -> expr.Expr: if public: return expr.Call( "Field_Size", [expr.Variable("Ctx"), expr.Variable(field.affixed_name)] ) return expr.Add( expr.Sub( expr.Selected(expr.Indexed(cursors, expr.Variable(field.affixed_name)), "Last"), expr.Selected(expr.Indexed(cursors, expr.Variable(field.affixed_name)), "First"), ), expr.Number(1), ) def parameter_value(parameter: model.Field, parameter_type: model.Type) -> expr.Expr: if isinstance(parameter_type, model.Enumeration): if embedded: return expr.Call("To_Base_Integer", [expr.Variable(parameter.name)]) return expr.Call("To_Base_Integer", [expr.Variable("Ctx" * parameter.identifier)]) if isinstance(parameter_type, model.Scalar): if embedded: return expr.Variable(parameter.name) return expr.Variable("Ctx" * parameter.identifier) assert False, f'unexpected type "{type(parameter_type).__name__}"' def field_value(field: model.Field, field_type: model.Type) -> expr.Expr: if isinstance(field_type, model.Enumeration): if public: return expr.Call( "To_Base_Integer", [expr.Call(f"Get_{field.name}", [expr.Variable("Ctx")])] ) return expr.Selected( expr.Indexed(cursors, expr.Variable(field.affixed_name)), "Value", ) if isinstance(field_type, model.Scalar): if public: return expr.Call(f"Get_{field.name}", [expr.Variable("Ctx")]) return expr.Selected( expr.Indexed(cursors, expr.Variable(field.affixed_name)), "Value", ) if isinstance(field_type, model.Composite): return expr.Variable(field.name) assert False, f'unexpected type "{type(field_type).__name__}"' def type_conversion(expression: expr.Expr) -> expr.Expr: return expr.Call(target_type, [expression]) if target_type else expression return { **{expr.First("Message"): type_conversion(first)}, **{expr.Last("Message"): type_conversion(last)}, **{expr.Size("Message"): type_conversion(expr.Add(last, -first, expr.Number(1)))}, **{expr.First(f.name): type_conversion(field_first(f)) for f in message.fields}, **{expr.Last(f.name): type_conversion(field_last(f)) for f in message.fields}, **{expr.Size(f.name): type_conversion(field_size(f)) for f in message.fields}, **{ expr.Variable(p.identifier): type_conversion(parameter_value(p, t)) for p, t in message.parameter_types.items() }, **{ expr.Variable(f.name): type_conversion(field_value(f, t)) for f, t in message.field_types.items() }, **{ expr.Literal(l): type_conversion(expr.Call("To_Base_Integer", [expr.Variable(l)])) for t in message.types.values() if isinstance(t, model.Enumeration) and t != model.BOOLEAN for l in t.literals.keys() }, **{ expr.Literal(t.package * l): type_conversion( expr.Call("To_Base_Integer", [expr.Variable(prefix * t.package * l)]) ) for t in message.types.values() if isinstance(t, model.Enumeration) and t != model.BOOLEAN for l in t.literals.keys() }, # https://github.com/Componolit/RecordFlux/issues/276 **{expr.ValidChecksum(f): expr.TRUE for f in message.checksums}, }
def test_call_rflx_expr() -> None: assert ada.Call( "X", [ada.Variable("Y"), ada.Variable("Z")]).rflx_expr() == expr.Call( "X", [expr.Variable("Y"), expr.Variable("Z")])