You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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应该支持继承属性吧?该怎么实现?或者有没有更好的方法实现按左到右顺序打印错误?


解决方案

问题根源

  1. 硬赋值重置了计数:你在assignment规则里把$1.order直接设为1,每次遇到未声明的左值标识符都会重置计数,这是输出一直为1的直接原因。
  2. 属性未正确初始化:expression规则里的$$.order没有初始值,$1.order = $$.order +1其实是在给一个随机值加1,逻辑自然混乱。
  3. 计数方式逻辑错误:你试图用每个标识符的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的属性机制,需要把计数器作为继承属性在语法规则间传递:

  1. 给所有非终结符添加counter属性,用于传递当前顺序编号。
  2. 每个规则将继承属性传递给子节点,遇到未声明标识符时递增并输出。

示例代码片段:

// 定义属性联合体
%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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 14:22:15