Bison在错误的行之后输出字符串

时间:2013-02-25 17:29:52

标签: c++ linux bison flex-lexer

输入

 1  -- Narrowing Variable Initialization  
 2  
 3  function main a: integer returns integer;  
 4      b: integer is a * 2.;  
 5  begin  
 6      if a <= 0 then  
 7          b + 3;  
 8      else  
 9          b * 4;  
10      endif;  
11  end;  

正在产生输出

  1  -- Narrowing Variable Initialization
  2  
  3  function main a: integer returns integer;
  4      b: integer is a * 2.;
  5  begin
Narrowing Variable Initialization
  6      if a <= 0 then
  7          b + 3;
  8      else
  9          b * 4;
 10      endif;
 11  end;

而不是将错误消息放在第4行,这是错误实际发生的地方。我已经看了好几个小时了,无法弄明白。

%union
{
    char* ident;
    Types types;
}

%token <ident> IDENTIFIER
%token <types> INTEGER_LITERAL
%token <types> REAL_LITERAL
%token  BEGIN_
%token  FUNCTION
%token  IS
%token  <types> INTEGER
%token  <types> REAL
%token  RETURNS

%type  <types> expression
%type  <types> factor
%type  <types> literal
%type  <types> term
%type  <types> statement
%type  <types> type
%type  <types> variable

%%

program:
    /* empty */ |
    functions ;

functions:
    function_header_recovery body ; |
    function_header_recovery body functions ;

function_header_recovery:
    function_header ';' |
    error ';' ;

function_header:
    FUNCTION {locals = new Locals();} IDENTIFIER optional_parameters RETURNS type {globals->insert($3,locals->tList);} ;

optional_parameters:
    /* empty */ |
    parameters;

parameters:
    IDENTIFIER ':' type {locals->insert($1, $3); locals->tList.push_back($3); } |
    IDENTIFIER ':' type {locals->insert($1, $3); locals->tList.push_back($3); } "," parameters;

type:
    INTEGER | REAL ;

body:
    optional_variables BEGIN_ statement END ';' ;

optional_variables:
    /* empty */ |
    variables ;

variables:
    variable IS statement {checkTypes($1, $3, 2);} |
    variable IS statement {checkTypes($1, $3, 2);} variables ;

variable:
    IDENTIFIER ':' type {locals->insert($1, $3);} {$$ = $3;} ;

statement:
    expression ';' |

...

Types checkTypes(Types left, Types right, int flag)
{
    if (left == right)
    {
        return left;
    }
    if (flag == 1)
    {
        Listing::appendError("Conditional Expression Type Mismatch", Listing::SEMANTIC);
    }
    else if (flag == 2)
    {
        if (left < right)
        {
            Listing::appendError("Narrowing Variable Initialization", Listing::SEMANTIC);
        }
    }
    return REAL_TYPE;
}

打印由:

处理
void Listing::nextLine()
{
printf("\n");
if (error == "")
{
    lineNo++;
    printf("%4d%s",lineNo,"  ");
}
else
{
    printf("%s", error.c_str());
error = "";
nextLine();
}
}

void Listing::appendError(const char* errText, int errEnum)
{
error = error + errText;

if (errEnum == 997)
{
    lexErrCount++;
}
else if (errEnum == 998)
{
    synErrCount++;
}
else if (errEnum == 999)
{
    semErrCount++;
}
}

void Listing::display()
{
printf( "\b\b\b\b\b\b    " );

if (lexErrCount + synErrCount + semErrCount > 0)
{
    printf("\n\n%s%d","Lexical Errors ",lexErrCount);
    printf("\n%s%d","Syntax Errors ",synErrCount);
    printf("\n%s%d\n","Semantic Errors ",semErrCount);
}
else
{
    printf("\nCompiled Successfully\n");
}
}

2 个答案:

答案 0 :(得分:1)

这就是bison的工作方式。它会生成一个单令牌前瞻解析器,因此在生成后读取跟随生成的令牌时,才会触发生产操作。因此,必须在与begin相关联的操作发生之前阅读variables。 (bison从不尝试组合操作,即使它们在文本上是相同的。所以它实际上无法知道哪个variables生成适用以及在看到以下标记之前要执行哪个操作。)

有多种方法可以将行号和/或列位置与每个令牌相关联,并在生成错误消息时使用该信息。通常,将错误和/或警告与输入文本交叉,需要缓冲输入;对于语法错误,您只需缓冲直到下一个令牌,但这不是一般解决方案;例如,在某些情况下,您可能希望将错误与运算符相关联,但在解析运算符的尾随参数之前,不会检测到错误。

使用source正确地散布错误/警告的一种简单方法是将所有错误/警告写入临时文件,将文件偏移量放在每个错误的前面。然后可以对此文件进行排序,然后可以重新读取输入,在适当的位置插入错误消息。这个策略的好处在于它避免了必须为每个错误维护行号,这显着减慢了词法分析。当然,如果你允许像C #include这样的结构,那么它将不会那么容易。

因为生成好的错误消息很难,甚至跟踪位置会使解析速度变慢,所以如果检测到错误,我有时会使用解析输入两次的策略。第一个解析只检测错误,如果不能做更合理的事情就提前失败;如果检测到错误,则使用更精细的解析器重新分析输入,该解析器仔细跟踪文件位置,甚至可能使用诸如缩进深度之类的启发式来尝试生成更好的错误消息。

答案 1 :(得分:0)

正如rici所说,bison产生了一个LALR(1)解析器,所以它使用一个前瞻标记来知道要采取什么行动。但是,它并不总是使用前瞻的标记 - 在某些情况下(无论前瞻哪一种只有一种可能),它使用默认的减少,这可以减少规则(并运行相关的操作)而不用前瞻。

在您的情况下,如果您确实需要,可以利用它来使操作无需前瞻。有问题的特定规则(触发前瞻性要求)是:

variables:
    variable IS statement {checkTypes($1, $3, 2);} |
    variable IS statement {checkTypes($1, $3, 2);} variables ;

在这种情况下,在看到variable IS statement之后,需要查看下一个标记以确定是否有更多变量声明以便知道要运行哪个动作(第一个或第二个)。但由于这两个动作实际上是相同的,您可以将它们组合成一个动作:

variables: vardecl | vardecl variables ;
vardecl: variable IS statement {checkTypes($1, $3, 2);}

最终会使用默认缩减,因为它不需要前瞻来决定两次缩减/操作。

请注意,上述内容取决于能够找到statement的结尾而不是前瞻,只要所有语句都以;

明确结束,就应该是这种情况。