0

我正在开发一个控制台应用程序。为了创建一个解释器,我使用了 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($2);
                  m.assignParameters();
               }
            ;

因为我注意到,如果我添加一个没有 lista_param 的产品,只是 MKDISK,它可以工作,并且解析器将始终执行该产品,如果字符串与另一个字符串匹配。

解析器.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($2);
                  m.assignParameters();
               }
            ;
   
   lista_param :  lista_param parametro   
                  {
                     printf("Lista de parametros\n");
                     $$=$1;
                     $$.push_back($2);
                  }
               | parametro                
                  {  
                     printf("parametro individual\n");
                     vector<Parametro> params;
                     params.push_back($1);
                     $$ = params;
                  }
               ;
   
   parametro : GUION nom_param IGUAL atributo 
               {  

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

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

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

%%

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

词法分析器

%{
  #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);
}
4

1 回答 1

0

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

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

"-"               { 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 在遇到此类问题时甚至不会尝试创建有意义的错误消息。

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

inicio : lista_comandos "\n"

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

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

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

于 2021-08-09T02:41:04.107 回答