Зубр: различай переменные по типу / YYERROR в GLR-парсерах - PullRequest
0 голосов
/ 13 июня 2018

Я работаю над парсером, использующим GNU Bison, и столкнулся с интересной проблемой.Моя настоящая проблема немного другая и в целом менее интересная, поэтому я изложу ее немного по-другому, так что в целом ответ будет более полезным.

Мне нужно различать выражения в зависимости от их типа, например:арифметические выражения из строковых выражений.Их нетерминалы верхнего уровня имеют общих предков, например

statement
    : TOK_PRINT expression TOK_SEMICOLON {...}
;
expression
    : arithmeticexpression {...}
    | stringexpression {...}
;

Теперь мне нужно иметь возможность иметь переменные в обоих видах выражений

arithmeticexpression
    : TOK_IDENTIFIER {...}
;
stringexpression
    : TOK_IDENTIFIER {...}
;

(только переменные строки-тип должен быть разрешен в случае строкового выражения, и только переменные типа int или float должны быть разрешены в случае арифметического выражения) Но это, очевидно, вызывает конфликт R / R, который присущ языку - его невозможно разрешить, потому чтоязык неоднозначен.

Конечно, я мог бы описать язык так, чтобы в стеке разбора передавались только общие объекты "Выражения", но тогда мне пришлось бы делать много ручного типа- проверка действий, которых я хотел бы избежать.Кроме того, в моем реальном сценарии использования пути через грамматику к правилам переменных настолько различны, что мне пришлось бы настолько ослабить язык, что я бы потерял много правил грамматики (т.е. потерял много(структурная информация), и мне нужно было бы написать механизмы синтаксического анализа в некоторых действиях.

Я читал о синтаксическом анализе GLR, который, похоже, может решить мою проблему.Я собираюсь использовать эту функцию: иметь грамматику, как указано выше, и YYERROR в путях, где соответствующая переменная имеет неправильный тип.

arithmeticexpression
    : TOK_IDENTIFIER {
          if(!dynamic_cast<IntVariable*>(
              symbol_table[*$<stringvalue>1]))
              YYERROR;
      }
;
stringexpression
    : TOK_IDENTIFIER {
          if(!dynamic_cast<StringVariable*>(
              symbol_table[*$<stringvalue>1]))
              YYERROR;
      }
;

, но в Bison Manual написано

  1. Во время детерминированной операции GLR эффект YYERROR такой же, как его эффект в детерминированном синтаксическом анализаторе.
  2. Эффект в отложенном действии аналогичен, но точенточка ошибки не определена;вместо этого синтаксический анализатор возвращается к детерминированной операции, выбирая неопределенный стек, для которого следует продолжить с синтаксической ошибкой.
  3. В семантическом предикате (см. Семантические предикаты) во время недетерминированного синтаксического анализа, YYERROR тихо удаляет анализ, вызвавший тест.

Я не уверен, что правильно понимаю - я понимаю это так:

  1. здесь не применимо, потому что анализ GLR не выполняетсяt детерминированный
  2. - это то, как у меня это в приведенном выше коде, но это не следует делать таким образом, потому что совершенно случайно, какой путь уничтожен YYERROR (поправьте меня, если я ошибаюсь)
  3. не решит мою проблему, потому что семантические предикаты (не семантические действия !) Должны находиться в начале правила (исправьте меня, еслиЯ ошибаюсь), в этот момент yylval из токена TOK_IDENTIFIER недоступен (поправьте меня, если я ошибаюсь), поэтому я не могу заглянуть в таблицу символов, чтобы найти тип переменной.

У кого-нибудь есть опыт в такой ситуации?Мое понимание руководства неверно?Как бы вы подошли к этой проблеме?Мне кажется, что проблема настолько естественна, что я предполагаю, что люди сталкиваются с ней достаточно часто, чтобы у бизонов было встроенное решение ...

Ответы [ 2 ]

0 голосов
/ 21 августа 2018

Пример здесь работает, но когда я собрал его вместе с решением для Bison: GLR-синтаксический анализ допустимого выражения завершается неудачно без сообщения об ошибке Я столкнулся со следующей проблемой:

переменная может быть определена более чем одним идентификатором.Вы можете иметь индекс массива или он может быть членом другого объекта.Я смоделировал эту ситуацию, имея другой нетерминал, такой как

lvalue
    : TOK_IDENTIFIER
    | lvalue '[' arithmeticexpression ']'
    | lvalue '.' TOK_IDENTIFIER

Но когда у меня теперь есть

arithmeticexpression : lvalue
stringexpression : lvalue

и (попробуйте) получить доступ к объекту из нетерминального "lvalue"как и выше, я получаю ошибку сегментации.Таким образом, кажется, что метод здесь не работает для более сложных ситуаций.


Теперь я сделал то, что я получил доступ к объекту в семантическом действии и установил $$ в nullptr, еслипеременная имеет неправильный тип.

arithmeticexpression
    : lvalue
    {
        $$ = nullptr;
        if(dynamic_cast<IntVariable*>($lvalue->getVariable()))
            $$ = new ArithmeticExpressionIntVariable($lvalue);
    }

Тогда мне нужно было распространить nullptr (это довольно много дополнительного кода), как это

arithmeticexpression
    ...
    | arithmeticexpression[left] '+' arithmeticexpressionM[right]
    {
        $$ = nullptr;
        if($left && $right)
            $$ = new ArithmeticExpressionPlus($left, $right);
    }

Так что теперь, в

expression
    : arithmeticexpression
    | stringexpression
(note: I have "Expression* expr;" in the "%union{" declaration
and "%type <expression> expr" in the prologue)

У меня есть неоднозначность: один и тот же входной текст может быть проанализирован двумя разными способами, но только один из них будет иметь значение != nullptr.На данный момент мне нужна пользовательская процедура слияния, которая в основном просто выбирает ненулевое значение.

Для этого я объявил об этом в прологе моего файла зубров, как это

static Expression* exprMerge (yy::semantic_type x0, yy::semantic_type x1);

и определил его в эпилоге следующим образом

static Expression* exprMerge (YYSTYPE x0, YYSTYPE x1) \
{   
    /* otherwise we'd have a legitimate ambiguity */
    assert(!x0.expr || !x1.expr);
    return x0.expr ? x0.expr : x1.expr;
}

наконец, мне пришлось сказать bison, чтобы использовать эту процедуру для устранения неоднозначности, используя

expression
    : arithmeticexpression  %merge <exprMerge>
    | stringexpression %merge <exprMerge>

Честно говоря, я думаю, что этоэто довольно много усилий, которые не были бы необходимы, если бы семантические предикаты проверялись бизоном (по крайней мере те, которые стоят за правилом), когда он пытается объединить пути, а не исключать пути до того, как они объединятся.

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

0 голосов
/ 13 июня 2018

Примечание: Поскольку по крайней мере bison 3.0.1 и начиная с bison 3.0.5, есть ошибка в обработке действий семантического предиката, из-за которой bison выводит директиву #line в середине.линии, вызывая сбой компиляции.Простое исправление описано в этом ответе и было зафиксировано в maint ветви хранилища зубров.(Сборка бизонов из репозитория не для слабонервных. Но достаточно будет загрузить data/c.m4 из этого репозитория, если вы не хотите просто редактировать файл самостоятельно.)


ДавайтеНачните с конкретного вопроса о YYERROR в синтаксическом анализаторе GLR.

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

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

Семантический предикат имеет доступ к токену предпросмотра, еслиодин, через переменную yychar.Если yychar не является ни YYEMPTY, ни YYEOF, соответствующие семантические значения и информация о местоположении находятся в yylval и yylloc, соответственно.(см. Жетоны Lookahead ).Однако перед использованием этой функции необходимо соблюдать осторожность;если в этой точке в грамматике нет двусмысленности, вполне вероятно, что синтаксический анализатор бизонов выполнил семантический предикат, не прочитав лексем предпросмотра.

Таким образом, ваше понимание было близко, но не совсем правильно:

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

  2. Поскольку отложенные действия не выполняются до устранения неоднозначности, YYERROR в отложенном действии будет эффективно выполняться, как если бы он находился в детерминированном состоянии, как в предыдущем абзаце.Это верно, даже если есть специальная процедура слияния;если выполняется пользовательское объединение, all альтернативы, участвующие в пользовательском объединении, будут отброшены.После этого обычный алгоритм восстановления после ошибок будет продолжен.Я не думаю, что точка восстановления после ошибки является «случайной», но предсказать, где это произойдет, непросто.

  3. Как указано выше, семантические предикаты могут появляться в любом месте правила,и иметь доступ к жетону предпросмотра, если он есть.(Руководство Bison немного вводит в заблуждение: семантический предикат не может содержать вызов YYERROR, поскольку YYERROR является оператором, а содержимое блока семантического предиката является выражением. Если значение семантического предиката равно falseзатем синтаксический анализатор выполняет действие YYERROR.)


Фактически это означает, что вы можете использовать семантические предикаты вместо (или так же) действий, но в основномтак, как вы предлагаете:

arithmeticexpression
    : TOK_IDENTIFIER %?{
          dynamic_cast<IntVariable*>(symbol_table[*$1])
      }
stringexpression
    : TOK_IDENTIFIER %?{
          dynamic_cast<StringVariable*>(symbol_table[*$1])
      }

Примечание: Я удалил объявления типов из $<stringvalue>1, потому что:

  1. Это в основном нечитаемо, по крайней мере для меня,

  2. Что еще более важно, как явное приведение, оно не типобезопасно.

Вы должны объявить семантический тип ваших токенов:

%type <stringvalue> ID

, который позволит бизону проверять тип.


Является ли использование семантических предикатов для этой целихорошая идея - другой вопрос.


...