yacc中获取行号的方法

Methods in yacc to get line no

在 yacc 中获取行号的不同方法有哪些?我知道 yylineno 但当 yacc 读取先行标记时它可以关闭 1。

我在 yacc 中使用 yytext 时遇到了同样的问题。它没有给出预期的令牌。我在 lex 中使用 yylval.str 来完成它。有没有类似的东西来获得确切的行号。

Bison 中是否有更好的选项可以使处理更容易和准确

扫描器的作用是生成一系列词法标记,每个词法标记都有一个类型、一个语义值,可能还有一个位置。解析器接收这个标记流,并将它们处理成输入的结构表示。

应该清楚,在解析器中,"the last semantic value scanned" 的价值很小或没有价值,特别是因为解析器通常需要要求扫描器向前看至少一个标记才能决定如何进行.但更一般地说,解析器操作将结合有关一系列令牌(以及一系列已经减少的产品)的信息,因此没有 "one value" 在操作中有意义。

同样,每个标记在输入中都有一个位置,如果解析器需要将位置与语法特征相关联,它需要能够引用任何给定标记的位置。

Bison 通过允许扫描器填写 location object 以及语义值 (yylval) 来促进此过程。 location 对象称为 yylloc ,与语义值不同,它通常是每个标记的相同类型。如果您的 Bison 源使用位置,它将创建一个与语义值堆栈同步的位置对象堆栈。在规则中,token(或非终结符)的语义值可以表示为</code>、<code>等;同样,token/non-terminal 的位置将是 @1@2、...

您不需要告诉 Bison 收集位置信息。如果您在任何解析器操作中简单地使用一些位置引用 (@<i>n</i>),它将自动发生。

事实上,您不需要在解析器中做很多事情就可以使用位置信息,因为默认值通常就足够了。除非你 #define 预处理器宏 YYLTYPE,一个名为 YYLTYPE 的位置对象类型将声明如下:

typedef struct YYLTYPE {
  int first_line;
  int first_column;
  int last_line;
  int last_column;
} YYLTYPE;

声明将被放入生成的头文件中,以便扫描器也可以使用它。 YYLTYPE 结构反映了一个标记——更重要的是,一个非终端——跨越源文件中一系列位置的事实。

非终端的位置结构也将由生成的解析器自动填充,尽管您可以通过在解析器操作中分配给 @$ 来自由修改它。默认是从 @1 中获取 first_linefirst_column 字段,从 @<i> 中获取 <code>last_linelast_column 字段n,其中 n 是右侧符号的数量。换句话说,当您减少产生式时,生成的位置将跨越表示产生式标记的所有源文本。

虽然yylloc同时包含行和列信息,但您不必使用列数据。将这些字段设置为 0 是最方便的,以防你想在你的解析器的某些更高版本中使用它们;您可以通过重新定义 YYLTYPE 来减少位置堆栈的开销,但是您还需要覆盖默认位置操作,因为它引用那些命名字段。

填写 yylloc 对象完全是您的扫描仪的责任,不幸的是,flex 对您帮助不大。如果您要求 (%option yylineno),Flex 将保持 yylineno,但它不会填充 yylloc,因此您需要自己执行此操作。幸运的是,flex 允许您定义 YY_USER_ACTION 宏。这个宏插入在每个 flex动作的开头,可以用来复制位置信息到yylloc.

作为一个简单的例子,如果 none 你的标记跨越超过一行(或者你不关心确实跨越超过一行的标记的起始行),你可以简单地把这在您的 flex 定义的序言中:

#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;

并使用

启用yylineno跟踪
%option yylineno

完成后,无需对 flex 或 bison 定义进行其他更改,您就可以编写如下操作:

assignment: IDENTIFIER '=' expression {
               printf("%s is defined at line %d\n", , @1.first_line);
            }

请注意,由于上述规则指的是 IDENTIFIER 标记的位置,因此 expression 使用了多少行并不重要。您可以使用 @$ 的默认设置来更精确:

assignment: IDENTIFIER '=' expression {
               printf("%s is defined in lines %d to %d\n",
                      , @$.first_line, @$.last_line);
            }