Висячий else — это проблема в программировании генераторов парсеров , в которых необязательное предложение else в операторе if–then(–else) приводит к неоднозначности вложенных условных операторов. Формально, ссылочная контекстно-свободная грамматика языка неоднозначна , что означает, что существует более одного правильного дерева синтаксического анализа .
Во многих языках программирования условно выполняемый код можно записать в двух формах: форма if-then и форма if-then-else, причем предложение else необязательно:
если а, то s, если б, то s1 , иначе s2
Это приводит к неоднозначности интерпретации, когда имеются вложенные операторы, особенно всякий раз, когда форма if-then появляется как s1
if-then-else:
если а, то если б, то с, иначе с2
В этом примере s
однозначно выполняется, когда a
is true и b
is true, но можно интерпретировать s2
как выполняемое, когда a
is false (таким образом присоединяя else к первому if) или когда a
is true и b
is false (таким образом присоединяя else ко второму if). Другими словами, можно рассматривать предыдущее утверждение как одно из следующих выражений:
если а, то ( если б, то с) иначе с2 если а , то ( если б, то с, иначе с2)
Проблема висячего else датируется ALGOL 60 , [1] и была решена различными способами в последующих языках. В LR-анализаторах висячий else является архетипическим примером конфликта сдвига-свертки .
Это проблема, которая часто возникает при построении компилятора , особенно при разборе без сканера . Соглашение при работе с висячим else заключается в присоединении else к соседнему оператору if, [2] что позволяет использовать однозначные контекстно-свободные грамматики, в частности. Такие языки программирования, как Pascal, [3], C [4] и Java [5], следуют этому соглашению, поэтому в семантике языка нет неоднозначности , хотя использование генератора синтаксического анализатора может привести к неоднозначным грамматикам . В этих случаях альтернативная группировка выполняется явными блоками, например, begin...end
в Pascal [6] и {...}
C.
В зависимости от подхода к построению компилятора можно предпринять различные корректирующие действия, чтобы избежать двусмысленности:
Проблему также можно решить, сделав явной связь между else и его if в синтаксисе. Обычно это помогает избежать человеческих ошибок. [7]
Возможные решения:
if
без резервного предложения является ошибкой, эффективно отличая условные выражения (т. е. if
) от условных операторов (т. е. when
и unless
, которые не имеют резервных предложений).if e do s
для одноальтернативного случая и if e1 then e2 else e3
для общего случая. [10]Далее следуют конкретные примеры.
В языке C грамматика, в частности, выглядит следующим образом:
утверждение = ... | заявление о выборе оператор выбора = ... | IF (выражение) оператор | IF (выражение) оператор ELSE оператор
Таким образом, без дополнительных правил, утверждение
если ( а ) если ( б ) s ; иначе s2 ;
может быть неоднозначно проанализировано, как если бы это было:
если ( а ) { если ( б ) s ; иначе s2 ; }
или:
если ( а ) { если ( б ) s ; } иначе s2 ;
Стандарт C разъясняет, что else
блок связан с ближайшим if
. [4] Поэтому выбирается первое дерево.
Чтобы устранить неоднозначность, приведенный выше пример можно переписать следующим образом:
заявление: open_statement | закрытое_заявление ;open_statement: IF '(' выражение ')' оператор | ЕСЛИ '(' выражение ')' закрытый_оператор ИНАЧЕ открытый_оператор ;закрытое_утверждение: не_если_утверждение | ЕСЛИ '(' выражение ')' закрытый_оператор ИНАЧЕ закрытый_оператор ;не_if_statement: ... ;
Любые другие правила грамматики, связанные с утверждениями, также могут быть продублированы таким же образом, если они могут прямо или косвенно заканчиваться на a statement
или selection-statement
нетерминал.
Однако мы даем грамматику, которая включает как операторы if, так и while.
заявление: open_statement | закрытое_заявление ;open_statement: IF '(' выражение ')' оператор | ЕСЛИ '(' выражение ')' закрытый_оператор ИНАЧЕ открытый_оператор | WHILE '(' выражение ')' open_statement ;закрытое_утверждение: простое_утверждение | ЕСЛИ '(' выражение ')' закрытый_оператор ИНАЧЕ закрытый_оператор | WHILE '(' выражение ')' закрытый_оператор ;простое_утверждение: ... ;
Наконец, мы приводим грамматику, которая запрещает двусмысленные IF-утверждения.
заявление: open_statement | закрытое_заявление ;open_statement: IF '(' выражение ')' оператор | ЕСЛИ '(' выражение ')' закрытый_оператор ИНАЧЕ открытый_оператор | WHILE '(' выражение ')' open_statement ;закрытое_утверждение: простое_утверждение | ЕСЛИ '(' выражение ')' закрытый_оператор ИНАЧЕ закрытый_оператор | WHILE '(' выражение ')' закрытый_оператор ;простое_утверждение: ... ;
С помощью этой грамматики утверждение if (a) if (b) c else d
может быть проанализировано только одним способом, поскольку другая интерпретация ( if (a) {if (b) c} else d
) получается как
заявлениеоткрытый_учетЕСЛИ '(' выражение ')' закрытый_оператор ИНАЧЕ открытый_оператор'if' '(' 'a' ')' закрытый_оператор 'else' 'd'
и затем парсинг терпит неудачу, пытаясь сопоставить closed_statement
с "if (b) c". Попытка с closed_statement
терпит неудачу таким же образом. Другой парсинг, if (a) {if (b) c else d}
) успешен:
заявлениеоткрытый_учетIF '(' выражение ')' операторЕСЛИ '(' выражение ')' закрытый_операторЕСЛИ '(' а ')' (ЕСЛИ '(' выражение ')' закрытый_оператор ИНАЧЕ закрытый_оператор)ЕСЛИ '(' а ')' (ЕСЛИ '(' б ')' в ИНАЧЕ ' г')
{{cite book}}
: |website=
проигнорировано ( помощь )