BNF: ввод неправильный нетерминал - PullRequest
2 голосов
/ 02 декабря 2010

Я разрабатываю BNF для шахматной алгебраической нотации и столкнулся с интересным случаем, ввод идет в неправильный нетерминал.

Мое правило запуска BNF следующее (обратите внимание, что это намеренно невключая рокировку или примечания):

algebraic_notation : piece start_position capture end_position promotion

piece, start_position, capture и promotion могут быть пустыми, таким образом, допускается движение, подобное 'd4'.Проблема заключается в том, что когда вводится такое движение, вход ('d4') берется start_position, что приводит к ошибке b / c, поэтому больше нет ввода для end_position, который не может быть пустым.

Очевидный способ взлома / обходного пути состоит в том, чтобы позволить end_position быть пустым, а затем проверить, получили ли мы какие-либо входные данные для него и действовать соответствующим образом.

Это работает, но я хотел бы знать,если есть способ с этим справиться.Возможно ли, чтобы ввод не переходил к первому совпадающему символу, если он приводит к тому, что все выражение не совпадает?

Другой вопрос, является ли это стандартным поведением для BNF, или проблема с yaccer, который я использую: PLY v 3.3.

Попробовал с помощью flex / bison и получил то же самое.Таким образом, это не относится к PLY.

Вот все соответствующие правила для полноты:

algebraic_notation : piece start_position capture end_position promotion

piece : KING
        | QUEEN
        | BISHOP
        | KNIGHT
        | ROOK
        | pawn

pawn : empty

start_position : FILE
                | NUMBER
                | FILE NUMBER
                | empty

end_position : FILE NUMBER
                | empty                 // this line is the hack/workaround

capture : CAPTURE
        | empty

promotion : EQUAL QUEEN
            | EQUAL ROOK
            | EQUAL KNIGHT
            | EQUAL BISHOP
            | empty

empty : 

Ответы [ 4 ]

1 голос
/ 22 марта 2015

Проблема в том, что вы игнорируете конфликт сдвига / уменьшения, который вы получаете от генератора парсера.В то время как yacc / bison (и предположительно PLY) исправит ошибки за вас, это разрешение может не выполнять то, что вы хотите, и может привести к синтаксическому анализу, который анализирует язык, отличный от того, который вы пытаетесь проанализировать.

Всякий раз, когда вы получаете сдвиг / уменьшение (или уменьшение / уменьшение) конфликт от генератора синтаксического анализатора LR, вам действительно нужно понимать, что такое конфликт (и почему он возникает), чтобы знать, можете ли вы его игнорировать или нужно ли его исправлять,Итак, давайте исправим вашу грамматику, избавившись от «хака» (что явно неправильно, а не того, что вы хотите проанализировать), а также от бесполезного «пустого» правила (которое просто смущает вещи):

%token FILE NUMBER
%%
algebraic_notation : piece start_position capture end_position promotion
piece : 'K' | 'Q' | 'B' | 'N' | 'R' | /*pawn*/
start_position : FILE | NUMBER | FILE NUMBER | /*empty*/
end_position : FILE NUMBER
capture : 'x' | /*empty*/
promotion : '=' 'Q' | '=' 'R' | '=' 'N' | '=' 'B' | /*empty*/

Теперь, когда вы запустите это через 'bison -v' (ВСЕГДА используйте -v для получения подробного выходного файла - я не уверен, что эквивалентен PLY), вы получите сообщение о конфликте сдвига / уменьшения, и есливы смотрите в файле .output и видите, что это такое:

state 7

    1 algebraic_notation: piece . start_position capture end_position promotion

    FILE    shift, and go to state 9
    NUMBER  shift, and go to state 10

    FILE      [reduce using rule 11 (start_position)]
    $default  reduce using rule 11 (start_position)

    start_position  go to state 11

Это говорит о том, что после просмотра piece, когда следующий токен - FILE, он не знаетдолжен ли он сдвигаться (трактовать FILE как (часть) start_position) или уменьшать (давая пустое start_position).Это потому, что ему нужно больше внимания, чтобы увидеть, есть ли вторая позиция для использования в качестве end_position, чтобы знать, что делать, поэтому простое игнорирование конфликта приведет к парсеру, который не сможет проанализировать множество допустимых вещей (в основном, что-либо сempty start_position и capture).

Лучший способ решить связанный с упреждением конфликт с уменьшением смещения, включающий пустое производство, подобное этому (или практически любой конфликт, связанный с пустым производством, на самом деле), заключается вразобрать грамматику - избавиться от пустого правила и продублировать любое правило, которое использует нетерминал как с таковым, так и без него.В вашем случае это дает вам правила:

algebraic_notation : piece capture end_position promotion
algebraic_notation : piece start_position capture end_position promotion
start_position : FILE | NUMBER | FILE NUMBER 

