首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Bison在错误的行后输出字符串

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

Stack Overflow用户
提问于 2013-02-26 01:29:52
回答 2查看 242关注 0票数 1

输入

代码语言:javascript
复制
 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;  

正在产生输出

代码语言:javascript
复制
  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行下,这是错误实际发生的地方。我已经看了好几个小时了,还是想不明白。

代码语言:javascript
复制
%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;
}

打印由以下人员处理:

代码语言:javascript
复制
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");
}
}
EN

回答 2

Stack Overflow用户

发布于 2013-02-26 02:18:41

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

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

正确地将错误/警告与源代码混合的一种简单技术是将所有错误/警告写到一个临时文件中,将文件偏移量放在每个错误的前面。然后,可以对该文件进行排序,然后可以重新读取输入,从而在适当的位置插入错误消息。这种策略的好处是,它避免了为每个错误维护行号,这会显着降低词法分析的速度。当然,如果你允许像C的#include这样的结构,它就不会那么容易工作了。

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

票数 1
EN

Stack Overflow用户

发布于 2013-02-26 02:47:58

正如rici所指出的,bison生成一个LALR(1)解析器,因此它使用一个先行标记来知道要采取什么操作。然而,它并不总是使用先行标记--在某些情况下(无论先行先行,只有一种可能性),它使用缺省缩减,这可以在没有先行先行的情况下减少规则(并运行相关的操作)。

在你的例子中,如果你真的需要的话,你可以利用这一点让动作在没有先行的情况下运行。有问题的特定规则(触发先行查找的要求)是:

代码语言:javascript
复制
variables:
    variable IS statement {checkTypes($1, $3, 2);} |
    variable IS statement {checkTypes($1, $3, 2);} variables ;

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

代码语言:javascript
复制
variables: vardecl | vardecl variables ;
vardecl: variable IS statement {checkTypes($1, $3, 2);}

这将最终使用默认的缩减,因为它不需要提前在两个缩减/动作之间做出决定。

请注意,上面的条件取决于是否能够在没有先行的情况下找到statement的结尾,只要所有语句都明确以;结尾,就应该是这种情况

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15072799

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档