Reentrant Bison / Flex, как получить сообщение об ошибке для каждого экземпляра yyscan_t - PullRequest
0 голосов
/ 01 апреля 2020

Я пытаюсь создать программу, которая использует многопоточность с flex / bison для анализа больших объемов данных. Я немного растерялся от того, как получить yyerror реентерабельным способом.

В предыдущем не входящем тесте с бизоном / флексом я использовал extern, чтобы получить yyerror

extern void yyerror(const char*);

void yyerror(const char* msg) {
    std::cout << " Error: " + std::string(msg) << std::endl;
    ...
    calling appropriate code to handle error etc
    ...
}

Теперь я пытаюсь реализовать это с помощью реентерабельных бизонов и гибких.

Используя пример кода от пользователя @rici Потокобезопасный / повторно входящий бизон + flex , я пытаюсь понять, как я смогу получить сообщение об ошибке после yyparse называется. Как я могу реализовать следующее?


class container {

public:

bool errorOccured;
std::string errorMessage;

void parse() {
    yyscan_t scanner;
    yylex_init(&scanner);
    yy_scan_string("123 + + 123 \n", scanner);
    yyparse(scanner);
    yylex_destroy(scanner);
    //errorOccured = ?;
    //errorMessage = ?;
}

bool checkIfErrorOccured() {
    std::cout << errorMessage << std::endl;
    return errorOccured;
}

}

Потокобезопасный / повторно входящий бизон + flex Для справки здесь используется код lex, написанный пользователем @ rici

%option noinput nounput noyywrap 8bit nodefault                                 
%option yylineno
%option reentrant bison-bridge bison-locations                                  

%{
  #include <stdlib.h>                                                           
  #include <string.h>
  #include "parser.tab.h"                                                   

  #define YY_USER_ACTION                                             \
    yylloc->first_line = yylloc->last_line;                          \
    yylloc->first_column = yylloc->last_column;                      \
    if (yylloc->last_line == yylineno)                               \
      yylloc->last_column += yyleng;                                 \
    else {                                                           \
      yylloc->last_line = yylineno;                                  \
      yylloc->last_column = yytext + yyleng - strrchr(yytext, '\n'); \
    }
%}                                                                              
%%
[ \t]+            ;                                                  
#.*               ;                                                  

[[:digit:]]+      *yylval = strtol(yytext, NULL, 0); return NUMBER;  

.|\n              return *yytext;

бизон

%define api.pure full
%define parse.error verbose
%locations
%param { yyscan_t scanner }

%code top {
  #include <stdio.h>
  #include <string.h>
} 
%code requires {
  typedef void* yyscan_t;
}
%code {
  int yylex(YYSTYPE* yylvalp, YYLTYPE* yyllocp, yyscan_t scanner);
  void yyerror(YYLTYPE* yyllocp, yyscan_t unused, const char* msg);
}

%token NUMBER UNOP
%left '+' '-'
%left '*' '/' '%'
%precedence UNOP
%%
input: %empty
     | input expr '\n'      { printf("[%d]: %d\n", @2.first_line, $2); }
     | input '\n'
     | input error '\n'     { yyerrok; }
expr : NUMBER
     | '(' expr ')'         { $$ = $2; }
     | '-' expr %prec UNOP  { $$ = -$2; }
     | expr '+' expr        { $$ = $1 + $3; }
     | expr '-' expr        { $$ = $1 - $3; }
     | expr '*' expr        { $$ = $1 * $3; }
     | expr '/' expr        { $$ = $1 / $3; }
     | expr '%' expr        { $$ = $1 % $3; }

%%

void yyerror(YYLTYPE* yyllocp, yyscan_t unused, const char* msg) {
  fprintf(stderr, "[%d:%d]: %s\n",
                  yyllocp->first_line, yyllocp->first_column, msg);
}


1 Ответ

1 голос
/ 01 апреля 2020

Если анализ не выполнен, yyparse возвращает ненулевое значение. Это одинаково для парентерабельных и не реентерабельных парсеров. Поэтому вы должны всегда получить возвращаемое значение из yyparse:

status = yy_parse(scanner);

Если вы выполняете восстановление после ошибок (то есть у вас есть одно или несколько error произведений), тогда вам придется вести подсчет ошибок самостоятельно. Возвращение ошибки yyparse происходит только в случае сбоя восстановления после ошибки (или при наличии ошибки выделения памяти).

yyerror вызывается при обнаружении ошибки (до попытки восстановления ошибки). В примерах игрушек он обычно просто выводит аргумент stderr. (В конфигурации по умолчанию аргумент «синтаксическая ошибка», но вы можете получить лучшие сообщения об ошибках с помощью %define parse.error verbose.) В рабочем анализаторе с восстановлением ошибок yyerror может ничего не делать, оставляя процедуру восстановления после ошибки для попытаться создать более значимое сообщение об ошибке. Или он может хранить сообщение об ошибке бизона где-нибудь для дальнейшего использования.

Нет большой проблемы с печатью на stderr, поскольку вызов yyerror выполняется синхронно в том же потоке, что и синтаксический анализатор (bison полностью не знает о потоках). Но некоторые приложения предпочитают помещать сообщения в некую структуру данных для последующей обработки. (Вы определенно захотите учесть это в многопоточном приложении.) Для облегчения этого, как вы можете видеть в моем коде, yyerror вызывается с теми же дополнительными параметрами, что и yyparse.

В примере кода эта функция не использовалась (поэтому аргумент scanner_t называется unused). Но поскольку flex позволяет расширять объект контекста сканера дополнительными данными , это было бы разумным местом для размещения сборщика ошибок, поэтому будет полезно, если yyerror имеет к нему доступ. (Конечно, он также доступен в любом действии парсера, поскольку это параметр yyparse.)

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

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

Круговая зависимость не будет проблемой ни в одном модуль перевода, отличный от кода, сгенерированного Bison. Если вы хотите использовать метод дополнительных данных, упомянутый выше, вам нужно попросить flex сгенерировать файл заголовка и убедиться, что вы #include этот заголовок в файле, где определено yyerror.

...