3.3. Printer automation
An explicit serializer has one central obligation, encode_view: its output decodes to
a valid typed view whose converted denotation is the original domain value.
The automation does not prove arbitrary serializer behavior. Each route starts from evidence that matches how the serializer was developed, then removes the repeated work of constructing the existential view witness.
3.3.1. Existing external parser: triptych_encode
Use triptych_encode when an existing serializer already roundtrips through an external
parser and that parser has an agreement theorem. Decimal supplies exactly those facts:
theorem Decimal.encode_view (i : Int64) :
∃ v : Decimal.View,
Decimal.decodeView (decimalToStr i) = some v ∧
Decimal.View.Valid v ∧
Int64.ofInt (Decimal.View.denotation v) = i := i:Int64⊢ ∃ v, decodeView (decimalToStr i) = some v ∧ v.Valid ∧ Int64.ofInt v.denotation = i
All goals completed! 🐙
The tactic combines Cedar's serializer roundtrip, Decimal parser agreement, the generated view
normal forms, and the Int64 conversion inverse. From this one witness, Triptych derives
Decimal.parse_toString_roundtrip, serializer injectivity, and normalization results.
3.3.2. Direct generated-spec facts: triptych_encode_direct
Use triptych_encode_direct when there is no external parser to reconcile. Its inputs say
directly that the serializer output is accepted by the generated specification and computes the
intended value:
private theorem triptychEncodeDirect_identity (n : Nat) :
∃ view,
encodeDecodeView (encodePrinter n) = some view ∧
encodeValid view ∧
view.denotation = n := n:Nat⊢ ∃ view, encodeDecodeView (encodePrinter n) = some view ∧ encodeValid view ∧ view.denotation = n
All goals completed! 🐙
This regression test has a serializer and generated-spec-style parser but no parser-agreement
theorem. The tactic turns the direct acceptance and value facts into the same encode_view
obligation expected by downstream roundtrip theorems.
3.3.3. Structural printer: triptych_encode_derivation
Use triptych_encode_derivation when the serializer naturally constructs a grammar
derivation. The proof supplies a valid root tree for each domain value; the generated
decodeView_render theorem projects that tree to the flat view:
private theorem triptychEncodeDerivation_identity (n : Nat) :
∃ view,
encodeDecodeView (encodePrinter n) = some view ∧
encodeValid view ∧
view.denotation = n := n:Nat⊢ ∃ view, encodeDecodeView (encodePrinter n) = some view ∧ encodeValid view ∧ view.denotation = n
All goals completed! 🐙This route avoids separately proving string-level acceptance and value equations, because those facts follow from the valid derivation and its rendering theorem.
3.3.4. Fully synthesized printer: printer auto
printer auto goes one step further: for supported value expressions, Triptych synthesizes
the serializer and its Triptych.DerivationPrinter certificate together. The signed-integer
syntax test checks both the emitted spelling and the generated roundtrip theorem:
triptych SignedInteger where
grammar
Root ::= Sign Digits
Sign ::= sign
Digits ::= digit+
value
Sign * nat Digits
printer auto
#guard SignedInteger.toString 0 = "0"
#guard SignedInteger.toString 42 = "42"
#guard SignedInteger.toString (-42) = "-42"
#guard SignedInteger.parse (SignedInteger.toString (-42)) = some (-42)
Its current proved rule covers total signed decimal integers with no constraints or domain
conversion. Unsupported constrained, ofSpec, and opaque value' cases fail with
source-located diagnostics instead of emitting an unjustified theorem.