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

如何用C++变体实现Flex与Bison对接并解决编译错误?

问题解决:Bison/Flex使用variant语义值时的头文件循环依赖错误

问题背景

将StackOverflow某高赞回答适配为使用variant而非std::string作为语义值,修改了以下内容:

  • 在fooParser.yy中,将%define api.value.type {std::string}改为%define api.value.type variant,并为HELLO、WORLD令牌指定<std::string>类型
  • 在FooLexer.hh中,将yylex的参数从std::string *const yylval改为yy::parser::value_type *const yylval

修改后出现两类编译错误:

  1. 未引入fooParser.tab.hh时,编译器提示‘yy’未声明
  2. 引入fooParser.tab.hh后,新增错误:
fooParser.tab.hh:666:21: error: expected ‘)’ before ‘&’ token
  666 |     parser (FooLexer &lexer_yyarg);
      |            ~        ^~
      |                     )
fooParser.tab.hh:1099:5: error: ‘FooLexer’ does not name a type
 1099 |     FooLexer &lexer;
      |     ^~~~~~~~

错误原因

这是头文件循环依赖导致的:

  • FooLexer.hh包含fooParser.tab.hh,而fooParser.tab.hh因%parse-param {FooLexer &lexer}需要FooLexer的定义,但此时FooLexer类还未被声明(因为FooLexer.hh先包含了fooParser.tab.hh)
  • 同时fooParser.yy的%code requires块中包含了FooLexer.hh,进一步加剧了循环依赖

解决步骤

  1. 解除头文件循环依赖:在FooLexer.hh中移除对fooParser.tab.hh的直接包含,改用前向声明yy::parser
  2. 调整Parser的前向声明:在fooParser.yy的%code requires块中仅保留FooLexer的前向声明,不要包含FooLexer.hh
  3. 在Lexer实现中引入Parser头文件:在fooLexer.ll的代码块中包含fooParser.tab.hh,确保编译时能解析yy::parser::value_type

修改后的完整代码

FooLexer.hh

#pragma once

#include <string>
#if ! defined(yyFlexLexerOnce)
#include <FlexLexer.h>
#endif

// 前向声明yy::parser,避免直接包含头文件
namespace yy {
    class parser;
}

class FooLexer : public yyFlexLexer
{
public:
    int yylex(yy::parser::value_type *const yylval);
};

fooParser.yy

%require "3.2"
%language "c++"

%code requires {
    #include <string>
    // 仅前向声明FooLexer,不要包含头文件
    class FooLexer;
}

%define api.value.type variant

%parse-param {FooLexer &lexer}

%header

%code {
    #define yylex lexer.yylex
    // 在parser的实现代码中包含FooLexer头文件
    #include "FooLexer.hh"
}

%token <std::string> HELLO
%token <std::string> WORLD

%%

hello_world: HELLO WORLD '!' { std::cout << "Goodbye " << $WORLD << '!' << std::endl; };

%%

void yy::parser::error(const std::string &message)
{
    std::cerr << "Error: " << message << std::endl;
}

fooLexer.ll

%{
    #include "FooLexer.hh"
    #include "fooParser.tab.hh"

    #undef  YY_DECL
    #define YY_DECL int FooLexer::yylex(yy::parser::value_type *const yylval)
%}

%option c++ noyywrap

%option yyclass="FooLexer"

%%

[[:space:]] ;
Hello { return yy::parser::token::HELLO; }
[[:alpha:]]+ { *yylval = std::string(yytext, yytext + yyleng); return yy::parser::token::WORLD; }
. { return yytext[0]; }

main.cc

#include "FooLexer.hh"
#include "fooParser.tab.hh"

int main()
{
    FooLexer lexer;
    yy::parser parser(lexer);
    return parser();
}

Makefile

.RECIPEPREFIX = >

prog: main.o fooParser.tab.o lex.yy.o
> g++ $^ -o $@

main.o: main.cc FooLexer.hh fooParser.tab.hh
> g++ -c $< -o $@

lex.yy.o: lex.yy.cc FooLexer.hh fooParser.tab.hh
> g++ -c $< -o $@

fooParser.tab.o: fooParser.tab.cc FooLexer.hh
> g++ -c $< -o $@

lex.yy.cc: fooLexer.ll
> flex $<

fooParser.tab.hh fooParser.tab.cc fooParser.output: fooParser.yy
> bison $<

.PHONY: clean
clean:
> rm -f prog main.o lex.* fooParser.tab.* stack.hh

验证编译

执行make命令即可正常编译生成可执行文件,测试输入Hello World!会输出Goodbye World!

内容的提问来源于stack exchange,提问作者Jakob E.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 18:43:09