lex/yacc&C++使用及学习记录
(写在前面的话)本文是笔者在工作项目中实际使用到的lex/yacc的记录,主要介绍的是项目中如何组织、编译以及遇到的一些问题。不会提到一些十分基础的知识。如果需要学习基础知识可以移步去看o’reilly的书。
一、准备工作
使用lex yacc和C++一起实现功能时,需要建立四个文件来进行调用,即.l、.y、.cpp和.h文件。
xxxLexer.cpp (MyxxxLexer中的函数具体实现,需要重载yyFlexLexer中的一些函数) xxxLexer.h (继承yyFlexLexer生成一个新的类MyxxxLexer) xxxLexer.l (lex 词法分析器实现) xxxParser.y (yacc语法分析器实现)
yyFlexLexer是继承自FlexLexer的一个类,其中有诸多接口,这个类会在.l和.y文件编译后存在与C代码中,这里贴一个之前看到的类文档页面,里面介绍了涉及到的一些接口,以作参考 casa: yyFlexLexer Class Reference (nrao.edu)
二 、编译过程
lex yacc需要生成一些C源代码(.cxx、.hxx)来使用,所以如果是在一个项目中进行编译,需要在CMakeLists中添加FLEX_TARGET和BISON_TARGET
FLEX_TARGET(xxxScanner xxxLexer.l ${CMAKE_CURRENT_BINARY_DIR}/xxxLexer.cxx)
BISON_TARGET(xxxParser xxxParser.y ${CMAKE_CURRENT_BINARY_DIR}/xxxParser.cxx
COMPILE_FLAGS -v DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/xxxPraser.hxx)
ADD_FLEX_BISON_DEPENDENCY(xxxScanner xxxParser) # 这里一定要把Scanner放在前面
三、代码编写注意事项
.l文件的编写中没太多需要注意的东西,唯一需要注意的是一些option
%option noyywrap
%option c++
%option prefix="xxx"
.y文件中 ①数据类型的定义,可以使用union来进行,也可以使用#define YYSTYPE type来进行,这个type可以是C的基本类型,也可以是自己定义的指针或自己定义的struct等,但是要在定义段声明或者指定命名空间才能在union中使用。如果没有定义的话,yacc中传递的数据默认为int。 ②在.y中可以重写yylex等函数,调用cpp中自己生成的类对象,以将lex词法分析获取的信息赋给自己定义的数据类型,从而能作为参数用于语法分析中。 ③在编写规则时,不同的两条规则直之间会有一些规约冲突,可能是编写的规则有重复的部分,需要对其进行寻找并修改。//定义段,引入头文件、数据类型等等定义
%{
#define yyFlexLexer xxxFlexLexer
#include <FlexLexer.h>
#include <string.h>
#include "xxxLexer.h"
#include "xxxParser.hxx"
#include <iostream>
#define CUR_LEXER static_cast<MyxxxLexer*>(_param)
%}
%union
{
int *_value;
char *_op ;
}
%{
int yylex(YYSTYPE *yyvalp, void *param)
{
auto lexer = static_case<MyxxxLexer*>(_param);
int res = lexer->yylex();
yyvalp->_op = strdup(lexer->YYText());
return res;
}
%}
%token <_value> VALUE
%token<_op> OP
%%
%%
.h与.cpp中 如果同一个目录下的代码已经有另一些cpp代码定义过,即#define yyFlexLexer zzzFlexLexer,需要在.h头文件中写以下代码以避免继承yyFlexLexer的报错,让代码知道自己继承的是哪个yyFlexLexer
#if !defined(yyFlexLexerOnce)
#undef yyFlexLexer
#define yyFlexLexer xxxFlexLexer
#include <FlexLexer.h>
#endif
一个编写模板如下:
class MyxxxLexer : public yyFlexLexer
{
public:
MyxxxLexer(const char *data) {}
virtual ~MyxxxLexer() {}
virtual int LexerInput(char* buf, int max_size);
virtual void LexerError(const char* msg);
private:
};
void processFuncLexer(const char *data, MyxxxLexer *funcLexer);
extern int xxxparse(void*);
void processFuncLexer(const char *func, MyxxxLexer *funcLexer)
{ xxxparse(funcLexer); }
int YFuncLexer::LexerInput(char* buf, int max_size)
{
size_t num = strlen(_str) + 1
memcpy(buf, _str, num);
return num;
}
四、踩到的一些坑
①编译报错 在项目其他目录的代码文件中使用MyxxxLexer这个类时,如果在Linking CXX executable时报错,提示信息是undefined reference to `yyFlexLexer::yyFlexLexer(std::istream*, std::ostream*)等等,此时要首先看自己的一些宏定义是不是写错了,其次才是考虑CMakeLists中链接的问题。 ②归约错误,即.y中规则段编写的注意事项,如果规则段没有编写好,出现了一些匹配冲突,yacc会报相关的冲突错误,这时需要自己寻找哪里写得不合理,避免规则之间有冲突。
|