Рекурсивный разбор спуска-от LL (1) вверх
следующая простая грамматика" выражения калькулятора " (BNF) может быть легко проанализирована с помощью тривиального парсера рекурсивного спуска, который является предиктивным LL (1):
<expr> := <term> + <term>
| <term> - <term>
| <term>
<term> := <factor> * <factor>
<factor> / <factor>
<factor>
<factor> := <number>
| <id>
| ( <expr> )
<number> := d+
<id> := [a-zA-Z_]w+
потому что всегда достаточно увидеть следующий токен, чтобы знать правило для выбора. Однако предположим, что я добавлю следующее правило:
<command> := <expr>
| <id> = <expr>
С целью взаимодействия с калькулятором в командной строке, с переменными, как это:
calc> 5+5
=> 10
calc> x = 8
calc> 6 * x + 1
=> 49
это правда, что я могу не используйте простой ll (1) прогностический парсер для разбора <command>
правила ? Я попытался написать парсер для него, но кажется, что мне нужно знать больше токенов вперед. Является ли решение использовать backtracking, или я могу просто реализовать LL (2) и всегда смотреть на два токена вперед ?
как генераторы RD parser справляются с этой проблемой (например, ANTLR)?
4 ответов
проблема с
<command> := <expr>
| <id> = <expr>
это когда вы "видите"<id>
вы не можете сказать, является ли это началом назначения (второе правило) или это "<factor>
". Вы узнаете только тогда, когда прочтете следующий токен.
AFAIK ANTLR-LL (*) (и также способен генерировать Парсеры rat-pack, если я не ошибаюсь), поэтому он, вероятно, будет обрабатывать эту грамматику, рассматривая сразу два токена.
если вы можете играть с грамматикой, я бы предложил либо добавить ключевое слово для задания (например,let x = 8
):
<command> := <expr>
| "let" <id> "=" <expr>
или использовать =
для обозначения оценки:
<command> := "=" <expr>
| <id> "=" <expr>
Я думаю, что есть два способа решить это с помощью рекурсивного парсера спуска: либо с помощью (больше) lookahead, либо с помощью backtracking.
Lookahead
command() {
if (currentToken() == id && lookaheadToken() == '=') {
return assignment();
} else {
return expr();
}
}
откат
command() {
savedLocation = scanLocation();
if (accept( id )) {
identifier = acceptedTokenValue();
if (!accept( '=' )) {
setScanLocation( savedLocation );
return expr();
}
return new assignment( identifier, expr() );
} else {
return expr();
}
}
проблема в том, что грамматика:
<command> := <expr>
| <id> = <expr>
не является взаимно рекурсивной процедурой. Для рекурсивного приличного парсера вам нужно будет определить нерекурсивный эквивалент.
rdentato post показывает, как это исправить, предполагая, что вы можете играть с грамматикой. Эта powerpoint излагает проблему немного более подробно и показывает, как ее исправить: http://www.google.com/url?sa=t&source=web&ct=res&cd=7&url=http%3A%2F%2Fxml.cs.nccu.edu.tw%2Fcourses%2Fcompiler%2Fcp2006%2Fslides%2Flec3-Parsing%26TopDownParsing.ppt&ei=-YLaSPrWGaPwhAK5ydCqBQ&usg=AFQjCNGAFrODJxoxkgJEwDMQ8A8594vn0Q&sig2=nlYKQVfakmqy_57137XzrQ
ANTLR 3 использует парсер" LL (*) " в отличие от парсера LL(k), поэтому он будет смотреть вперед, пока не достигнет конца ввода, если это необходимо, без отхода, используя специально оптимизированные детерминантные конечные автоматы (DFA).