示例#1
0
    def create_scalar_accessor_functions(scalar_fields: Mapping[Field, Scalar]) -> UnitPart:
        def specification(field: Field, field_type: Type) -> FunctionSpecification:
            return FunctionSpecification(
                f"Get_{field.name}", field_type.full_name, [Parameter(["Ctx"], "Context")]
            )

        def result(field: Field, field_type: Type) -> Expr:
            value = Selected(
                Indexed("Ctx.Cursors", Name(field.affixed_name)), f"Value.{field.name}_Value"
            )
            if isinstance(field_type, Enumeration):
                return Call("Convert", [value])
            return value

        return UnitPart(
            [
                SubprogramDeclaration(
                    specification(f, t),
                    [
                        Precondition(
                            And(VALID_CONTEXT, Call("Valid", [Name("Ctx"), Name(f.affixed_name)]))
                        )
                    ],
                )
                for f, t in scalar_fields.items()
            ],
            [
                ExpressionFunctionDeclaration(specification(f, t), result(f, t))
                for f, t in scalar_fields.items()
            ],
        )
示例#2
0
 def composite_setter_postconditions(
     self, message: Message, field: Field, field_type: Type
 ) -> Sequence[Expr]:
     return [
         VALID_CONTEXT,
         Call("Has_Buffer", [Name("Ctx")]),
         *self.setter_postconditions(message, field, field_type),
         Call("Structural_Valid", [Name("Ctx"), Name(field.affixed_name)]),
     ]
示例#3
0
 def setter_preconditions(self, field: Field) -> Sequence[Expr]:
     return [
         VALID_CONTEXT,
         Not(Constrained("Ctx")),
         Call("Has_Buffer", [Name("Ctx")]),
         Call("Valid_Next", [Name("Ctx"), Name(field.affixed_name)]),
         LessEqual(
             Call("Field_Last", [Name("Ctx"), Name(field.affixed_name)]),
             Div(Last(self.types.bit_index), Number(2)),
         ),
     ]
示例#4
0
 def field_bit_location_declarations(self, field_name: Name) -> Sequence[Declaration]:
     return [
         ObjectDeclaration(
             ["First"],
             self.types.bit_index,
             Call("Field_First", [Name("Ctx"), field_name]),
             True,
         ),
         ObjectDeclaration(
             ["Last"], self.types.bit_index, Call("Field_Last", [Name("Ctx"), field_name]), True,
         ),
     ]
示例#5
0
 def test_ass_expr_converted(self) -> None:
     self.assertEqual(
         And(Variable("X"), Number(1)).converted(
             lambda x: Name(x.name) if isinstance(x, Variable) else x
         ),
         And(Name("X"), Number(1)),
     )
     self.assertEqual(
         Mul(Variable("X"), Number(1)).converted(
             lambda x: Name("Y") if x == Mul(Variable("X"), Number(1)) else x
         ),
         Name("Y"),
     )
示例#6
0
 def test_bin_expr_converted(self) -> None:
     self.assertEqual(
         Less(Variable("X"), Number(1)).converted(
             lambda x: Name(x.name) if isinstance(x, Variable) else x
         ),
         Less(Name("X"), Number(1)),
     )
     self.assertEqual(
         Sub(Variable("X"), Number(1)).converted(
             lambda x: Name("Y") if x == Sub(Variable("X"), Number(1)) else x
         ),
         Name("Y"),
     )
示例#7
0
 def unbounded_composite_setter_preconditions(
     self, message: Message, field: Field
 ) -> Sequence[Expr]:
     return [
         Call(
             "Field_Condition",
             [Name("Ctx"), NamedAggregate(("Fld", Name(field.affixed_name)))]
             + (
                 [Call("Field_Length", [Name("Ctx"), Name(field.affixed_name)],)]
                 if length_dependent_condition(message)
                 else []
             ),
         ),
         self.common.sufficient_space_for_field_condition(Name(field.affixed_name)),
     ]
示例#8
0
 def result(field: Field, message: Message) -> NamedAggregate:
     aggregate: List[Tuple[str, Expr]] = [("Fld", Name(field.affixed_name))]
     if field in message.fields and isinstance(message.types[field], Scalar):
         aggregate.append(
             (
                 f"{field.name}_Value",
                 Call(
                     "Extract",
                     [
                         Slice("Ctx.Buffer.all", Name("Buffer_First"), Name("Buffer_Last")),
                         Name("Offset"),
                     ],
                 ),
             )
         )
     return NamedAggregate(*aggregate)
