Рекурсивный разбор спуска-от 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).