Kam*_*łys 1 c bison flex-lexer
我写了一个简单的语法:
operations :
/* empty */
| operations operation ';'
| operations operation_id ';'
;
operation :
NUM operator NUM
{
printf("%d\n%d\n",$1, $3);
}
;
operation_id :
WORD operator WORD
{
printf("%s\n%s\n%s\n",$1, $3, $<string>2);
}
;
operator :
'+' | '-' | '*' | '/'
{
$<string>$ = strdup(yytext);
}
;
Run Code Online (Sandbox Code Playgroud)
如您所见,我定义了operator可以识别4个符号之一的。现在,我要在中打印该符号operation_id。问题是,逻辑operator仅适用于替代中的最后一个符号。所以如果我写a / b; 它显示ab /,这很酷。但是对于其他操作,例如。a + b; 它打印aba。我究竟做错了什么?
*我在示例输出中省略了换行符号。
语法的这种非终结性是完全错误的。
operator :
'+' | '-' | '*' | '/' { $<string>$ = strdup(yytext); }
;
Run Code Online (Sandbox Code Playgroud)
首先,在yacc /野牛中,每个产品都有一个动作。该规则有四个产生,其中只有最后一个具有相关的动作。这样写起来会更清楚:
operator : '+'
| '-'
| '*'
| '/' { $<string>$ = strdup(yytext); }
;
Run Code Online (Sandbox Code Playgroud)
这使得该操作仅适用于令牌的减少更为明显'/'。
动作本身也不正确。yytext切勿在词法分析器操作之外使用它,因为它的值不可靠;它是最近执行词法分析器操作时的值,但是由于解析器通常(但不总是)先读取一个令牌,因此它通常(但不总是)是与下一个令牌关联的字符串。这就是为什么通常的建议是复制的原因yytext,但想法是将其复制到lexer规则中,然后将副本分配给的适当成员,yylval以便解析器可以使用令牌的语义值。
您应该避免使用$<type>$ =。非终结符只能具有一种类型,应在野牛文件的序言中声明:
%type <string> operator
Run Code Online (Sandbox Code Playgroud)
最后,您会发现拥有一个非终结符来识别不同的运算符的用处很少,因为不同的运算符在语法上是不同的。在更完整的表达语法中,您需要区分a + b * c,它是a与b和c的乘积之和,和a * b + c,是c与a和b的乘积之和。可以通过使用不同的非终端的和与积语法来完成,或者通过使用不同的制作用于表达非终端与优先级规则消除歧义,但在这两种情况下,你将不能够使用一个operator非终端,其产生+和*不加选择。
为了说明其价值,以下是为什么会a+b导致输出aba:
生产operator : '+'没有显式操作,因此最终使用默认操作$$ = $1。
但是,返回的词法分析器规则'+'(可能是我猜到的)永远不会设置yylval。因此yylval仍然具有上次分配的值。
大概是(另一个猜测),词法则规则可WORD正确生成集合yylval.string = strdup(yytext);。因此,'+'令牌的语义值是前一个WORD令牌的语义值,即指向字符串的指针"a"。
所以当规则
operation_id :
WORD operator WORD
{
printf("%s\n%s\n%s\n",$1, $3, $<string>2);
}
;
Run Code Online (Sandbox Code Playgroud)执行,$1并且$2都具有值"a"(指向同一字符串的两个指针),并且$3都具有值"b"。
显然,$2拥有值在语义上是不正确的"a",但是还有另一个错误等待发生。如前所述,您的解析器会泄漏内存,因为您从不会free()创建任何字符串strdup。这不是很令人满意,并且在某些时候您将需要修复这些操作,以便在不再需要语义值时将其释放。到那时,您将发现具有两个语义值指向已分配内存的同一块,这很有可能free()在同一内存块上被调用两次,这是未定义行为(并且可能会产生非常难以诊断的行为)错误)。