Skip to content

Add DDM grammar productivity checker with correctness proofs#1156

Open
julesmt wants to merge 1 commit into
mainfrom
ddm-productivity-check
Open

Add DDM grammar productivity checker with correctness proofs#1156
julesmt wants to merge 1 commit into
mainfrom
ddm-productivity-check

Conversation

@julesmt
Copy link
Copy Markdown
Member

@julesmt julesmt commented May 12, 2026

Decides whether each declared category in a dialect admits a finite parse tree. Productivity is rooted in frameworkAtomicCategories — the categories the parser constructs directly — with all other categories derived from operator chains.

  • Strata/DDM/Analysis/Productivity.lean — algorithm + wrapper
  • Strata/DDM/Analysis/Productivity/Spec.lean — sound, complete, and correctness proofs for the fixpoint kernel and the top-level wrapper
  • StrataTest/DDM/Analysis/ProductivityTest.lean — unit tests
  • Scripts/ProductivityCheck.lean — lake exe productivityCheck

One sorry remains: transitiveImports.fuel_suffices — proving the fuel bound is enough for the dialect-import-graph traversal.

Issue #, if available:

Description of changes:

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Decides whether each declared category in a dialect admits a finite
parse tree.  Productivity is rooted in `frameworkAtomicCategories` —
the categories the parser constructs directly — with all other
categories derived from operator chains.

* Strata/DDM/Analysis/Productivity.lean — algorithm + wrapper
* Strata/DDM/Analysis/Productivity/Spec.lean — sound, complete, and
  correctness proofs for the fixpoint kernel and the top-level wrapper
* StrataTest/DDM/Analysis/ProductivityTest.lean — unit tests
* Scripts/ProductivityCheck.lean — `lake exe productivityCheck`

One sorry remains: `transitiveImports.fuel_suffices` — proving the
`n³` fuel bound is enough for the dialect-import-graph traversal.
@julesmt julesmt requested a review from a team May 12, 2026 00:15
@github-actions github-actions Bot added Waiting-For-Review dependencies Pull requests that update a dependency file labels May 12, 2026
, Strata.B3AST
, Strata.B3CST
, Strata.Laurel.Laurel
]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This seems like a candiate to move somewhere else and share it. strata print has a form of this. Can we reuse?

/-! ## HashSet membership -/

def Mem (s : Std.HashSet QualifiedIdent) (c : QualifiedIdent) : Prop :=
s.contains c = true
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why introduce the special Prop? You could just use c \in s.

| byOp : ∀ (op : OpInfo),
op ∈ ops →
(∀ a ∈ op.args, Productive ops base a) →
Productive ops base op.result
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A docstring would really help here. What is the relationship between OpInfo and the dialects?

Copy link
Copy Markdown
Contributor

@joehendrix joehendrix left a comment

Choose a reason for hiding this comment

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

Hi, this is an well written contribution — the proof work is clean and well-tested.

However, I'd recommend substantial changes for the goal of avoiding stack overflows. I may have used the wrong term when mentioning a productivity checker. The property we need is guaranteed progress — every cycle in the parser's call graph must consume input before recursing. Productivity (every category has a finite derivation) is a weaker property that doesn't prevent parser loops. For example, A → B ...; B → A ... will spin the Pratt parser without consuming input even though both categories are productive.

Checking progress requires knowing the operator syntax layout (which tokens come before recursive calls), which the OpInfo extraction discards. So this isn't something that can be adapted without a fundamental rework of the approach.

We could potentially adapt this as a DDM linter for unproductive non-terminals. They are potentially useful for dialects intended to be imported (like Init), but effectively dead code in terminal dialects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file Waiting-For-Review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants