Verified Cedar Extension Parsers in Lean 4

4.2. Formal Specification🔗

We formalize the validity of an input string by the predicate IsWfIPNet (well-formed syntax of the grammar) and the value functions v4Value/v6Value (the IPNet a well-formed string denotes). Unlike the decimal grammar, whose value is a single Int, an IP-net's value is an IPNet, so soundness and completeness are phrased per witnessing components rather than through a single computeValue.

The building block for numeric groups is IsCanonicalNat, which captures a non-empty digit string with the grammar's "no leading zeros" rule (str.startsWith "0" → str = "0"), building on the shared IsDigits predicate:

public def IsCanonicalNat (s : String) : Prop := IsDigits s (s.startsWith "0" s = "0")

An IPv4 address is four decimal groups; syntaxWf pins each group to a canonical Digit{1,3} string and constraintsWf bounds each value by 255:

public def V4Components.syntaxWf (v : V4Components) : Prop := (IsCanonicalNat v.g₀ v.g₀.length 3) (IsCanonicalNat v.g₁ v.g₁.length 3) (IsCanonicalNat v.g₂ v.g₂.length 3) (IsCanonicalNat v.g₃ v.g₃.length 3)public def V4Components.constraintsWf (v : V4Components) : Prop := numValue v.g₀ 255 numValue v.g₁ 255 numValue v.g₂ 255 numValue v.g₃ 255

An IPv6 address is modelled on the grammar's :: structure — either a full list of eight groups, or a gap form whose two sides straddle the :: and whose total is strictly fewer than eight groups (the gap expanding to the missing zero groups). This mirrors how the parser (next section) splits on "::":

public inductive V6Components where | full (gs : List String) | gap (l r : List String)public def V6Components.syntaxWf : V6Components Prop | .full gs => gs.length = 8 s gs, IsHexGroup s | .gap l r => l.length + r.length < 8 ( s l, IsHexGroup s) ( s r, IsHexGroup s)

The optional CIDR prefix is a canonical decimal number bounded by the address width, with absence denoting the full-width prefix:

public def IsWfOptionalPrefix (digits size : Nat) : Option String Prop | none => True | some p => IsCanonicalNat p p.length digits numValue p size

Well-formedness of the whole string then reads off the grammar — a well-formed V4 rendering or a well-formed V6 rendering, each phrased existentially over the components' asString (which bakes in the separators, group count, and ::-placement):

public def IsWfV4 (str : String) : Prop := (v : V4Components) (pre : Option String), v.syntaxWf v.constraintsWf IsWfOptionalPrefix 2 (ADDR_SIZE V4_WIDTH) pre str = v.asString ++ (match pre with | none => "" | some p => "/" ++ p)public def IsWfIPNet (str : String) : Prop := IsWfV4 str IsWfV6 str