Бизон принимает ввод после выполнения правила - PullRequest
0 голосов
/ 15 мая 2018

Я хочу разобрать текст для одного запроса. Этот запрос заканчивается точкой с запятой. Это будет как sql. Пример: CREATE TABLE 'somename'; Мой файл у

%{
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stdbool.h>
#include "ast.h"

extern int yylex(void);
extern void yyerror(char const *msg);

QueryNode *queryNode;

%}

%union {
int integer;
char *str;
char chr;
bool boolean;
int intval;
char *strval;
ObjectTypeNode *objectTypeNode;
CreateClauseNode *createClauseNode;
QueryNode *queryNode;
}

%token  NUMBER
%token  INTNUM

%token<str> CREATE_KEYWORD
%token<str> DATABASE_KEYWORD
%token<str> TABLE_KEYWORD
%token<str> LETTER
%token<str> STRING
%token<str> IDENTIFIER
%token<chr> LEFT_BRACKET RIGHT_BRACKET COMMA SEMICOLON EOL

%type<objectTypeNode> object_type
%type<createClauseNode> create_clause
%type<queryNode> query

%start input

%%
input:      SEMICOLON EOL                               { queryNode = NULL; }
        |   query   SEMICOLON EOL                       { queryNode = $1; }
        ;

query:  create_clause                                   { $$ = CreateQueryNode($1, CREATE_CLAUSE_TYPE); }
        ;

create_clause:  CREATE_KEYWORD  object_type STRING      { $$ = CreateCreateClauseNode($2, $3); }
                ;

object_type:    DATABASE_KEYWORD                        { $$ = CreateObjectTypeNode(DATABASE_OBJECT); }
            |   TABLE_KEYWORD                           { $$ = CreateObjectTypeNode(TABLE_OBJECT); }
            ;
%%
void yyerror(char const *msg) {
    printf("Error: %s\n", msg);
}

И мой файл l

%{
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include "ast.h"
#include "y.tab.h"
%}

%option noyywrap nodefault yylineno case-insensitive

%%
CREATE                  { yylval.strval = "create"; return CREATE_KEYWORD; }
DATABASE                { return DATABASE_KEYWORD; }
TABLE                   { return TABLE_KEYWORD; }
"("                     { return LEFT_BRACKET; }
")"                     { return RIGHT_BRACKET; }
";"                     { return SEMICOLON; }

-?[0-9]+                { yylval.intval = atoi(yytext); return INTNUM; }

L?'(\\.|[^\\'])+'   |
L?\"(\\.|[^\\"])*\"     { yylval.strval = yytext;   return STRING; }

[a-zA-Z]+[0-9]*         { return IDENTIFIER; }
[a-zA-Z]+               { return LETTER; }
[\n]                    { printf("eol\n"); return EOL; }
[ \t\f\v]               { ; }

.                       { return *yytext; }
%%

Я использую функцию yyparse() в другой основной функции. Файл main.c

#include <stdio.h>
#include <stdlib.h>
#include "ast.h"
#include "y.tab.h"

extern QueryNode *queryNode;

int main(int argc, char *argv[]) {
    int result = yyparse();
    if(result == 0 && queryNode != NULL) {
        printf("AST created\n");
    } else {
        printf("Problem!\n");
    }
    return 0;
}

Когда я ввожу как CREATE TABLE 'testo'; yyparse не завершается и программа ожидает в строке int result = yyparse();. Как я могу это исправить? Я использую флекс и бизон. Я хочу завершить с этим вводом.

1 Ответ

0 голосов
/ 16 мая 2018

В исходной версии этого вопроса основными правилами в спецификации грамматики были:

input: SEMICOLON {queryNode = NULL;YYACCEPT;} |query SEMICOLON {queryNode = $ 1;YYACCEPT;};

Как я уже говорил в исходной версии этого ответа, эти правила гарантируют, что yacc примет запрос, сопровождаемый точкой с запятой, как только встретится точка с запятой, из-за YYACCEPTaction:

yacc "принимает", потому что вы использовали YYACCEPT в действии.YYACCEPT означает «как только эта продукция будет распознана, примите ввод, даже если он не был полностью использован».Так что он делает то, что вы просили.

Затем я предложил удалить действия YYACCEPT, чтобы синтаксический анализатор не возвращался до тех пор, пока лексер не сообщит о конце ввода:

Если вы хотите принять ввод, только если весь ввод соответствует грамматике, просто не вызывайте YYACCEPT.Yacc автоматически примет соответствие, если начало производства совпадает, а следующий токен является маркером конца ввода.

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

Если вы хотите, чтобы лексер считывал только одну строку, которая должна быть допустимой командой, выможно легко сделать это, удалив YYACCEPT из действий анализатора и заставить сканер возвращать указание конца файла, когда он видит символ новой строки:

\n    { return 0; }

(Возвращение нуля - это то, как заканчиваются сигналы сканера-of-input.)

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

Вы также можете играть в игры в синтаксическом анализаторе, как с вашим новым предложением, но если сканер возвращает маркер новой строки, когда он видит новую строку.Затем вы могли бы принять или отклонить ввод при получении токена новой строки, используя YYACCEPT, YYABORT и error продукцию:

input: SEMICOLON EOL              { queryNode = NULL; YYACCEPT; }
     | query SEMICOLON EOL        { queryNode = $1;   YYACCEPT; }
     | query error EOL            { YYABORT; }
     ;

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

Хотя это решение имеет некоторые преимущества, оно несколько сложнее, чем то, которое просто возвращает 0 при прочтении новой строки.Поэтому трудно оправдать дополнительную сложность.

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


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

...