示例#9
0
 def result(field: Field, field_type: Type) -> Expr:
     value = Selected(
         Indexed("Ctx.Cursors", Name(field.affixed_name)), f"Value.{field.name}_Value"
     )
     if isinstance(field_type, Enumeration):
         return Call("Convert", [value])
     return value
示例#10
0
    def create_incomplete_function() -> UnitPart:
        specification = FunctionSpecification(
            "Incomplete", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]
        )

        return UnitPart(
            [SubprogramDeclaration(specification, [Precondition(VALID_CONTEXT)])],
            [
                ExpressionFunctionDeclaration(
                    specification,
                    Equal(
                        Selected(Indexed("Ctx.Cursors", Name("Fld")), "State"), Name("S_Incomplete")
                    ),
                )
            ],
        )
示例#11
0
    def create_structural_valid_function() -> UnitPart:
        specification = FunctionSpecification(
            "Structural_Valid",
            "Boolean",
            [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")],
        )

        return UnitPart(
            [SubprogramDeclaration(specification, [Precondition(VALID_CONTEXT)])],
            [
                ExpressionFunctionDeclaration(
                    specification,
                    And(
                        Or(
                            *[
                                Equal(
                                    Selected(Indexed("Ctx.Cursors", Name("Fld")), "State"), Name(s)
                                )
                                for s in ("S_Valid", "S_Structural_Valid")
                            ]
                        )
                    ),
                )
            ],
        )
示例#12
0
 def test_quantified_expression_variables(self) -> None:
     self.assertEqual(
         ForAllOf(
             "A", Name("List"), Add(Variable("X"), Add(Variable("Y"), Variable("Z")))
         ).variables(),
         [Variable("X"), Variable("Y"), Variable("Z")],
     )
示例#13
0
 def valid_path_to_next_field_condition(
     self, message: Message, field: Field, field_type: Type
 ) -> Sequence[Expr]:
     return [
         If(
             [
                 (
                     l.condition,
                     And(
                         Equal(
                             Call("Predecessor", [Name("Ctx"), Name(l.target.affixed_name)],),
                             Name(field.affixed_name),
                         ),
                         Call("Valid_Next", [Name("Ctx"), Name(l.target.affixed_name)])
                         if l.target != FINAL
                         else TRUE,
                     ),
                 )
             ]
         ).simplified(
             {
                 **{
                     Variable(field.name): Call("Convert", [Name("Value")])
                     if isinstance(field_type, Enumeration) and field_type.always_valid
                     else Name("Value")
                 },
                 **self.public_substitution(message),
             }
         )
         for l in message.outgoing(field)
         if l.target != FINAL
     ]
示例#14
0
    def create_composite_accessor_procedures(self, composite_fields: Sequence[Field]) -> UnitPart:
        def specification(field: Field) -> ProcedureSpecification:
            return ProcedureSpecification(f"Get_{field.name}", [Parameter(["Ctx"], "Context")])

        return UnitPart(
            [
                SubprogramDeclaration(
                    specification(f),
                    [
                        Precondition(
                            And(
                                VALID_CONTEXT,
                                Call("Has_Buffer", [Name("Ctx")]),
                                Call("Present", [Name("Ctx"), Name(f.affixed_name)]),
                            )
                        )
                    ],
                    [
                        FormalSubprogramDeclaration(
                            ProcedureSpecification(
                                f"Process_{f.name}", [Parameter([f.name], self.types.bytes)]
                            )
                        )
                    ],
                )
                for f in composite_fields
            ],
            [
                SubprogramBody(
                    specification(f),
                    [
                        ObjectDeclaration(
                            ["First"],
                            self.types.index,
                            Call(
                                self.types.byte_index,
                                [Selected(Indexed("Ctx.Cursors", Name(f.affixed_name)), "First")],
                            ),
                            True,
                        ),
                        ObjectDeclaration(
                            ["Last"],
                            self.types.index,
                            Call(
                                self.types.byte_index,
                                [Selected(Indexed("Ctx.Cursors", Name(f.affixed_name)), "Last")],
                            ),
                            True,
                        ),
                    ],
                    [
                        CallStatement(
                            f"Process_{f.name}",
                            [Slice("Ctx.Buffer.all", Name("First"), Name("Last"))],
                        )
                    ],
                )
                for f in composite_fields
            ],
        )
示例#15
0
 def setter_postconditions(
     self, message: Message, field: Field, field_type: Type
 ) -> Sequence[Expr]:
     return [
         *[
             Call("Invalid", [Name("Ctx"), Name(p.affixed_name)])
             for p in message.successors(field)
             if p != FINAL
         ],
         *self.common.valid_path_to_next_field_condition(message, field, field_type),
         *[
             Equal(e, Old(e))
             for e in [
                 Selected("Ctx", "Buffer_First"),
                 Selected("Ctx", "Buffer_Last"),
                 Selected("Ctx", "First"),
                 Call("Predecessor", [Name("Ctx"), Name(field.affixed_name)]),
                 Call("Valid_Next", [Name("Ctx"), Name(field.affixed_name)]),
             ]
             + [
                 Call(f"Get_{p.name}", [Name("Ctx")])
                 for p in message.definite_predecessors(field)
                 if isinstance(message.types[p], Scalar)
             ]
         ],
     ]
示例#16
0
 def field_byte_location_declarations(self) -> Sequence[Declaration]:
     return [
         ExpressionFunctionDeclaration(
             FunctionSpecification("Buffer_First", self.types.index),
             Call(self.types.byte_index, [Name("First")]),
         ),
         ExpressionFunctionDeclaration(
             FunctionSpecification("Buffer_Last", self.types.index),
             Call(self.types.byte_index, [Name("Last")]),
         ),
         ExpressionFunctionDeclaration(
             FunctionSpecification("Offset", self.types.offset),
             Call(
                 self.types.offset,
                 [Mod(Sub(Number(8), Mod(Name("Last"), Number(8))), Number(8))],
             ),
         ),
     ]
示例#17
0
    def create_incomplete_message_function(message: Message) -> UnitPart:
        specification = FunctionSpecification(
            "Incomplete_Message", "Boolean", [Parameter(["Ctx"], "Context")]
        )

        return UnitPart(
            [SubprogramDeclaration(specification, [Precondition(VALID_CONTEXT)])],
            [
                ExpressionFunctionDeclaration(
                    specification,
                    Or(
                        *[
                            Call("Incomplete", [Name("Ctx"), Name(f.affixed_name)])
                            for f in message.fields
                        ]
                    ),
                )
            ],
        )
示例#18
0
 def invalid_successors_invariant() -> Expr:
     return AndThen(
         *[
             If(
                 [
                     (
                         AndThen(
                             *[
                                 Call("Invalid", [Indexed("Cursors", Name(p.affixed_name))])
                                 for p in message.direct_predecessors(f)
                             ]
                         ),
                         Call("Invalid", [Indexed("Cursors", Name(f.affixed_name))]),
                     )
                 ]
             )
             for f in message.fields
             if f not in message.direct_successors(INITIAL)
         ]
     )
示例#19
0
    def create_present_function() -> UnitPart:
        specification = FunctionSpecification(
            "Present", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]
        )

        return UnitPart(
            [SubprogramDeclaration(specification, [Precondition(VALID_CONTEXT)])],
            [
                ExpressionFunctionDeclaration(
                    specification,
                    AndThen(
                        Call("Structural_Valid", [Indexed("Ctx.Cursors", Name("Fld"))]),
                        Less(
                            Selected(Indexed("Ctx.Cursors", Name("Fld")), "First"),
                            Add(Selected(Indexed("Ctx.Cursors", Name("Fld")), "Last"), Number(1)),
                        ),
                    ),
                )
            ],
        )
