"Syntax Error" 在 Bison 中没有解释

"Syntax Error" in Bison without explanation

我正在开发控制台应用程序。为了创建解释器,我使用了 Flex 和 Bison。我创建了一个语法,但每次尝试使用字符串时都会收到“语法错误”而没有任何其他解释。我尝试使用的字符串是:MKDISK -PATH=./home/erick/disk.dk -u=k -size=1000\n

我知道制作有问题

   comando : MKDISK lista_param      
               {
                  printf("Mkdisk con parametros\n");
                  Mkdisk m;
                  m.agregarParametros();
                  m.assignParameters();
               }
            ;

因为我注意到如果我添加一个没有 lista_param 的产生式,只有 MKDISK,它就可以工作,解析器将始终为那个产生式,事件如果字符串与另一个匹配。

parser.yy:

%skeleton "lalr1.cc" /* -*- C++ -*- */

%defines
%define api.parser.class {Parser}
%define api.token.constructor
%define api.value.type variant

%define parse.trace
%define parse.error verbose
%param { Driver& driver }


%code requires
{
   class Driver;
   class Comando;
   class Parametro;
   class Mkdisk;

}
%{
   using namespace std;
   #include <stdio.h>
   #include <iostream>
   #include <string>
   #include <vector>
   
   #include "driver.h"
%}


/******* TERMINALES ********/
%token <std::string> NUM"NUM" SIZE"SIZE" F"F" PATH"PATH" U"U" BF"BF" FF"FF" WF"WF" K"K" M"M" RUTA"RUTA" MKDISK"MKDISK" RMDISK"RMDISK"
%token GUION"GUION" IGUAL"IGUAL" 


/******* NO TERMINALES ********/
%start inicio;
%type <Parametro> parametro
%type <Comando> comando
%type <std::vector<Parametro>> lista_param
%type <std::string> atributo nom_param


%%

   inicio : lista_comandos "\n"
         { 
            printf("Primer nivel del arbol\n");
         }
          ;

   lista_comandos : lista_comandos comando  
                  { 
                     printf("Lista de comandos\n");
                  }
                  | comando                
                  { 
                     printf("Comando individual\n");
                  }
                  ;

   comando : MKDISK lista_param      
               {
                  printf("Mkdisk con parametros\n");
                  Mkdisk m;
                  m.agregarParametros();
                  m.assignParameters();
               }
            ;
   
   lista_param :  lista_param parametro   
                  {
                     printf("Lista de parametros\n");
                     $$=;
                     $$.push_back();
                  }
               | parametro                
                  {  
                     printf("parametro individual\n");
                     vector<Parametro> params;
                     params.push_back();
                     $$ = params;
                  }
               ;
   
   parametro : GUION nom_param IGUAL atributo 
               {  

                  printf("Quinto nivel del arbol\n");
                  Parametro param;
                  param.setNombre();
                  param.setValor();
                  $$ = param;
               }
             ;

   nom_param : SIZE     { $$=; }
             | F        { $$=; }
             | PATH     { $$=; }
             | U        { $$=; }
             ;

   atributo : NUM    { $$=; }
            | BF     { $$=; }
            | FF     { $$=; }
            | WF     { $$=; }
            | K      { $$=; }
            | M      { $$=; }
            | RUTA   { $$=; }
            ;

%%

void yy::Parser::error( const std::string& error){
  std::cout <<"\e[0;31m"<< error << std::endl;
}

lexer.l

%{
  #include <stdio.h>
  #include <string>
  #include "driver.h"
  #include "parser.tab.hh"
%}
%option case-insensitive
%option noyywrap
%option outfile="scanner.cc" 


DIGIT   [0-9]
NUM     {DIGIT}+("."{DIGIT}+)?
PATH    \"?(\/([^\/\n])*)+\"?

%%


"MKDISK"          { return yy::Parser::make_MKDISK(yytext); }
"RMDISK"          { return yy::Parser::make_RMDISK(yytext); }


"SIZE"            { return yy::Parser::make_SIZE(yytext); }
"F"               { return yy::Parser::make_F(yytext); }
"PATH"            { return yy::Parser::make_PATH(yytext); }
"U"               { return yy::Parser::make_U(yytext); }


{NUM}             { return yy::Parser::make_NUM(yytext);}
"BF"              { return yy::Parser::make_BF(yytext); }
"FF"              { return yy::Parser::make_FF(yytext); }
"WF"              { return yy::Parser::make_WF(yytext); }
"K"               { return yy::Parser::make_K(yytext); }
"M"               { return yy::Parser::make_M(yytext); }
{PATH}            { return yy::Parser::make_RUTA(yytext); }


"-"               { return yy::Parser::symbol_type(); }
"="               { return yy::Parser::symbol_type(); }

[[:blank:]]       {}
.                 { printf("Caracter no reconocido: %s\n",yytext);}

%%

void Driver::runScanner(){
    yy_flex_debug = false;
    yyin = fopen (file.c_str (), "r");
    if(yyin == NULL){
        printf("No se encontro el archivo de entrada");
        exit(1);
    }
}

void Driver::runScannerWithText(std::string text){
    yy_flex_debug = true;
    YY_BUFFER_STATE buffer = yy_scan_string(text.c_str());
}

void Driver::closeFile(){
    fclose(yyin);
}

虽然您的问题中没有包含 driver.ccdriver.hh,但我怀疑它们是从 Bison manual 中的示例 C++ 代码改编而来的。该代码允许您使用命令行标志启用扫描器或解析器跟踪。如果您没有包含示例代码的那部分,我强烈建议您将其放回原处并启用跟踪。您会发现更容易看到正在发生的事情。

这里的直接问题是,当您的扫描器看到 - 时,它会执行操作:

"-"               { return yy::Parser::symbol_type(); }

它向解析器发送一个空令牌。空标记不是有效标记,因此解析器会报错。这是跟踪(通过使用标志 -p 调用可执行文件创建):

Starting parse
Entering state 0
Stack now 0
Reading a token
MKDISK -PATH=./home/erick/disk.dk -u=k -size=1000  
Next token is token MKDISK (MKDISK)
Shifting token MKDISK (MKDISK)
Entering state 1
Stack now 0 1
Reading a token
Next token is empty symbol                   <====== AQUÍ
syntax error
Error: popping token MKDISK (MKDISK)
Stack now 0
Stack now 0

显然,当遇到这样的问题时,bison 甚至不会尝试创建有意义的错误消息。

除了修复 -= 操作外,您还需要做一些事情:

inicio : lista_comandos "\n"

虽然合法,但行不通。扫描器甚至不响应换行符(甚至不声明它们是非法的),因为没有应用扫描器规则。 (我喜欢使用 %option nodefault 这样当我错过一些可能的输入时 flex 会警告我。)但是即使扫描器确实检测到换行符,它也无法知道如何向解析器发送 "\n",因为那个令牌没有名字。由于扫描器无法发送令牌,因此规则永远无法匹配。

您必须创建一个命名的换行符并在该规则中使用它而不是 "\n"。而且,当然,您必须让扫描器在读取换行符时发送该令牌。

顺便说一句,给令牌一个与令牌名称完全相同的别名几乎没有意义。别名的目的是在错误消息中提供更好的标记名称;如果您不给令牌别名,解析器将按原样使用令牌名称。因此,提供别名的唯一要点是它是否比令牌名称更具可读性。别名没有任何其他用途。