def fixture_icmp_checksum_message_first(icmp_message: model.Message) -> pyrflx.MessageValue: return pyrflx.MessageValue( icmp_message.copy( structure=[ model.Link( l.source, l.target, condition=expr.And(l.condition, expr.ValidChecksum("Checksum")), ) if l.target == model.FINAL else l for l in icmp_message.structure ], checksums={ ID("Checksum"): [ expr.ValueRange( expr.First("Message"), expr.Sub(expr.First("Checksum"), expr.Number(1)) ), expr.Size("Checksum"), expr.ValueRange( expr.Add(expr.Last("Checksum"), expr.Number(1)), expr.Last("Message") ), ] }, ) )
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), )
expr.Number(1), ), ), ( "A - (B * 2**3 - 1)", expr.Sub( expr.Variable("A"), expr.Sub( expr.Mul(expr.Variable("B"), expr.Pow(expr.Number(2), expr.Number(3))), expr.Number(1), ), ), ), ( "A + B * (-8)", expr.Add(expr.Variable("A"), expr.Mul(expr.Variable("B"), expr.Number(-8))), ), ( "A + B mod 8 * 2", expr.Add( expr.Variable("A"), expr.Mul(expr.Mod(expr.Variable("B"), expr.Number(8)), expr.Number(2)), ), ), ], ) def test_expression_mathematical(string: str, expected: expr.Expr) -> None: actual = parse_math_expression(string, extended=False) assert actual == expected assert actual.location
def message_structure_invariant( message: model.Message, prefix: str, link: model.Link = None, embedded: bool = False ) -> Expr: def prefixed(name: str) -> expr.Expr: return expr.Selected(expr.Variable("Ctx"), name) if not embedded else expr.Variable(name) if not link: return message_structure_invariant( message, prefix, message.outgoing(model.INITIAL)[0], embedded ) source = link.source target = link.target if target == model.FINAL: return TRUE field_type = message.types[target] condition = link.condition.substituted(substitution(message, prefix, embedded)).simplified() size = ( field_type.size if isinstance(field_type, model.Scalar) else link.size.substituted( substitution(message, prefix, embedded, target_type=const.TYPES_BIT_LENGTH) ).simplified() ) first = ( prefixed("First") if source == model.INITIAL else link.first.substituted( substitution(message, prefix, embedded, target_type=const.TYPES_BIT_INDEX) ) .substituted( mapping={ expr.UNDEFINED: expr.Add( expr.Selected( expr.Indexed(prefixed("Cursors"), expr.Variable(source.affixed_name)), "Last", ), expr.Number(1), ) } ) .simplified() ) invariant = [ message_structure_invariant(message, prefix, l, embedded) for l in message.outgoing(target) ] return If( [ ( AndThen( Call( "Structural_Valid", [Indexed(prefixed("Cursors").ada_expr(), Variable(target.affixed_name))], ), *([condition.ada_expr()] if condition != expr.TRUE else []), ), AndThen( Equal( Add( Sub( Selected( Indexed( prefixed("Cursors").ada_expr(), Variable(target.affixed_name), ), "Last", ), Selected( Indexed( prefixed("Cursors").ada_expr(), Variable(target.affixed_name), ), "First", ), ), Number(1), ), size.ada_expr(), ), Equal( Selected( Indexed( prefixed("Cursors").ada_expr(), Variable(target.affixed_name), ), "Predecessor", ), Variable(source.affixed_name), ), Equal( Selected( Indexed( prefixed("Cursors").ada_expr(), Variable(target.affixed_name), ), "First", ), first.ada_expr(), ), *[i for i in invariant if i != TRUE], ), ) ] )
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}, }