Как я могу передать байты лексера данных одновременно? - PullRequest
1 голос
/ 10 июля 2019

Я пытаюсь адаптировать парсер lexer + в потоковый интерфейс в моей системе, который имеет следующий интерфейс:

bool writeData(stream *obj, char *data, size_t length); //lets the stream know of data coming from upstream so that it prepare it to be read from downstream
void read(stream *obj, char *data, size_t length); //read the processed data
size_t readLengthAvailable(stream *obj); //return the amount of data available to be read

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

[ \t]   ; // ignore all whitespace
[0-9]+\.[0-9]+  {yylval->fval = atof(yytext); return T_FLOAT;}
[0-9]+      {yylval->ival = atoi(yytext); return T_INT;}
\n      {return T_NEWLINE;}
"+"     {return T_PLUS;}
"-"     {return T_MINUS;}
"*"     {return T_MULTIPLY;}
"/"     {return T_DIVIDE;}
"("     {return T_LEFT;}
")"     {return T_RIGHT;}
calculation: line { *(int*)out = $1; $$ = $1; YYACCEPT ; }
;

line: T_NEWLINE
    | mixed_expression T_NEWLINE { $$ = (int)$1; }
    | expression T_NEWLINE { $$ = $1; }
    | T_QUIT T_NEWLINE { printf("bye!\n"); exit(0); }
;

mixed_expression: T_FLOAT                        { $$ = $1; }
      | mixed_expression T_PLUS mixed_expression     { $$ = $1 + $3; }
      | mixed_expression T_MINUS mixed_expression    { $$ = $1 - $3; }
      | mixed_expression T_MULTIPLY mixed_expression { $$ = $1 * $3; }
      | mixed_expression T_DIVIDE mixed_expression   { $$ = $1 / $3; }
      | T_LEFT mixed_expression T_RIGHT      { $$ = $2; }
      | expression T_PLUS mixed_expression       { $$ = $1 + $3; }
      | expression T_MINUS mixed_expression      { $$ = $1 - $3; }
      | expression T_MULTIPLY mixed_expression   { $$ = $1 * $3; }
      | expression T_DIVIDE mixed_expression     { $$ = $1 / $3; }
      | mixed_expression T_PLUS expression       { $$ = $1 + $3; }
      | mixed_expression T_MINUS expression      { $$ = $1 - $3; }
      | mixed_expression T_MULTIPLY expression   { $$ = $1 * $3; }
      | mixed_expression T_DIVIDE expression     { $$ = $1 / $3; }
      | expression T_DIVIDE expression       { $$ = $1 / (float)$3; }
;

expression: T_INT               { $$ = $1; }
      | expression T_PLUS expression    { $$ = $1 + $3; }
      | expression T_MINUS expression   { $$ = $1 - $3; }
      | expression T_MULTIPLY expression    { $$ = $1 * $3; }
      | T_LEFT expression T_RIGHT       { $$ = $2; }
;

В настоящее время мой код для подачи данных в анализатор выглядит так:

    FILE* yyin = stdin;
    int result = 0;

    yyscan_t scanner;
    yy_lex_init(&scanner);
    do {
        result = 0;
        char *line = NULL;
        size_t size = 0;
        if (getline(&line, &size, stdin) == -1) {
            printf("No line\n");
            fflush(0);
            continue;
        }
        yy_scan_bytes(line, size - 2, scanner); // - 2 to remove CRLF
        int parseResult = yy_parse(scanner, &result);
        printf("[Result (%d)]: %d\n", parseResult, result);
        fflush(0);
    } while(!feof(yyin));

однако, когда я пытаюсь сделать что-то вроде «1 + CRLF» с последующим «1», я ожидаю получить ошибку, за которой следует 2, но я получаю синтаксическую ошибку с последующим 1.

1    +     1
[Result (0)]: 2
1 +
Parse error: syntax error
[Result (1)]: 0
1
[Result (0)]: 1

Есть ли способ получить изгиб, чтобы поднять, где он остановился?

Спасибо!

1 Ответ

1 голос
/ 11 июля 2019

Из комментария:

Проблема в том, что API-интерфейс потока только уведомляет больше данных, вызывая функцию обратного вызова, что означает, что если предыдущий обратный вызов просто не вернул поток, он недоступен для вызоваОбратный вызов снова с новыми данными

Этот комментарий предполагает, что лексинг выполняется в контексте обратного вызова.Обратный вызов должен обрабатывать все байты;не будет другого обратного вызова, пока этот обратный вызов не вернется.

Lex (и Yacc) не могут использоваться таким образом.Вы должны построить либо блокирующий API поверх этого механизма ввода (дождаться некоторого семафора, который его ударил обратным вызовом), либо использовать опрос (вращаться в цикле с некоторым сном, пока API не покажет, что байты доступны).

С Lex и Yacc довольно трудно продолжить анализ предсказуемых фрагментов ввода, так что yyparse возвращается между ними.Например, если вы хотите, чтобы сканирование каждого определения верхнего уровня на языке требовало отдельного вызова yyparse.Хаки требуют, чтобы синтаксический анализ мог продолжаться там, где прервался предыдущий вызов.

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

...