Skip to main content

Module interp

Module interp 

Source
Expand description

Where a "…" literal ends and where its holes are — one answer, for both readers (§8.1, ADR-147).

A { inside a text literal opens an interpolation hole holding a full expression (ADR-147 decision 1), so "Part 2: {part2}" is not one token: the lexer splits it into fragment tokens with the hole’s ordinary tokens between them, which is what gives a name inside a hole a real range in the lossless tree and therefore makes it a closure capture.

Two readers have to agree about the extent of such a run: text_end, which the lexer asks before it emits anything, and fragment_end, which the lexer asks again each time a hole closes and it has to resume scanning text. They are the same rule read from two starting points, so they live in one module and call one another rather than each owning a copy of it — see template’s doc.

§The rule

A text literal ends at the line it opens on; crate::template gives backtick templates the same bound. Within a literal:

  • \ hides the next scalar, so "\"" is one literal and "\{" is a literal brace (crate::literal::decode_escape owns which escapes mean what; this module only needs to know that a backslash consumes one scalar).
  • { opens a hole. A } in ordinary text closes nothing and is literal — the asymmetry is deliberate, because outside a hole there is nothing for a } to be ambiguous with.
  • Inside a hole the scanner reads expression source: braces nest, and a "…", a '…', a `…` and a /* … */ are each skipped whole, because each of them can hold a } that is not structure. A nested "…" is skipped by re-entering text_end, so "{f("{y}")}" is one literal containing another.
  • A // inside a hole means the rest of the line is a comment, so the literal cannot close on its line: that is an unterminated literal, and it is reported as one.

§Why an unterminated run is the whole answer

TextEnd::Unterminated is not a detail of error reporting. The lexer only enters interpolation mode — the brace-depth stack that decides whether a } closes a hole or a block — for a literal this module has already proved closes. So there is no path on which a newline or an EOF reaches that stack, and an unterminated literal is one TextLit token plus T004 (ADR-147 decision 5).

That is also why nesting past MAX_INTERPOLATION_NESTING answers Unterminated rather than “stop treating quotes as structure”. Refusing to enter is a bound both readers observe by construction; a bound that changed what a quote means would put the lexer’s resume path and this scanner on different rules at exactly the depth nobody tests.

Enums§

FragmentEnd
What ends a run of literal text inside a "…".
TextEnd
Where a "…" literal ends, and whether it has any holes.

Functions§

fragment_end
Find where the run of literal text starting just past at ends.
text_end
Find the end of the "…" literal whose opening quote is at open, and the first hole in it.