2014-01-27 3 views
0

Я определил два набора идентификаторов IDENTIFIER_ONE и IDENTIFIER_TWO, которые являются одновременно эксклюзионными подмножествами IDENTIFIER. Я хотел бы написать парсер, что:Попробуйте первое правило, если второе правило не удалось

"i1(arg) EOS" can't be parsed (1) 
"i2(arg) EOS" can be parsed (2) 
"i1(arg) = value EOS" can be parsed (3) 
"i2(arg) = value EOS" can be parsed (4) 

где i1 (соответственно, i2.) Принадлежит IDENTIFIER_ONE; (соответственно, IDENTIFIER_TWO.) arg и value принадлежат к IDENTIFIER. Следующий parser.mly уже реализовал все моменты я после, кроме (4):

identifier: 
| IDENTIFIER_ONE { $1 } 
| IDENTIFIER_TWO { $1 } 

block_statement_EOS: 
| identifier LPAREN identifier RPAREN EQUAL identifier EOS { BSE_Let ($1, $3, $6) } 
| IDENTIFIER_TWO LPAREN identifier RPAREN EOS { BSE_I_I ($1, $3) } 

Учитывая i1(arg) = value EOS в качестве входных данных, а цель (3) это правильно читается как BSE_Let (i1, arg, value). Однако, учитывая ввод i2(arg) = value EOS, он останавливает разбор после чтения EQUAL. Я думаю, это потому, что раз парсе встречаются i2(arg), он переходит во 2-ое правило block_statement_EOS, а затем EQUAL не может быть разобран.

В идеале я бы надеялся, что анализатор может попробовать 1-е правило block_statement_EOS, если 2-е правило выходит из строя. Может ли кто-нибудь помочь мне сделать это возможным?

PS: Если я напишу parser.mly следующим образом, все цели могут быть достигнуты. Кто-нибудь знает, почему? Кроме того, я действительно не нравится этот обходной путь, потому что мне нужно написать identifier вместо двух подмножеств во многих других правил, я хотел бы надеяться, более элегантное решение ...

block_statement_EOS: 
| IDENTIFIER_ONE LPAREN identifier RPAREN EQUAL identifier EOS { BSE_Let ($1, $3, $6) } 
| IDENTIFIER_TWO LPAREN identifier RPAREN EQUAL identifier EOS { BSE_Let ($1, $3, $6) } 
| IDENTIFIER_TWO LPAREN identifier RPAREN EOS { BSE_I_I ($1, $3) } 

ответ

0

Когда парсер встречает LPAREN после IDENTIFIER_TWO, он должен решить, следует ли перенести или уменьшить:

  • сдвиг: положить LPAREN в стек;
  • уменьшить: заменить IDENTIFIER_TWO, который находится на вершине стека, на identifier.

Если анализатор выбирает сдвиг, он никогда не будет уменьшать эту конкретную IDENTIFIER_TWO в identifier (потому, что эта IDENTIFIER_TWO никогда не будет на вершине снова в стеке), а это означает, что он всегда будет уменьшить второе правило block_statement_EOS.

Если анализатор выбирает уменьшить, он никогда не будет уменьшать второе правило block_statement_EOS, как это правило начинается с IDENTIFIER_TWO и не identifier.

Вот почему ваша вторая версия работает, потому что нет необходимости выбирать между сдвигом и уменьшением после IDENTIFIER_TWO. Выбор сделан позже, если хотите.

+0

Спасибо ... Давайте по-прежнему возьмем пример 'i2 (arg) = value EOS', означает ли это, что парсер выбирает' shift', когда он встречает 'LPAREN'? Жаль, что парсер не смог попробовать другой выбор ('reduce'), если один выбор (' shift') приводит к сбою ... Есть ли другой способ обойти, кроме моей второй версии, которая мне не нравится? – SoftTimur

+1

В любой момент все символы сдвигаются (т.е. накладываются на стек). Смещение в основном означает: чтение следующего символа. То, что вы хотите, подразумевало бы откат, что потенциально очень неэффективно.Однако возможно, что другие технологии синтаксического анализа будут работать, но я не знаком с ними. Взгляните на Менхира, возможно, он справится с этим делом лучше. – Bardou

Смежные вопросы