示例#20
0
def valid_message_condition(
    message: Message, field: Field = INITIAL, structural: bool = False
) -> Expr:
    if not message.outgoing(field):
        return TRUE
    return Or(
        *[
            l.condition
            if l.target == FINAL
            else AndThen(
                Call(
                    "Structural_Valid"
                    if structural and isinstance(message.types[l.target], Composite)
                    else "Valid",
                    [Name("Ctx"), Name(l.target.affixed_name)],
                ),
                l.condition,
                valid_message_condition(message, l.target, structural),
            )
            for l in message.outgoing(field)
        ]
    )
示例#21
0
 def valid_predecessors_invariant() -> Expr:
     return AndThen(
         *[
             If(
                 [
                     (
                         Call(
                             "Structural_Valid", [Indexed("Cursors", Name(f.affixed_name))]
                         ),
                         Or(
                             *[
                                 AndThen(
                                     Call(
                                         "Structural_Valid"
                                         if l.source in composite_fields
                                         else "Valid",
                                         [Indexed("Cursors", Name(l.source.affixed_name))],
                                     ),
                                     Equal(
                                         Selected(
                                             Indexed("Cursors", Name(f.affixed_name)),
                                             "Predecessor",
                                         ),
                                         Name(l.source.affixed_name),
                                     ),
                                     l.condition,
                                 ).simplified(self.substitution(message, False))
                                 for l in message.incoming(f)
                             ]
                         ),
                     )
                 ]
             )
             for f in message.fields
             if f not in message.direct_successors(INITIAL)
         ]
     )
