skip to content

Why do scanners often match an identifier first and then consult a reserved-word table rather than writing one rule per keyword?

level: middleimportance: should knowfreq 38%

answer

  1. a keyword is a reserved identifier
  2. both designs work
  3. one pattern versus many
  4. table edit, not pattern edit
  5. soft keywords decided by the parser

basics

~20 s

Because every keyword also matches the identifier pattern. Matching the identifier once and looking the text up in a table keeps the recogniser small, makes the keyword set a data edit, and leaves room for words that are keywords only in some positions.

solid answer

~50 s

A keyword is just an identifier the language reserves, so both designs work and both need the same tie-break discipline. Writing one rule per keyword is correct as long as longest match applies - otherwise `iffy` becomes `if` plus `fy` - and as long as the keyword rules are declared before the identifier rule so they win the equal-length tie. What it costs is size: every keyword adds states to the combined recogniser, and the set becomes part of the pattern specification. The table approach matches the identifier once, then does a single lookup on the matched text: the machine stays one identifier-shaped pattern, adding a keyword is a data change, and a **soft keyword** - reserved only in one position - can simply be emitted as an identifier and matched by the parser on its text.

go deeper

for a junior

Know that a keyword looks exactly like an identifier to the scanner and that something extra decides between them - a lookup in a reserved-word list is the common answer.

for a middle

Contrast the two designs and say what each needs: longest match plus declaration order for one rule per keyword, nothing extra for identifier-plus-table. Explain the iffy hazard.

for a senior

Explain why deferring the decision matters in a language that keeps adding syntax, and how soft keywords are emitted as identifiers and matched by the parser on their text.

for a principal

Treat the reserved set as a compatibility surface: every word moved into it can break existing sources, which is the argument for contextual keywords and for keeping the decision late.

## Keywords are a subset of the identifier pattern In almost every language, a keyword is spelled exactly like an identifier: letters, maybe digits and an underscore. So the character run `if` is accepted by the keyword pattern and by the identifier pattern at the same time and at the same length. The scanner must decide which token class to emit, and there are two standard designs. ## Design one: a rule per keyword Each reserved word gets its own pattern, declared before the general identifier rule. This is correct, and it relies on two rules working together: 1. **Longest match** stops the keyword rule from firing on a prefix. Without it, `iffy` would be tokenized as the keyword `if` followed by the identifier `fy`, because the keyword pattern does accept a prefix of the input. 2. **Declaration order** settles the genuine tie. On the exact text `if`, both rules accept the same two characters, so the earlier-declared keyword rule wins. Get either wrong and the bug is subtle: reorder the rules and every keyword silently becomes an identifier; drop longest match and every identifier that starts with a keyword breaks. ## Design two: identifier, then a lookup The scanner has one pattern for the identifier shape. When it matches, it takes the matched text and looks it up in a table of reserved words; a hit means the token class is that keyword, a miss means it is an identifier. | Aspect | Rule per keyword | Identifier plus table | |---|---|---| | Recogniser size | grows with every keyword | one identifier pattern | | Adding a keyword | edits the pattern specification | edits a data table | | Prefix hazard (`iffy`) | handled by longest match | cannot arise - the identifier already took the whole run | | Tie-break needed | yes, declaration order | none | | Soft keywords | awkward - the class is fixed too early | easy - emit identifier, decide later | The prefix row is the quietly important one. With the table design the hazard **disappears by construction**: the identifier pattern consumes the entire run `iffy` before any lookup happens, so there is nothing for a keyword to steal. ## Why the lookup is cheap - It happens once per identifier-shaped token, not per character. - The table is small, fixed at build time, and often a perfect hash or a length-bucketed comparison, so a hit or miss costs work proportional to the word's length. - The keyword set becomes introspectable data: tooling can list it, a highlighter can consume it, a diagnostic can suggest a near-miss spelling. Compare that with the rule-per-keyword design, where the same information is baked into transition tables and cannot be recovered without decompiling them. ## Contextual and soft keywords Some languages reserve a word only in one syntactic position - it names a construct there and is a perfectly legal variable name everywhere else. A scanner cannot know which position it is in, because positions are a parser notion. The table design handles this gracefully: - keep such words **out** of the reserved table; - emit them as ordinary identifiers with their lexeme intact; - let the parser match on the lexeme text in the one place the word is special. With a rule per keyword the class is fixed at scan time, so the parser receives a keyword token in positions where a variable name was intended, and the grammar has to accept that keyword token as a name everywhere - a rule that must then be repeated for each soft keyword. ## What to say in an interview The honest framing is not *one design is wrong*. It is: both work, the table design keeps the recogniser independent of the size of the keyword set, removes the need for a tie-break at all, and leaves the keyword-or-not decision late enough that a parser can still change its mind. That last property is what makes it the default in languages that keep adding syntax without wanting to break existing code that used the new word as a name.

  • With one rule per keyword, what stops `iffy` from being scanned as `if` then `fy`?
    Longest match. The identifier rule accepts all four characters while the keyword rule accepts only two, so the longer match wins and no tie-break is consulted. With the identifier-plus-table design the hazard cannot arise at all: the identifier pattern has already consumed `iffy` before any lookup runs, and `iffy` is not in the table.
  • How does either design handle a word that is reserved only in one position?
    By keeping it out of the reserved set and letting the parser match on the lexeme text where the word is special. The table design supports this directly, since the class is decided by a lookup that can simply omit the word. With a rule per keyword the class is fixed during scanning, so the grammar must additionally accept that keyword token wherever a plain name is legal.

saying these in an interview costs you the question

  • Claims one rule per keyword cannot work at all
  • Thinks keywords are lexically distinct from identifiers in shape
  • Forgets that the keyword rule must precede the identifier rule
  • Says the lookup costs work proportional to the table size
  • Believes the scanner can tell whether a soft keyword is reserved here