Skip to content

sir: text format for Vars - #21

Open
Eduardogbg wants to merge 36 commits into
sir/loweringfrom
sir/text-format
Open

sir: text format for Vars#21
Eduardogbg wants to merge 36 commits into
sir/loweringfrom
sir/text-format

Conversation

@Eduardogbg

@Eduardogbg Eduardogbg commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

this PR adds the SIR text format for Vars: lexer, parser, printer, an extractor from .sir to a Lean module, and a well-formedness checker with a soundness theorem.

  • the parser covers every Vars statement and terminator (text malloc is Vars.Stmt.malloc, mallocany is mallocUninit). numeric literals in operand position become a fresh const; init is required, main optional; a function's first block is its entry, as in the rust parser.
  • for the parser correctness we state and prove:
    • normalize is a normal form for alpha equivalence (alphaEquiv_iff_normalize_eq, alphaEquiv_equivalence).
    • parse ∘ print = normalize for well-formed programs (parse_print_normalize) and parse ∘ print = id on normal forms (parse_print, unconditional since parser output is always normal, parse_normal): print is a section of parse, the normal forms are a retract of the text.
  • every WellFormed clause has a proof-free decider in Sir/Vars/Spec/Check.lean (checkIcallArity, checkIretArity, checkAcyclicCalls, checkEntryArity, checkValidJumpTargets, checkVariablesDefinedBeforeUse), failing with a Diagnostic; checkAcyclicCalls computes a rank on the call graph and checks it decreases along every call. checkWellFormed sequences them and wellFormed_of_check : checkWellFormed p = .ok () → p.WellFormed is the exported soundness theorem; the extractor runs the checker, the example programs get their WellFormed witnesses from it by rfl and an ill-formed one is rejected with its diagnostic (Sir/Examples/Text.lean).
  • lake env lean --run SirExtract.lean input.sir Output.lean name writes the parsed program as Lean source. examples have their printed text pinned and reparsed (Sir/Examples/Text.lean).

@Eduardogbg
Eduardogbg marked this pull request as ready for review August 13, 2026 04:55
@Eduardogbg
Eduardogbg force-pushed the sir/text-format branch 3 times, most recently from 965b494 to eb508c6 Compare August 15, 2026 19:25
@Eduardogbg
Eduardogbg changed the base branch from main to sir/machine-languages August 15, 2026 19:25
@Eduardogbg
Eduardogbg force-pushed the sir/text-format branch 3 times, most recently from d674566 to 8fca58e Compare August 18, 2026 06:48
@Eduardogbg Eduardogbg changed the title sir: text format parser, printer, and program extraction sir: text format for Vars Aug 18, 2026
Comment thread sir/Sir/Text/Spec/Parser.lean Outdated
let (preludes, identifiers) ← operands rest
return (prelude ++ preludes, #[identifier] ++ identifiers)

def parseMnemonic (functions : List String) (line : Line) (mnemonic : String)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd make the parsing of mnemonics more modular here, every mnemonic is quite repetitive and will be annoying to review once we start adding all the ops

Kernel reduction of `parse` was never the obstacle. The elaborator's smart
unfolding copies the unreduced lexer term that `internVariable` stores in the
interning state, once per unfolding attempt, and the copies miss the whnf cache.
Plain delta reduction does not, so `parse_rfl` disables smart unfolding at the
proof site: the witness goes from unelaborable to 1.4s of elaboration and 0.65s
in the kernel, on the ordinary `rfl` trust story.
`sir-extract` reads a `.sir` file and writes a comment-free Lean module defining the program it denotes. Declaration names are restricted to plain identifiers before emission, and read, parse, and write failures use path-qualified errors.

Generated modules are ordinary committed source: reviewable, diffable, and tracked by Lake, unlike a program conjured at elaboration time.
`Ensures P` is `Except Diagnostic (PLift P)`: a real monad, so checks compose in `do`, while the clause each one discharges rides in its type and cannot be forged. `ensureAll` matches the shape `WellFormed` is already written in, so a check reads like the clause it proves. Acyclicity of the call graph is not decidable as stated, so it takes a rank certificate instead.
decimalDigits was the one definition in this layer written by well-founded recursion, so it never reduced in the kernel and nothing mentioning print could be settled by rfl. It becomes structural on a fuel argument, which reduces: seven-digit values take milliseconds.

That makes five closed example witnesses provable. Each shows parse (print p) = .ok p for one program and pins the printed text, which is also the only place the surface syntax is written down against a program that means something.

Each witness reduces the printing and parsing halves separately. Composing them puts parse to work on an unreduced print term, and those copies miss the whnf cache in the same way smart unfolding does.
A numeric literal in an operand position expands to a fresh variable defined by a preceding const, but the result name was interned first, so the temporary got the higher id while being printed first. Reprinting and reparsing such a program returned it renamed rather than unchanged.

Operand literals are now lifted before results are interned, matching printer order. The generated temporaries therefore receive the lower identifiers they occupy in emitted text, so reparsing no longer swaps their identifiers with statement results.
@Eduardogbg
Eduardogbg changed the base branch from sir/machine-languages to sir/lowering August 19, 2026 21:54
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants