Pratt parser#240
Conversation
ludofischer
left a comment
There was a problem hiding this comment.
Very interesting! How much is AI generated? I am asking because I had thought of trying to use AI to generate a "hand-written" parser, but never tried it.
ludofischer
left a comment
There was a problem hiding this comment.
It's good that you are comparing against the csstools output, as I suppose they checked they're fairly spec-compliant. Are you also testing against the current postcss-calc test suite? There's a lot of code here. when do you think it's going to stabilize so I can look at it more thoroughly?
|
|
||
| async function main(): Promise<void> { | ||
| // Dynamic imports so CJS/ESM interop works under tsx. | ||
| const v10Plugin = (await import('../src/index.js')).default as () => AcceptedPlugin; |
There was a problem hiding this comment.
I find the naming (version 10? version 3?) strange, I don't think we need dynamic import either, we can drop Node 20 compatibility if we release a major.
| "fast-check": "^4.7.0", | ||
| "jison-gho": "0.6.1-216", | ||
| "postcss": "^8.5.10", | ||
| "prettier": "^3.8.3", |
There was a problem hiding this comment.
I think you can update TypeScript to the latest version. cssnano is also using it. You could even try TypeScript go.
| "target": "es2022", | ||
| "lib": ["es2022"], | ||
| "module": "esnext", | ||
| "moduleResolution": "bundler", |
There was a problem hiding this comment.
Why are there two tsconfig files?
e362c9e to
16c1db0
Compare
…, rename benchmark, lint cleanups)
|
I am pretty much done with coding part. I will provide more context about this over weekends. Sorry for delay with response |
|
Quick note on the reasoning before the deeper review. Why replace jison. The generated parser worked, but the simplifier lived inside its action rules — parsing and reduction were tangled together. Ten years of edge-case patches piled on, and the output approximated the spec rather than following it. Hard to reason about, harder to extend. Why Pratt. A Pratt parser handles operator precedence with one short priority table instead of a long list of grammar rules (one per operator arrangement). For the calc grammar that collapses ~60 jison rules down to ~11 small handlers. The whole parser is ~250 lines you can read top-to-bottom. With parsing isolated, simplification and serialization become separate narrow modules instead of being mixed into parse actions. Net effect: each spec section maps to one file. If you find a bug, you know within a minute where to look. |
cfcc02e to
1e8afd8
Compare
|
Top-level architecture is a pipeline. Each stage is one file under Supporting files: The PostCSS adapter at |
|
Three opt-in flags bridge spec-compliant defaults with legacy jison behavior. Each lives in exactly one stage of the pipeline:
Defaults follow the spec. |
|
Testing runs across seven layers:
Plus mutation testing via Stryker (must kill ≥85% of injected mutations) and quality gates: lint, typecheck, depcruise for module boundaries, type-coverage ≥99.5%, knip for dead code. |
|
Five support scripts live under
|
8e07933 to
8a96a40
Compare
|
Performance. Two comparisons on the harvested corpus. Raw pipeline (tokenize → simplify → serialize, no PostCSS overhead) over 21,260 expressions: End-to-end PostCSS run (full plugin, 100 iterations × 21,416 declarations): |
|
Scaffolding.
|
|
Open-issues sweep. Fixed by the rewrite (parser errors or unsupported syntax that now work cleanly):
Newly supported as features:
Improved (closer to the reporter's intent, not byte-identical):
Won't fix / design call: |
|
All 190 fixtures pass. |
|
The ~39 changed-output cases cluster into seven categories, all spec-aligned:
|
|
Closed-issues sanity check. Ran 48 testable repros from the 82 closed issues through the new pipeline (skipped non-bug entries — release notes, README links, etc.):
Notable historical bugs now producing correct results:
No regressions, no unexpected throws. |
What
A decoupled prototype of new calc() internals under
playground/pratt/Why
v10 uses a JISON-generated LR parser and an ad-hoc reducer. It doesn't cover
min()/max()/clamp(), typed division (calc(100vw / 1px)), or calc-keyword folding (pi,e,infinity). CSS Values & Units 4 §10 gives a deterministic simplification algorithm — implementing it directly makes correctness reviewable section-by-section rather than invented-and-patched.How
Five TS modules, each mapped one-to-one to a spec section:
tokenizer.ts— §10.1 tokensparser.ts— §10.1 grammar (Pratt + parselet registry)type.ts— §10.2 calculation typessimplify.ts— §10.10 simplificationserialize.ts— §10.12 output169 tests (parser / tokenizer / simplify / typed / opaque / serialize / WPT crib), run via
pnpm test:pratt.tsxruntime, no build step.See
ROADMAP.mdfor more details