A parser behind an editor reparses a half-typed file on every keystroke — what should it guarantee callers?
answer
- the contract, not the cleverness
- always return a tree
- cover every byte of input
- error nodes, never dropped spans
- one front end, caller picks strictness
basics
~20 sTotality: a tree for any input, spanning every byte, with unparsable spans held by explicit error nodes and fabricated tokens marked, plus one flag saying whether any error node exists — so each caller picks its own strictness.
solid answer
~50 sThe decision is what contract the front end offers, not how clever its recovery is. A parser serving an editor should be **total**: for any input it returns a tree; that tree covers every byte, including the parts it could not understand; unparsable spans appear as explicit **error nodes** rather than being dropped; and fabricated tokens are marked. A single flag — does this tree contain an error node — then lets each caller choose its own policy. Highlighting, navigation and completion consume a damaged tree happily, because a wrong answer costs a keystroke. Anything that emits an artefact refuses outright, because a wrong answer ships. Keeping **one** front end with one contract, rather than a lenient parser for the editor and a strict one for the build, is what stops the two from telling the author different things about the same file.
go deeper
Know that a parser behind an editor must return something useful for a file that does not parse, because a file being typed almost never parses cleanly.
Explain the shape of that guarantee: a tree for any input, error nodes covering what failed, and marked fabricated tokens, so positions stay correct across the whole file.
Show how consumers split — highlighting and completion accept a damaged tree, artefact-producing phases must refuse — and how one flag plus node spans enforces that split.
Own the contract itself: one front end with caller-chosen strictness rather than two grammars, a bounded repair policy written down, and an explicit statement of what a partial tree does not promise.
## Why this is a contract question, not a recovery question Recovery strategies are mechanisms; the thing a lead actually owns is the promise the front end makes to everyone downstream. A file being typed is ungrammatical most of the time, so "parse failed" is not an exceptional path — it is the normal one. Once that is accepted, the design question becomes: what can a caller rely on when the input is broken, and who decides how much brokenness is too much? ## The guarantees worth committing to 1. **Totality.** Every input produces a tree. No failure is signalled by returning nothing or by unwinding out of the parse, because callers on the interactive path have nothing useful to do with either. 2. **Full coverage.** The tree spans the whole file. Every byte belongs to some node, damaged or not, so a position lookup after the damage still lands correctly. 3. **Explicit error nodes.** Text that could not be parsed is held by a node that says so, with its real span. Dropping the text instead destroys coverage and quietly shifts everything after it. 4. **Marked fabrication.** Tokens the parser invented are flagged and zero-width, so no consumer mistakes them for something the author wrote. 5. **One summary flag.** The tree says whether it contains any error node, so a caller can decide in one test rather than walking the tree. ## Consumers differ in how much damage they tolerate | Consumer | Tolerates a damaged tree? | Why | |---|---|---| | Highlighting and folding | Yes | A wrong colour for one keystroke costs nothing and the file is mostly intact | | Completion at the cursor | Yes, and needs it | The cursor is usually *inside* the damaged region; refusing there is refusing when it matters most | | Navigation and outline | Mostly | Missing one member is better than an empty outline | | Automated edits and formatting | Only outside the damaged span | Rewriting text the parser guessed at can destroy the author's work | | Anything producing an artefact | No | A wrong answer is committed rather than discarded, so it must refuse while any error node exists | That table is the whole argument for pushing the decision to the caller: the same tree is exactly right for one consumer and unacceptable for another, and only the caller knows which it is. ## One front end or two The tempting alternative is a lenient parser for the editor and a strict one for the build. It fails in a specific, expensive way: two rule sets drift. Every language change must land twice; when they disagree, the editor accepts what the build rejects, or flags what the build is happy with, and the author is asked to believe two tools about one file. Debugging that is miserable because neither tool is wrong on its own terms. One parser, one contract, and strictness chosen by the caller makes the two answers identical by construction, at the cost of pushing the policy decision one layer up. ## How aggressive should repair be - **Repair that is too timid** turns a small typo into a large hole, so completion inside it has no context at all and the editor feels dead exactly where the user is working. - **Repair that is too confident** invents structure. One fabricated closer can make the remainder of a file parse as a plausible but wrong shape, so suggestions describe a construct the author is not in, and messages point at spans nobody wrote. - **The middle** is bounded repair plus honesty: try the cheap local edits, mark what you invented, and where you cannot repair, record an error node rather than a guess. An error node is information; a confident wrong tree is misinformation. - **Decide where the boundary sits once**, and write it down, because it is otherwise re-litigated in every bug report about a bad suggestion. ## What to measure before committing The useful measurements are not about grammars. Capture real half-typed files from the situations that matter — mid-identifier, immediately after opening a block, in the middle of an argument list — and ask two questions of each: how much of the file still has structure, and does the cursor's own enclosing construct survive? A recovery policy that scores well on whole-file coverage but loses the construct under the cursor is optimising the wrong thing for an editor, and it is a trade that only shows up when you measure at the cursor rather than over the file. ## The honest statement of what a partial tree is not A partial tree does not mean the file is valid, does not mean later phases may proceed, and does not mean the shape shown is the shape the author intends. It means the parser has published everything it could establish plus a truthful account of what it could not — which is the most a front end can offer about a file that is still being written.
- What single property of the tree lets every later phase decide whether to trust it?A flag saying the tree contains at least one error node, backed by per-node spans. Phases that emit artefacts test the flag and refuse. Phases that merely answer questions about structure run anyway, and use the spans to suppress any message falling inside a damaged range.
- Why not keep a lenient grammar for the editor and a strict one for the build?Because two rule sets drift. Every language change must land twice, and when they disagree the editor and the build tell the author different things about one file — a class of bug that is painful to diagnose because neither tool is wrong by itself. One parser with caller-chosen strictness keeps the two answers identical by construction.
- What is the cost of a parser that repairs too confidently?It invents structure. A fabricated closer can make the rest of a file parse as a plausible but wrong shape, so suggestions describe a construct the author is not in and messages point at text nobody wrote. Bounded repair plus an explicit error node is more useful than a confident guess, because an error node is information.
- How would you measure whether a recovery policy is good enough for an editor?On captured half-typed files from realistic moments — mid-identifier, just after opening a block, inside an argument list — measure both how much of the file retains structure and whether the construct enclosing the cursor survives. The second matters more: whole-file coverage can look excellent while the one region the user is working in is a hole.
saying these in an interview costs you the question
- Returns nothing, or unwinds out of the parse, when the file does not parse
- Drops unparsable text instead of covering it with an error node
- Keeps a separate lenient grammar just for the editor
- Lets a phase emit an artefact from a tree containing error nodes
- Repairs as confidently as possible, inventing structure nobody wrote
- Treats fabricated tokens as ordinary source text downstream