skip to content

In a grammar where a conditional arm may be a single statement, which conditional does a trailing else attach to?

level: middleimportance: nice to knowfreq 28%

answer

  1. one text, two parse trees
  2. the else has a choice of owner
  3. nearest conditional still missing one
  4. indentation is not the arbiter
  5. terminator or matched-statement grammar

basics

~10 s

By the conventional resolution of the dangling-else ambiguity, a trailing else binds to the nearest preceding conditional that does not already have one — the inner one. The grammar decides this, not the indentation.

solid answer

~50 s

When a conditional's arm may itself be a bare conditional with no closing delimiter, the text `if A then if B then X else Y` has two legal parse trees: the else can belong to the outer conditional or to the inner one. That is the **dangling-else ambiguity**, and the near-universal resolution is to bind the else to the **nearest preceding conditional still missing one**, which is the inner one. The consequence for a reader is blunt: indentation is a lie unless the grammar is layout-sensitive, so a Y written flush with the outer `if` still runs when A holds and B does not. Two grammar designs remove the ambiguity rather than resolving it — giving the conditional a closing terminator or mandatory bracketed arms so each one is self-delimiting, or splitting the grammar into matched and unmatched statement forms so only one parse exists.

code

pseudocode · 10 lines
pseudocode
if tokenIsValid then
    if roleIsAdmin then
        grant()
else
    denyAndLog()

# the layout suggests the else belongs to the outer conditional,
# but the nearest-unmatched rule binds it to the inner one:
# tokenIsValid true, roleIsAdmin false -> denyAndLog() runs
# tokenIsValid false                   -> nothing runs at all

go deeper

for a junior

Recall the rule: a trailing else belongs to the nearest conditional above it that has no else yet, whatever the indentation shows.

for a middle

Explain why the text admits two parse trees, and name the two grammar designs that remove the ambiguity instead of resolving it by convention.

for a senior

Point out the review habit this justifies — bracket every arm, never nest an else-less conditional inside one that has an else — and the input combination that exposes a wrong binding.

for a principal

Take the general point to notation choices your teams make: where a grammar lets layout and structure disagree, the disagreement will eventually ship.

## The ambiguity Take a grammar in which a conditional has an optional else arm and each arm may be **any single statement**, including another conditional, with nothing marking where the conditional ends. Now parse: ``` if A then if B then X else Y ``` Two derivations fit the same text: 1. The else belongs to the **inner** conditional: if A holds, choose between X and Y on B; if A fails, do nothing. 2. The else belongs to the **outer** conditional: if A holds, do X when B holds and nothing otherwise; if A fails, do Y. These are different programs. On the input where A holds and B does not, parse 1 runs Y and parse 2 runs nothing. On the input where A fails, parse 1 runs nothing and parse 2 runs Y. A grammar that admits both derivations for one string is **ambiguous**, and this particular one is named the **dangling-else ambiguity**. ## The conventional resolution Every practical design resolves it the same way: **the else binds to the nearest preceding conditional that does not already have an else** — parse 1, the inner conditional. The rule is applied either as a stated disambiguation attached to an otherwise-ambiguous grammar, or baked into the grammar itself (below). The consequence for a reader is the part worth remembering: - **Indentation does not bind anything** unless the grammar is layout-sensitive. Aligning Y with the outer `if` changes nothing about which conditional owns it. - A conditional **without** an else arm, used as the then-arm of another conditional, is the shape that creates the trap. Any else appearing after it is captured by it. - The bug it produces is a **wrong branch taken silently**: the program is well-formed, compiles, and does the wrong thing on one of the four input combinations. ## Two grammar cures Resolving an ambiguity by convention and removing it from the grammar are different things. | Approach | What changes | Effect | |---|---|---| | Nearest-unmatched-if rule | nothing in the grammar; a rule picks one parse | ambiguity still present, one parse chosen | | Self-delimiting conditional | a closing terminator, or bracketed arms required | ambiguity cannot arise | | Matched / unmatched forms | the grammar is split into two statement categories | only one derivation exists | 1. **Make the conditional self-delimiting.** Require a closing keyword that ends the conditional, or require every arm to be a bracketed block. The inner conditional then has a visible end, and an else appearing after that end can only belong to the outer one. The choice is no longer available to make wrongly. 2. **Split the grammar into matched and unmatched statements.** Define a *matched* statement as one in which every conditional has both arms, and permit only a matched statement in the then-arm of a conditional that has an else. An unmatched conditional then cannot sit where it could steal a following else, so exactly one derivation exists for the string and the grammar is unambiguous without any external rule. A third design sidesteps the question entirely: a **layout-sensitive** grammar, where indentation is part of the syntax, makes the visual nesting the actual nesting, and the ambiguity never arises because the text was never ambiguous to begin with. ## What this means for how you write conditionals Even where the grammar allows a bare statement as an arm, the defensive habits are cheap: - **Bracket every arm**, including one-statement arms, so nesting is explicit in the text rather than implied by a rule. - **Avoid an else-less conditional as the then-arm** of another conditional; invert or combine the conditions instead. - **Do not trust alignment while reading unfamiliar code** — find the nearest unmatched conditional above the else and read from there. ## Why the ambiguity is still worth knowing It is the canonical example of a general fact about selection: the **structure** of nested conditionals is decided by a grammar rule, while a reader infers it from layout, and the two can disagree without any warning. That gap is why self-delimiting or layout-sensitive designs win, and why the rule about brackets on one-line arms outlives any particular notation. It also explains a review instinct that is otherwise hard to justify: a nested conditional whose arms are not bracketed is worth a comment even when it is, today, correct.

  • Which input combination exposes a wrongly bound else in that permission check?
    A valid token with a non-admin role. Under the nearest-unmatched binding, that input runs the deny-and-log arm; under the intended outer binding it would do nothing and fall through to whatever follows. The invalid-token input is the mirror case: nothing runs where a denial was expected.
  • If a grammar keeps bare single-statement arms, how can it still be unambiguous?
    By splitting statements into matched and unmatched forms and allowing only a matched statement — one whose conditionals all have both arms — in the then-arm of a conditional that carries an else. An else-less conditional then cannot occupy the position where it could capture a following else, so only one derivation exists.
  • Does requiring bracketed arms remove the ambiguity or merely hide it?
    It removes it. The brackets give the inner conditional an explicit end, so an else written after that end has only one possible owner and no disambiguation rule is needed. That is stronger than the nearest-unmatched convention, which leaves the grammar ambiguous and picks a winner.

saying these in an interview costs you the question

  • Says indentation decides which conditional owns the else
  • Claims the parser rejects the text as ambiguous
  • Binds the else to the outermost conditional in the nesting
  • Thinks the ambiguity is a run-time rather than parse-time issue
  • Believes only a stated convention can resolve it, never the grammar