示例#22
0
    def create_verify_message_procedure(
        message: Message, context_invariant: Sequence[Expr]
    ) -> UnitPart:
        specification = ProcedureSpecification(
            "Verify_Message", [InOutParameter(["Ctx"], "Context")]
        )

        return UnitPart(
            [
                SubprogramDeclaration(
                    specification,
                    [
                        Precondition(VALID_CONTEXT),
                        Postcondition(
                            And(
                                VALID_CONTEXT,
                                Equal(
                                    Call("Has_Buffer", [Name("Ctx")]),
                                    Old(Call("Has_Buffer", [Name("Ctx")])),
                                ),
                                *context_invariant,
                            )
                        ),
                    ],
                )
            ],
            [
                SubprogramBody(
                    specification,
                    [],
                    [
                        CallStatement("Verify", [Name("Ctx"), Name(f.affixed_name)])
                        for f in message.fields
                    ],
                )
            ],
        )
示例#23
0
    def substitution(self, message: Message, prefix: bool = True) -> Mapping[Name, Expr]:
        def prefixed(name: str) -> Expr:
            return Selected(Name("Ctx"), name) if prefix else Name(name)

        first = prefixed("First")
        last = prefixed("Last")
        cursors = prefixed("Cursors")

        return {
            **{First("Message"): first},
            **{Last("Message"): last},
            **{
                First(f.name): Selected(Indexed(cursors, Name(f.affixed_name)), "First")
                for f in message.fields
            },
            **{
                Last(f.name): Selected(Indexed(cursors, Name(f.affixed_name)), "Last")
                for f in message.fields
            },
            **{
                Length(f.name): Add(
                    Sub(
                        Selected(Indexed(cursors, Name(f.affixed_name)), "Last"),
                        Selected(Indexed(cursors, Name(f.affixed_name)), "First"),
                    ),
                    Number(1),
                )
                for f in message.fields
            },
            **{
                Variable(f.name): Call(
                    self.types.bit_length,
                    [Selected(Indexed(cursors, Name(f.affixed_name)), f"Value.{f.name}_Value")],
                )
                for f, t in message.types.items()
                if not isinstance(t, Enumeration)
            },
            **{
                Variable(f.name): Call(
                    self.types.bit_length,
                    [Selected(Indexed(cursors, Name(f.affixed_name)), f"Value.{f.name}_Value")],
                )
                for f, t in message.types.items()
                if isinstance(t, Enumeration)
            },
            **{
                Variable(l): Call(self.types.bit_length, [Call("Convert", [Name(l)])])
                for l in itertools.chain.from_iterable(
                    t.literals.keys() for t in message.types.values() if isinstance(t, Enumeration)
                )
            },
        }
示例#24
0
 def prefixed(name: str) -> Expr:
     return Selected(Name("Ctx"), name) if prefix else Name(name)
示例#25
0
from rflx.model import (
    FINAL,
    INITIAL,
    Enumeration,
    Field,
    Link,
    Message,
    ModularInteger,
    RangeInteger,
    Scalar,
    Type,
)

from .types import Types

NULL = Name("null")
VALID_CONTEXT = Call("Valid_Context", [Name("Ctx")])  # WORKAROUND: Componolit/Workarounds#1


class GeneratorCommon:
    def __init__(self, prefix: str = "") -> None:
        self.types = Types(prefix)

    def substitution(self, message: Message, prefix: bool = True) -> Mapping[Name, Expr]:
        def prefixed(name: str) -> Expr:
            return Selected(Name("Ctx"), name) if prefix else Name(name)

        first = prefixed("First")
        last = prefixed("Last")
        cursors = prefixed("Cursors")
