Bison中未声明标识符的左到右错误顺序标记问题求助
问题描述
电路赋值示例:
A= B or C xor K.
需求:当标识符未声明时,按从左到右的出现顺序输出错误信息。为此,我给tIDENTIFIER和expression附加了属性,但目前只能按遇到顺序打印,还没实现存储功能。
以下是Bison代码片段:
circuitDesign : assignment | assignment circuitDesign ; assignment : tIDENTIFIER tASSIGNMENT expression {if(!isDeclared($1.val)){ $1.order=1;$3.order=1;fprintf(stderr,"%d\n",$1.order);}} ; expression : tNOT expression | tLPR expression tRPR | tIDENTIFIER {if(!isDeclared($1.val)){ $1.order = $$.order +1;$$.order=$1.order +1;fprintf(stderr,"%d\n",$1.order);}} | expression tAND expression | expression tOR expression | expression tXOR expression | tTRUE | tFALSE ;
当前每个错误输出都是1,我搞不懂为什么一直是1。在递增$1.order之前它始终是0,尽管我一开始赋值为1。考虑到这是自底向上解析器,我觉得顺序属性还不存在,但Bison应该支持继承属性吧?该怎么实现?或者有没有更好的方法实现按左到右顺序打印错误?
解决方案
问题根源
- 硬赋值重置了计数:你在
assignment规则里把$1.order直接设为1,每次遇到未声明的左值标识符都会重置计数,这是输出一直为1的直接原因。 - 属性未正确初始化:
expression规则里的$$.order没有初始值,$1.order = $$.order +1其实是在给一个随机值加1,逻辑自然混乱。 - 计数方式逻辑错误:你试图用每个标识符的
order属性维护全局顺序,但没有一个统一的计数器来跟踪当前编号。
正确实现思路
方法1:全局计数器(最简单直接)
不需要依赖Bison的属性传递,直接用全局变量跟踪标识符出现顺序,利用词法分析左到右扫描的特性:
// 全局计数器,初始化为0 int error_seq = 0; // 修改assignment规则 assignment : tIDENTIFIER tASSIGNMENT expression { if(!isDeclared($1.val)){ error_seq++; fprintf(stderr, "%d: 标识符 %s 未声明\n", error_seq, $1.val); } } ; // 修改expression中的tIDENTIFIER分支 expression : tIDENTIFIER { if(!isDeclared($1.val)){ error_seq++; fprintf(stderr, "%d: 标识符 %s 未声明\n", error_seq, $1.val); } } ;
这种方式完全符合输入的左到右顺序,实现简单且不易出错。
方法2:继承属性传递计数器(适合需要语法上下文的场景)
如果必须用Bison的属性机制,需要把计数器作为继承属性在语法规则间传递:
- 给所有非终结符添加
counter属性,用于传递当前顺序编号。 - 每个规则将继承属性传递给子节点,遇到未声明标识符时递增并输出。
示例代码片段:
// 定义属性联合体 %union { char* val; int counter; } %type <counter> circuitDesign assignment expression // 修改规则,传递计数器 circuitDesign : assignment { $$ = $1; } | assignment circuitDesign { $$ = $2; } ; assignment : tIDENTIFIER tASSIGNMENT expression[$1.counter] { int curr_counter = $3; if(!isDeclared($1.val)){ curr_counter++; fprintf(stderr, "%d: 标识符 %s 未声明\n", curr_counter, $1.val); } $$ = curr_counter; } ; // 给expression添加继承属性参数 expression[counter] : tNOT expression[counter] { $$ = $2; } | tLPR expression[counter] tRPR { $$ = $2; } | tIDENTIFIER { int curr = counter; if(!isDeclared($1.val)){ curr++; fprintf(stderr, "%d: 标识符 %s 未声明\n", curr, $1.val); } $$ = curr; } | expression[counter] tAND expression[$1] { $$ = $3; } | expression[counter] tOR expression[$1] { $$ = $3; } | expression[counter] tXOR expression[$1] { $$ = $3; } | tTRUE { $$ = counter; } | tFALSE { $$ = counter; } ;
这种方式通过继承属性传递计数器,保证顺序正确,但实现复杂度高于全局计数器,适合需要根据语法上下文调整计数的场景。
关键提示
- Bison的词法单元属性是在词法分析阶段生成的,你无法在语法规则里修改
tIDENTIFIER的属性并影响后续词法单元,因为这些单元已经被扫描存储。 - 全局计数器是当前需求的最优解,直接利用词法分析的左到右特性,逻辑清晰且易维护。
内容的提问来源于stack exchange,提问作者Metin
相关产品推荐
相关产品推荐

