日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

lex yacc與C++編寫代碼解析字符串代碼示例

作者:蓉 更新時間: 2022-09-22 編程語言

目錄

  • lex/yacc&C++使用及學習記錄
    • 一、準備工作
    • 二 、編譯過程
    • 三、代碼編寫注意事項
    • 四、踩到的一些坑

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"  //表示生成的lexyacc的C代碼中所有yy開頭的函數和類都會變為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) 
//這里define后,規則段可以調用對象里的函數,調用方式為CUR_LEXER->函數名()

%}
// 定義數據類型
%union 
{
  int *_value;
  char *_op    ;
}

%{
int yylex(YYSTYPE *yyvalp, ...) // 可以在這里重寫yylex函數
{
  auto lexer = static_case<MyxxxLexer*>(_param);
  int res = lexer.yylex();  // 好像是這么寫的 如果有問題我后面再回來改改
}
// 這個函數的返回值是.l中返回的類型所對應的整型值
%}

%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
// 如果這里沒重新定義自己的xxxFlexLexer,會在類定義的地方報error: expected class-name before ‘{’ token的錯

一個編寫模板如下:

/*.h文件中的類和接口定義*/
class MyxxxLexer : public yyFlexLexer
{
  public:
    // Construct function
    MyxxxLexer(const char *data) {}
    virtual ~MyxxxLexer() {}
    // 一些yyFlexLexer中的虛函數,可以重寫以實現自己的功能
    virtual int  LexerInput(char* buf, int max_size);  // 讀取數據用于詞法語法分析
    virtual void LexerError(const char* msg);

    // 自己可以定義一些接口,在.y文件中可以實例化,以在規則段用于調用
  private:
    // 自己定義的數據
};

void processFuncLexer(const char *data, MyxxxLexer *funcLexer);
//自定義一個接口,目的是告訴編譯器,調用這個函數的時候,開始對自己輸入的數據進行解析
/*.cpp文件中的實現*/
extern int xxxparse(void*);    // 這里的外部調用函數,叫xxxparse,是因為之前prefix定義為了xxx
void processFuncLexer(const char *func, MyxxxLexer *funcLexer)
{  xxxparse(funcLexer); }

int YFuncLexer::LexerInput(char* buf, int max_size)
{
  // char _str是自己定義的數據類型,這個函數的目的就是把數據放到buf中,傳給解析器
  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會報相關的沖突錯誤,這時需要自己尋找哪里寫得不合理,避免規則之間有沖突。

原文鏈接:https://blog.csdn.net/Rong_11/article/details/126979975

欄目分類
最近更新