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

如何通过Bison规则将INTEGER信息传递至names规则的动作中?

这个问题的核心在于Bison的自底向上解析特性:你的names规则会在匹配完成后立刻执行动作,而此时INTEGER还没被解析(因为它在names之后),所以names的动作里无法直接访问INTEGER的值。要实现把每个ID和INTEGER一起打印的需求,有两种常用的解决方案,我分别给你详细说明:

方法1:调整规则顺序,通过参数传递INTEGER值(推荐)

如果你的语法允许调整INTEGER和names的顺序,这种方法最简洁直接。我们可以给names规则添加参数,把提前解析好的INTEGER值传递进去,让names的每个动作都能直接使用这个值。

示例代码

首先是词法分析器(flex代码):

%{
#include "parser.tab.h"
%}

%%
[a-zA-Z_][a-zA-Z0-9_]* { yylval.str = strdup(yytext); return ID; }
[0-9]+ { yylval.num = atoi(yytext); return INTEGER; }
[ \t\n] ; // 忽略空白字符
. { return yytext[0]; }
%%

然后是Bison解析器代码:

%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义语义值的联合体
typedef union {
    int num;
    char *str;
} YYSTYPE;
#define YYSTYPE_IS_DECLARED 1
%}

%token ID INTEGER
%type <num> INTEGER
%type <void> names(int)  // 声明names接受一个int类型参数,无返回值

%%

// 调整规则顺序:先解析INTEGER,再把它的值传给names
rule1: INTEGER names($1) { /* do something1 可以在这里做后续处理 */ }
    ;

// names规则使用传入的val参数和ID一起打印
names(int val): ID {
    // do something2:打印当前ID和对应的INTEGER值
    printf("ID: %s, INTEGER: %d\n", $1, val);
    free($1); // 释放词法分析器分配的内存
}
    | names(val) ID {
    // do something2:打印新匹配的ID和INTEGER值
    printf("ID: %s, INTEGER: %d\n", $2, val);
    free($2);
}
    ;

%%

int yyerror(const char *msg) {
    fprintf(stderr, "Error: %s\n", msg);
    return 1;
}

int main() {
    return yyparse();
}

原理说明

Bison支持给非终结符添加参数,我们先解析INTEGER得到它的语义值,再将这个值作为参数传递给names规则。这样names的每个分支动作都能直接使用这个参数,和ID一起输出。

方法2:收集ID列表,在rule1动作中统一打印(无需调整规则顺序)

如果必须保持names INTEGER的原始规则结构,我们可以让names规则收集所有匹配到的ID,把它们存储成一个链表(或动态数组),然后在rule1的动作里,遍历这个列表并和INTEGER值一起打印。

示例代码

Bison解析器代码:

%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义存储ID的链表节点结构
typedef struct Node {
    char *id;
    struct Node *next;
} Node;

typedef union {
    int num;
    char *str;
    Node *list;
} YYSTYPE;
#define YYSTYPE_IS_DECLARED 1
%}

%token ID INTEGER
%type <num> INTEGER
%type <list> names

%%

// 保持原始规则顺序:names在前,INTEGER在后
rule1: names INTEGER {
    // do something1:遍历names返回的ID列表,逐个和INTEGER值打印
    Node *current = $1;
    while (current != NULL) {
        printf("ID: %s, INTEGER: %d\n", current->id, $2);
        // 释放内存,避免泄漏
        Node *temp = current;
        current = current->next;
        free(temp->id);
        free(temp);
    }
}
    ;

// names规则负责收集所有ID,返回链表头指针
names: ID {
    // 创建新节点存储当前ID
    Node *node = malloc(sizeof(Node));
    node->id = $1;
    node->next = NULL;
    $$ = node; // 将节点作为names的语义值返回
}
    | names ID {
    // do something2:把新ID添加到链表末尾
    Node *current = $1;
    while (current->next != NULL) {
        current = current->next;
    }
    Node *node = malloc(sizeof(Node));
    node->id = $2;
    node->next = NULL;
    current->next = node;
    $$ = $1; // 返回整个链表的头指针
}
    ;

%%

int yyerror(const char *msg) {
    fprintf(stderr, "Error: %s\n", msg);
    return 1;
}

int main() {
    return yyparse();
}

原理说明

这种方法让names规则在匹配过程中把所有ID存储成链表,等到rule1匹配完成后,我们已经拿到了完整的ID列表和INTEGER值,此时就可以统一遍历打印了。缺点是需要手动管理链表的内存,代码稍复杂,但能保持原始的语法结构。

两种方法各有适用场景:如果你的语法允许调整顺序,方法1更简洁高效;如果必须保持names INTEGER的结构,方法2是更合适的选择。无论哪种方式,都要注意内存管理,避免泄漏。

内容的提问来源于stack exchange,提问作者Haloha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:09:57