示例#26
0
 def public_substitution(self, message: Message) -> Mapping[Name, Expr]:
     return {
         **{First("Message"): Selected(Name("Ctx"), "First")},
         **{Last("Message"): Selected(Name("Ctx"), "Last")},
         **{
             First(f.name): Call("Field_First", [Name("Ctx"), Name(f.affixed_name)])
             for f in message.fields
         },
         **{
             Last(f.name): Call("Field_Last", [Name("Ctx"), Name(f.affixed_name)])
             for f in message.fields
         },
         **{
             Length(f.name): Call("Field_Length", [Name("Ctx"), Name(f.affixed_name)])
             for f in message.fields
         },
         **{
             Variable(f.name): Call(
                 self.types.bit_length, [Call(f"Get_{f.name}", [Name("Ctx")])]
             )
             for f, t in message.types.items()
             if not isinstance(t, Enumeration)
         },
         **{
             Variable(f.name): Call(
                 self.types.bit_length, [Call("Convert", [Call(f"Get_{f.name}", [Name("Ctx")])])]
             )
             for f, t in message.types.items()
             if isinstance(t, Enumeration)
         },
         **{
             Variable(l): Call(self.types.bit_length, [Call("Convert", [Name(l)])])
             for l in itertools.chain.from_iterable(
                 t.literals.keys() for t in message.types.values() if isinstance(t, Enumeration)
             )
         },
     }
示例#27
0
 def initialize_field_statements(self, message: Message, field: Field) -> Sequence[Statement]:
     return [
         CallStatement("Reset_Dependent_Fields", [Name("Ctx"), Name(field.affixed_name)],),
         Assignment(
             "Ctx",
             Aggregate(
                 Selected("Ctx", "Buffer_First"),
                 Selected("Ctx", "Buffer_Last"),
                 Selected("Ctx", "First"),
                 Name("Last"),
                 Selected("Ctx", "Buffer"),
                 Selected("Ctx", "Cursors"),
             ),
         ),
         # WORKAROUND:
         # Limitation of GNAT Community 2019 / SPARK Pro 20.0
         # Provability of predicate is increased by adding part of
         # predicate as assert
         PragmaStatement(
             "Assert", [str(self.message_structure_invariant(message, prefix=True))],
         ),
         Assignment(
             Indexed(Selected("Ctx", "Cursors"), Name(field.affixed_name)),
             NamedAggregate(
                 ("State", Name("S_Structural_Valid")),
                 ("First", Name("First")),
                 ("Last", Name("Last")),
                 ("Value", NamedAggregate(("Fld", Name(field.affixed_name))),),
                 (
                     "Predecessor",
                     Selected(
                         Indexed(Selected("Ctx", "Cursors"), Name(field.affixed_name),),
                         "Predecessor",
                     ),
                 ),
             ),
         ),
         Assignment(
             Indexed(
                 Selected("Ctx", "Cursors"),
                 Call("Successor", [Name("Ctx"), Name(field.affixed_name)]),
             ),
             NamedAggregate(
                 ("State", Name("S_Invalid")), ("Predecessor", Name(field.affixed_name)),
             ),
         ),
     ]
示例#28
0
 def sufficient_space_for_field_condition(field_name: Name) -> Expr:
     return GreaterEqual(
         Call("Available_Space", [Name("Ctx"), field_name]),
         Call("Field_Length", [Name("Ctx"), field_name]),
     )
示例#29
0
    def message_structure_invariant(
        self, message: Message, link: Link = None, prefix: bool = True
    ) -> Expr:
        def prefixed(name: str) -> Expr:
            return Selected(Name("Ctx"), name) if prefix else Name(name)

        if not link:
            return self.message_structure_invariant(message, message.outgoing(INITIAL)[0], prefix)

        source = link.source
        target = link.target

        if target is FINAL:
            return TRUE

        field_type = message.types[target]
        condition = link.condition.simplified(self.substitution(message, prefix))
        length = (
            Size(base_type_name(field_type))
            if isinstance(field_type, Scalar)
            else link.length.simplified(self.substitution(message, prefix))
        )
        first = (
            Name(prefixed("First"))
            if source == INITIAL
            else link.first.simplified(
                {
                    **self.substitution(message, prefix),
                    **{
                        UNDEFINED: Add(
                            Selected(
                                Indexed(prefixed("Cursors"), Name(source.affixed_name)), "Last"
                            ),
                            Number(1),
                        )
                    },
                }
            )
        )

        return If(
            [
                (
                    AndThen(
                        Call(
                            "Structural_Valid",
                            [Indexed(prefixed("Cursors"), Name(target.affixed_name))],
                        ),
                        condition,
                    ),
                    AndThen(
                        Equal(
                            Add(
                                Sub(
                                    Selected(
                                        Indexed(prefixed("Cursors"), Name(target.affixed_name)),
                                        "Last",
                                    ),
                                    Selected(
                                        Indexed(prefixed("Cursors"), Name(target.affixed_name)),
                                        "First",
                                    ),
                                ),
                                Number(1),
                            ),
                            length,
                        ),
                        Equal(
                            Selected(
                                Indexed(prefixed("Cursors"), Name(target.affixed_name)),
                                "Predecessor",
                            ),
                            Name(source.affixed_name),
                        ),
                        Equal(
                            Selected(
                                Indexed(prefixed("Cursors"), Name(target.affixed_name)), "First"
                            ),
                            first,
                        ),
                        *[
                            self.message_structure_invariant(message, l, prefix)
                            for l in message.outgoing(target)
                        ],
                    ),
                )
            ]
        ).simplified()