(другие правила не изменены). При этом у вас все еще есть конфликт с уменьшением смены:

state 7

    1 algebraic_notation: piece . capture end_position promotion
    2                   | piece . start_position capture end_position promotion

    FILE    shift, and go to state 9
    NUMBER  shift, and go to state 10
    'x'     shift, and go to state 11

    FILE  [reduce using rule 14 (capture)]

    start_position  go to state 12
    capture         go to state 13

По сути, мыМы только что переместили конфликт на один шаг и теперь имеем проблему с пустым правилом capture.Таким образом, мы также разглашаем это:

algebraic_notation : piece end_position promotion
algebraic_notation : piece capture end_position promotion
algebraic_notation : piece start_position end_position promotion
algebraic_notation : piece start_position capture end_position promotion
capture : 'x'

, и теперь бизоны не сообщают больше о конфликтах, поэтому мы можем быть достаточно уверены, что они будут анализироваться так, как мы хотим.Вы можете немного упростить его, избавившись от правила capture и используя литерал 'x' в правиле algebraic_notation.Лично я предпочитаю это, так как думаю, что яснее избежать ненужной косвенности:

%token FILE NUMBER
%%
algebraic_notation : piece end_position promotion
algebraic_notation : piece 'x' end_position promotion
algebraic_notation : piece start_position end_position promotion
algebraic_notation : piece start_position 'x' end_position promotion
piece : 'K' | 'Q' | 'B' | 'N' | 'R' | /*pawn*/
start_position : FILE | NUMBER | FILE NUMBER
end_position : FILE NUMBER
promotion : '=' 'Q' | '=' 'R' | '=' 'N' | '=' 'B' | /*empty*/
0 голосов
/ 03 декабря 2010

Что произойдет, если вы поместите start_position capture end_position в средний блок и удалите FILE NUMBER из start_pos, примерно так:

middle: start_pos capture end_pos
      | end_pos capture end_pos
      | capture end_pos

start_pos : FILE
      | NUMBER
      | empty

end_pos : FILE NUMBER

capture : CAPTURE
      | empty
0 голосов
/ 21 марта 2015

Этот вопрос является хорошей иллюстрацией проблемы теории компьютерных наук, удаления эпсилон (или пустых) произведений из грамматики.Проблемы с неоднозначностью шахматной нотации могут быть решены (для yacc или PLY) путем упрощения грамматики для удаления пустых произведений.Об этом много материала, как на SO / SE, так и на других сайтах.Я добавляю библиографию для заинтересованного читателя.

Выполняя бездумную трансформацию правил для удаления слепых / пустых / эпсилон-продукций, мы получаем следующий CFG:

algebraic_notation : piece start_position capture end_position promotion
                   | piece start_position capture end_position 
                   | piece start_position capture promotion
                   | piece start_position end_position promotion
                   | piece capture end_position promotion
                   | piece start_position capture
                   | piece start_position end_position
                   | piece capture end_position
                   | piece start_position promotion
                   | piece capture promotion
                   | piece end_position promotion
                   | piece promotion
                   | piece end_position 
                   | piece capture 
                   | piece start_position 
                   | piece 
                   | start_position capture end_position promotion
                   | start_position capture end_position
                   | start_position capture promotion
                   | start_position end_position promotion
                   | capture end_position promotion
                   | start_position capture
                   | start_position end_position
                   | capture end_position
                   | end_position promotion
                   | start_position 
                   | capture 
                   | end_position
                   | promotion
piece : KING
        | QUEEN
        | BISHOP
        | KNIGHT
        | ROOK

start_position : FILE
                | NUMBER
                | FILE NUMBER

end_position : FILE NUMBER

capture : CAPTURE

promotion : EQUAL QUEEN
            | EQUAL ROOK
            | EQUAL KNIGHT
            | EQUAL BISHOP

(Возможно, это может произойтиможно упростить, удалив те комбинации, которые не встречаются в шахматной записи, но это упражнение для читателя.

Библиография

Лучшими книгами для этого, вероятно, являются:

Или просто перейдите к слайдам из класса Джеффа Уллмана:

Или несколько вопросов по SO / SE:

0 голосов
/ 03 декабря 2010

Я не использовал PLY, и, не видя полных файлов flex / bison, которые вы пробовали, я мог бы выбрать несущественную проблему, но мне кажется, что вы не даете парсеру понять, что больше не будетдля текущего algebraic_notation правила.Вы не говорите, как вы знаете, что ввод 'd4' был сопоставлен с start_position, но если парсер знал, что у него есть все токены для правила, и единственный непустой токен - end_position, он должен был бы соответствовать 'd4'that.

Как насчет введения токена, отмечающего конец строки, например EOL.Итак, ваше первое правило становится:

algebraic_notation : piece start_position capture end_position promotion EOL

, и анализатор теперь видит токен 'd4', за которым следует EOL - это меняет поведение?

...