1

输入

 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");
}
}
4

2 回答 2

1

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

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

正确地将错误/警告与源代码散布的一种简单技术是将所有错误/警告写入临时文件,将文件偏移量放在每个错误的前面。然后可以对该文件进行排序,然后可以重新读取输入,在适当的点插入错误消息。这个策略的好处是它避免了为每个错误维护行号,这明显减慢了词法分析。当然,如果您允许像 C's 这样的构造,它不会那么容易工作#include

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

于 2013-02-25T18:18:40.560 回答
0

正如 rici 所指出的,bison 生成一个 LALR(1) 解析器,因此它使用一个前瞻标记来知道要采取什么行动。但是,它并不总是使用前瞻标记——在某些情况下(无论前瞻如何,只有一种可能性),它使用默认缩减,可以缩减规则(并运行相关操作)而无需前瞻。

在你的情况下,如果你真的需要,你可以利用它来让动作在没有前瞻的情况下运行。有问题的特定规则(触发前瞻要求)是:

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

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

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

这将最终使用默认减少,因为它不需要前瞻来决定两个减少/操作之间。

请注意,上述内容取决于能够在statement没有前瞻的情况下找到 a 的结尾,只要所有语句都以 a 明确结束就应该是这种情况;

于 2013-02-25T18:47:58.233 回答