Skip to main content

Module template

Module template 

Source
Expand description

Where a backtick template ends — one answer, for both scanners (D10).

Two hand-written scanners have to agree about the extent of a `…` run: praxis-parser’s lexer, which turns it into one BacktickTemplate token, and praxis-input-parser’s template scanner, which re-reads that token’s interior and has to find the same nested templates and the same closing backtick inside it. Two implementations of one rule drift, so there is one implementation.

praxis-syntax is the crate below both (it is where crate::ident and crate::numeric already live for exactly this reason), so the rule lives here and is called twice rather than written twice.

§The rule

Scanning starts just past the opening backtick and, until the run closes:

  • \ hides the next scalar, so an escaped backtick cannot terminate a run.
  • { opens a capture and } closes one — this is the only thing brace depth is for.
  • " inside a capture opens a string literal, which is skipped whole: one_of("{"), sep("}", int) and one_of("“)all hold delimiters that are text, not structure. Outside a capture a quote is ordinary literal text (``He said “hi” {x:int}` ``), which is why the rule is conditioned on depth rather than applied everywhere.
  • ` closes the run at capture depth 0. Inside a capture it opens a template of its own, because a capture body is a full parser expression (D10) and `{g:choice(A: `{x:int}`)}` is one template containing another.

At most MAX_TEMPLATE_NESTING templates may nest; past that a backtick simply closes, so adversarial input lands on the ordinary unterminated/unexpected-token paths rather than on the stack. There is one bound because there is one function.

§The quoted-run and scalar primitives

A "…" inside a capture and a '…' inside an interpolation hole are the same scan with a different closing byte, and both scanners have to step over a multi-byte scalar the same way. [quoted_run] and [skip_scalar] therefore live in this module — the lower of the two — and crate::interp calls them instead of keeping a second copy, for the same reason this module exists at all.

Enums§

TemplateEnd
Where a `…` run ends.

Functions§

string_end
Find the end of the "…" literal whose opening quote is at open, returning the index just past the closing quote, or None if the text ends first. \ hides the next scalar, so "\"" is one literal.
template_end
Find the end of the backtick template whose opening backtick is at open.