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 constraints(self, name: str, proof: bool = False, same_package: bool = True) -> ty.Sequence[expr.Expr]: if proof: prefixed_literals = { self.package * l: v for l, v in self.literals.items() } if self.package == const.BUILTINS_PACKAGE: literals = self.literals elif same_package: literals = {**self.literals, **prefixed_literals} else: literals = prefixed_literals result: ty.List[expr.Expr] = [ expr.Or( *[ expr.Equal(expr.Variable(name), expr.Literal(l), self.location) for l in literals ], location=self.location, ) ] result.extend([ expr.Equal(expr.Literal(l), v, self.location) for l, v in literals.items() ]) result.append(expr.Equal(expr.Size(name), self.size, self.location)) return result raise NotImplementedError
def has_size_dependent_condition(message: model.Message, field: model.Field = None) -> bool: field_sizes = {expr.Size(f.name) for f in message.fields} links = message.outgoing(field) if field else message.structure return any( size in field_sizes for link in links for size in link.condition.findall(lambda x: isinstance(x, expr.Size)) )
def constraints(self, name: str, proof: bool = False, same_package: bool = True) -> ty.Sequence[expr.Expr]: if proof: return [ expr.GreaterEqual(expr.Variable(name), self.first, location=self.location), expr.LessEqual(expr.Variable(name), self.last, location=self.location), expr.Equal(expr.Size(name), self.size, location=self.location), ] raise NotImplementedError
@pytest.mark.parametrize( "string,expected", [("X", expr.Variable("X")), ("X::Y", expr.Variable("X::Y"))] ) def test_variable(string: str, expected: decl.Declaration) -> None: actual = parse_expression(string, lang.GrammarRule.variable_rule) assert actual == expected assert actual.location @pytest.mark.parametrize( "string,expected", [ ("X'First", expr.First(expr.Variable("X"))), ("X'Last", expr.Last(expr.Variable("X"))), ("X'Size", expr.Size(expr.Variable("X"))), ("X'Head", expr.Head(expr.Variable("X"))), ("X'Opaque", expr.Opaque(expr.Variable("X"))), ("X'Present", expr.Present(expr.Variable("X"))), ("X'Valid", expr.Valid(expr.Variable("X"))), ("X'Valid_Checksum", expr.ValidChecksum(expr.Variable("X"))), ("X'Has_Data", expr.HasData(expr.Variable("X"))), ("X where X = 42", expr.Binding(expr.Variable("X"), {ID("X"): expr.Number(42)})), ("X'Head.Y", expr.Selected(expr.Head(expr.Variable("X")), "Y")), ], ) def test_expression_suffix(string: str, expected: expr.Expr) -> None: actual = parse_expression(string, lang.GrammarRule.extended_expression_rule) assert actual == expected assert actual.location
def element_size(self) -> expr.Expr: return expr.Size(self.element_type.name)
def __init__(self, identifier: StrID, element_type: Type, location: Location = None) -> None: super().__init__(identifier, location) self.element_type = element_type if not isinstance(element_type, Scalar) and not isinstance( element_type, message.Message): self.error.extend([ ( f'invalid element type of sequence "{self.name}"', Subsystem.MODEL, Severity.ERROR, location, ), ( f'type "{element_type.name}" must be scalar or message', Subsystem.MODEL, Severity.INFO, element_type.location, ), ], ) if isinstance(element_type, Scalar): element_type_size = element_type.size.simplified() if not isinstance(element_type_size, expr.Number) or int(element_type_size) % 8 != 0: self.error.extend([ ( f'unsupported element type size of sequence "{self.name}"', Subsystem.MODEL, Severity.ERROR, location, ), ( f'type "{element_type.name}" has size {element_type_size},' r" must be multiple of 8", Subsystem.MODEL, Severity.INFO, element_type.location, ), ], ) if isinstance(element_type, message.Message): error = ( f'invalid element type of sequence "{self.name}"', Subsystem.MODEL, Severity.ERROR, location, ) if not element_type.structure: self.error.extend([ error, ( "null messages must not be used as sequence element", Subsystem.MODEL, Severity.INFO, element_type.location, ), ], ) if any( bool( e.findall(lambda x: x in [expr.Size("Message"), expr.Last("Message")])) for l in element_type.structure for e in [l.condition, l.size]): self.error.extend([ error, ( "messages used as sequence element must not depend" ' on "Message\'Size" or "Message\'Last"', Subsystem.MODEL, Severity.INFO, element_type.location, ), ], )
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}, }