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

Bison与Flex集成时出现‘unknown type name ASTNode’错误求助

解决Bison/Flex项目中ASTNode类型未识别的编译错误

问题重现

编译时出现如下错误:

In file included from lexer.l:5:0:
parser.y:39:5: error: unknown type name 'ASTNode'
    ASTNode *astNode;  // Add ASTNode pointer for expression and other nodes

相关代码

parser.y

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

extern FILE *yyin;
extern FILE *yyout;

void yyerror(const char *s);

// Define NodeType enumeration for AST node types
typedef enum { NODE_VAR, NODE_ICONST, NODE_ADD, NODE_SUB, NODE_MUL, NODE_DIV } NodeType;

// Define the structure for AST nodes
typedef struct ASTNode {
    NodeType type;
    union {
        char *sval; // for variables
        int ival;       // for integer constants
        struct {
            struct ASTNode *left;
            struct ASTNode *right;
        } binary;       // for binary operations
    } data;
} ASTNode;

// Function prototypes for creating AST nodes
ASTNode* createVarNode(char *name);
ASTNode* createIntNode(int value);
ASTNode* createBinaryNode(NodeType type, ASTNode *left, ASTNode *right);
void printAST(ASTNode *node);
%}

// Union for semantic values
%union {
    int ival;
    char *sval;
    ASTNode *astNode; // Add ASTNode pointer for expression and other nodes
}

%token <sval> VAR
%token <ival> ICONST
%token EQUOP ADDOP SUBOP MULOP DIVOP LPAREN RPAREN

%type <astNode> program expression expression_suffix term term_suffix factor 

%start program

%%

// Rules of the grammar
program:
    VAR EQUOP expression {
        printf("Parsed program: %s = <expression>\n", $1);
        free($1); // Free the variable name after use
        $$ = $3; // Set the result of the program to the expression
    }
;

expression:
    term expression_suffix {
        printf("Parsed expression\n");
    }
;

expression_suffix:
    ADDOP term expression_suffix {
        printf("Parsed ADDOP\n");
    }
    | SUBOP term expression_suffix {
        printf("Parsed SUBOP\n");
    }
    | /* empty */ {
    }
;

term:
    factor term_suffix {
        $$ = $1; // Assign the factor to the term result
        printf("Parsed term\n");
    }
;

term_suffix:
    MULOP factor term_suffix {
        printf("Parsed MULOP\n");
    }
    | DIVOP factor term_suffix {
        printf("Parsed DIVOP\n");
    }
    | /* empty */ {
    }
;

factor:
    LPAREN expression RPAREN {
        $$ = $2; // The result is the expression inside the parentheses
        printf("Parsed factor: (expression)\n");
        printAST($$); // Print the AST for the expression inside the parentheses
    }
    | ICONST {
        $$ = createIntNode($1); // Create an integer node
        printf("Parsed ICONST: %d\n", $1);
    }
    | VAR {
        $$ = createVarNode($1); // Create a variable node
        printf("Parsed VAR: %s\n", $1);
    }
;

%% 

void yyerror(const char *s) {
    fprintf(stderr, "Error: %s\n", s);
}

// Function definitions for AST creation and printing
ASTNode* createVarNode(char *name) {
    ASTNode *node = malloc(sizeof(ASTNode));
    node->type = NODE_VAR;
    node->data.sval = strdup(name);
    return node;
}

ASTNode* createIntNode(int value) {
    ASTNode *node = malloc(sizeof(ASTNode));
    node->type = NODE_ICONST;
    node->data.ival = value;
    return node;
}

ASTNode* createBinaryNode(NodeType type, ASTNode *left, ASTNode *right) {
    ASTNode *node = malloc(sizeof(ASTNode));
    node->type = type;
    node->data.binary.left = left;
    node->data.binary.right = right;
    return node;
}

void printAST(ASTNode *node) {
    if (!node) return;
    switch (node->type) {
        case NODE_VAR:
            printf("VAR(%s)\n", node->data.sval);
            break;
        case NODE_ICONST:
            printf("ICONST(%d)\n", node->data.ival);
            break;
        case NODE_ADD:
            printf("ADD\n");
            printAST(node->data.binary.left);
            printAST(node->data.binary.right);
            break;
        case NODE_SUB:
            printf("SUB\n");
            printAST(node->data.binary.left);
            printAST(node->data.binary.right);
            break;
        case NODE_MUL:
            printf("MUL\n");
            printAST(node->data.binary.left);
            printAST(node->data.binary.right);
            break;
        case NODE_DIV:
            printf("DIV\n");
            printAST(node->data.binary.left);
            printAST(node->data.binary.right);
            break;
    }
}