示例#30
0
    def context_predicate(self, message: Message, composite_fields: Sequence[Field]) -> Expr:
        def valid_predecessors_invariant() -> Expr:
            return AndThen(
                *[
                    If(
                        [
                            (
                                Call(
                                    "Structural_Valid", [Indexed("Cursors", Name(f.affixed_name))]
                                ),
                                Or(
                                    *[
                                        AndThen(
                                            Call(
                                                "Structural_Valid"
                                                if l.source in composite_fields
                                                else "Valid",
                                                [Indexed("Cursors", Name(l.source.affixed_name))],
                                            ),
                                            Equal(
                                                Selected(
                                                    Indexed("Cursors", Name(f.affixed_name)),
                                                    "Predecessor",
                                                ),
                                                Name(l.source.affixed_name),
                                            ),
                                            l.condition,
                                        ).simplified(self.substitution(message, False))
                                        for l in message.incoming(f)
                                    ]
                                ),
                            )
                        ]
                    )
                    for f in message.fields
                    if f not in message.direct_successors(INITIAL)
                ]
            )

        def invalid_successors_invariant() -> Expr:
            return AndThen(
                *[
                    If(
                        [
                            (
                                AndThen(
                                    *[
                                        Call("Invalid", [Indexed("Cursors", Name(p.affixed_name))])
                                        for p in message.direct_predecessors(f)
                                    ]
                                ),
                                Call("Invalid", [Indexed("Cursors", Name(f.affixed_name))]),
                            )
                        ]
                    )
                    for f in message.fields
                    if f not in message.direct_successors(INITIAL)
                ]
            )

        return AndThen(
            If(
                [
                    (
                        NotEqual(Name(Name("Buffer")), NULL),
                        And(
                            Equal(First(Name("Buffer")), Name(Name("Buffer_First"))),
                            Equal(Last(Name("Buffer")), Name(Name("Buffer_Last"))),
                        ),
                    )
                ]
            ),
            GreaterEqual(
                Call(self.types.byte_index, [Name(Name("First"))]), Name(Name("Buffer_First"))
            ),
            LessEqual(Call(self.types.byte_index, [Name(Name("Last"))]), Name(Name("Buffer_Last"))),
            LessEqual(Name(Name("First")), Name(Name("Last"))),
            LessEqual(Name(Name("Last")), Div(Last(self.types.bit_index), Number(2))),
            ForAllIn(
                "F",
                ValueRange(First("Field"), Last("Field")),
                If(
                    [
                        (
                            Call("Structural_Valid", [Indexed(Name("Cursors"), Name("F"))]),
                            And(
                                GreaterEqual(
                                    Selected(Indexed(Name("Cursors"), Name("F")), "First"),
                                    Name(Name("First")),
                                ),
                                LessEqual(
                                    Selected(Indexed(Name("Cursors"), Name("F")), "Last"),
                                    Name(Name("Last")),
                                ),
                                LessEqual(
                                    Selected(Indexed(Name("Cursors"), Name("F")), "First"),
                                    Add(
                                        Selected(Indexed(Name("Cursors"), Name("F")), "Last"),
                                        Number(1),
                                    ),
                                ),
                                Equal(
                                    Selected(
                                        Selected(Indexed(Name("Cursors"), Name("F")), "Value"),
                                        "Fld",
                                    ),
                                    Name("F"),
                                ),
                            ),
                        )
                    ]
                ),
            ),
            valid_predecessors_invariant(),
            invalid_successors_invariant(),
            self.message_structure_invariant(message, prefix=False),
        )