int main() {
    yyin = fopen("input.txt", "r");
    if (!yyin) {
        perror("Failed to open input file");
        return 1;
    }

    yyout = fopen("lexicalOutput.txt", "w");
    if (!yyout) {
        perror("Failed to open output file");
        fclose(yyin);
        return 1;
    }

    yyparse();

    fclose(yyin);
    fclose(yyout);

    return 0; // Add return statement at the end
}

lexer.l

%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "parser.tab.h"

extern int yylex();
%}

/* Lexical analyzer rules */
%% 
[ \t]+ ; /* Ignore whitespace */
[0-9]+  { 
    yylval.ival = atoi(yytext); 
    fprintf(yyout, "Read ICONST: %d\n", yylval.ival);
    return ICONST; 
}

[a-zA-Z_][a-zA-Z0-9_]* { 
    yylval.sval = strdup(yytext); 
    fprintf(yyout, "Read VAR: %s\n", yylval.sval);
    return VAR; 
}

"=" { fprintf(yyout, "Read =\n"); return EQUOP; }
"+" { fprintf(yyout, "Read +\n"); return ADDOP; }
"-" { fprintf(yyout, "Read -\n"); return SUBOP; }
"*" { fprintf(yyout, "Read *\n"); return MULOP; }
"/" { fprintf(yyout, "Read /\n"); return DIVOP; }
"(" { fprintf(yyout, "Read (\n"); return LPAREN; }
")" { fprintf(yyout, "Read )\n"); return RPAREN; }

.   { /* Ignore unrecognized characters */ }
%%

int yywrap(void) {
    return 1;
}

编译命令

$ bison -d parser.y  
$ flex lexer.l
$ gcc -o myparser parser.tab.c lex.yy.c

错误原因

使用bison -d parser.y生成的parser.tab.h头文件,仅包含Bison自动生成的符号(如%union、%token、%type的声明),但你在parser.y的%{ ... %}块中定义的ASTNode结构体和NodeType枚举不会被写入该头文件。

而lexer.l包含了parser.tab.h,Flex生成的lex.yy.c中会使用yylval(属于%union类型),其中的astNode字段是ASTNode*类型,但此时ASTNode的定义并未在parser.tab.h中,导致编译时无法识别该类型。

解决方案

方案一:将AST类型定义移至独立头文件

  1. 创建ast.h文件,写入AST相关的类型定义和函数原型:
#ifndef AST_H
#define AST_H

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

typedef enum { NODE_VAR, NODE_ICONST, NODE_ADD, NODE_SUB, NODE_MUL, NODE_DIV } NodeType;

typedef struct ASTNode {
    NodeType type;
    union {
        char *sval;
        int ival;
        struct {
            struct ASTNode *left;
            struct ASTNode *right;
        } binary;
    } data;
} ASTNode;

ASTNode* createVarNode(char *name);
ASTNode* createIntNode(int value);
ASTNode* createBinaryNode(NodeType type, ASTNode *left, ASTNode *right);
void printAST(ASTNode *node);

#endif
  1. 修改parser.y的%{ ... %}块,替换原有的AST定义为包含ast.h:
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ast.h"

extern FILE *yyin;
extern FILE *yyout;

void yyerror(const char *s);
%}
  1. 修改lexer.l,在包含parser.tab.h前先包含ast.h:
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ast.h"
#include "parser.tab.h"

extern int yylex();
%}
  1. 重新执行编译命令即可。

方案二:使用Bison的%code requires块(推荐)

Bison的%code requires块中的代码会被自动包含到生成的parser.tab.h中,确保所有依赖该头文件的代码都能访问到AST类型定义:

  1. 修改parser.y,将AST类型定义移至%code requires块,原%{ ... %}块保留其他内容:
%code requires {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef enum { NODE_VAR, NODE_ICONST, NODE_ADD, NODE_SUB, NODE_MUL, NODE_DIV } NodeType;

typedef struct ASTNode {
    NodeType type;
    union {
        char *sval;
        int ival;
        struct {
            struct ASTNode *left;
            struct ASTNode *right;
        } binary;
    } data;
} ASTNode;

ASTNode* createVarNode(char *name);
ASTNode* createIntNode(int value);
ASTNode* createBinaryNode(NodeType type, ASTNode *left, ASTNode *right);
void printAST(ASTNode *node);
}

%{
extern FILE *yyin;
extern FILE *yyout;

void yyerror(const char *s);
%}
  1. lexer.l无需修改,直接重新执行编译命令即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:07:33