利用flex & bison (lex & yacc)創建可重入(線程安全)的詞法分析和語法解析器
希望讀者能有lex yacc基礎。
來自:http://www.loveopensource.com/?p=29
使用flex(lex)和bison(yacc)可以非常方便的創建詞法分析和語法分析器,典型的這類程序都是
使用一些全局變量進行信息的傳遞,這也是程序默認的方式,比如:flex解析到一個string,可以通過
yylval傳遞給bison;再就是flex和bison默認是通過一些全局變量來保存解析的status信息。簡單點說,
flex和bison默認創建的詞法分析和語法分析的C代碼都不是線程安全的,都是不可重入的。
隨着多線程編碼在linux上的廣泛使用,我們對線程安全的詞法分析和語法分析的需求也越來越強烈。
本節討論一下實施方法。
(1) flex的可重入實施
使用flex創建C++的詞法解析器時,默認就是可重入的。
需求加入:
%option c++
flex test.lex時,會自動生成lex.yy.cc文件。
root@horde1:~/test/Reentrant Parser#locate FlexLexer.h
/usr/include/FlexLexer.h
可以查看一下FlexLexer.h
裏面定義了2個類:FlexLexer和yyFlexLexer
說明見:
http://dinosaur.compilertools.net/flex/flex_19.html#SEC19
範例:
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
%}
%option yylineno
%option noyywrap
%option c++
%%
insert { return CMD_INSERT; }
delete { return CMD_DELETE; }
update { return CMD_UPDATE; }
select { return CMD_SELECT; }
[ /n/t/r]+ { ; }
. { return CMD_ERROR; }
%%
我自己做了集成,把要解析的命令傳給自己的class:
#ifndef _DO_H_
#define _DO_H_
#include <iostream>
#include <string>
#include <FlexLexer.h>
using namespace std;
class myLexer: public yyFlexLexer
{
public:
// set parse command
myLexer(const char *sql)
{
snprintf(content, sizeof(content), sql);
parseP = content;
}
// flex通過調用這個方法取得要解析的字符串的
virtual int LexerInput(char* buf, int max_size)
{
if(parseP == NULL) return 0;
if(*parseP == '/0') return 0;
strncpy(buf, parseP, max_size - 1);
buf[max_size - 1] = '/0';
int len = strlen(buf);
parseP += len;
return len;
}
//錯誤函數
virtual void LexerError(const char* msg)
{
cout<<"error:"<< msg << " at line:" << lineno() <<endl;
}
virtual ~myLexer() {}
private:
char content[4096];
char *parseP;
};
#endif
調用方法:
const char *sql = "update insert select delete update insert select delete";
const char *sql2 = "delete select insert update delete select insert update";
myLexer *myl = new myLexer(sql);
myLexer *myl2 = new myLexer(sql2);
int cmd, cmd2;
while((cmd = myl->yylex()) != 0 && (cmd2 = myl2->yylex()) != 0)
{
cout<< "cmd1 is:" << cmd << " cmd2 is:" << cmd2 << endl;
}
delete myl;
delete myl2;
return 0;
}
用2個詞法解析器同時進行解析,發現沒有問題,說明是可重入的。
(2) bison的可重入實施
官方文檔有:http://dinosaur.compilertools.net/bison/bison_6.html#SEC56
注意點: 必須加入%pure_parser
表示創建可重入的語法解析程序。
這時候無法使用全局變量共享信息,必須通過參數傳遞。
#define YYPARSE_PARAM parm
#define YYLEX_PARAM parm
表示,會傳遞一個參數給yyparse()和yylex函數,因爲默認的yyparse和yylex是不接受參數的。
#define yyerror(msg) my_yyerror(msg, YYPARSE_PARAM) //把這個參數也傳遞給yyerror
貼出全部程序:
%{
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include "do.h"
using namespace std;
%}
%union
{
int cmd_type;
}
%{
#define YYPARSE_PARAM parm
#define YYLEX_PARAM parm
#define yyerror(msg) my_yyerror(msg, YYPARSE_PARAM)
int yylex(YYSTYPE *lvalp, void *parm)
{
myLexer *l = (myLexer *)parm;
return l->yylex();
}
int my_yyerror(char *s, void *parm)
{
myLexer *l = (myLexer *)parm;
cout<<"bison got error/n";
l->LexerError(s);
return 1;
}
%}
%token CMD_INSERT
%token CMD_DELETE
%token CMD_UPDATE
%token CMD_SELECT
%token CMD_ERROR
%pure_parser
%start sql
%%
sql:
|sql command;
command: CMD_INSERT { cout<<"meet a insert/n"; }
| CMD_DELETE { cout<<"meet a delete/n"; }
| CMD_UPDATE { cout<<"meet a update/n"; }
| CMD_SELECT { cout<<"meet a select/n"; }
%%
調用方法:
#include <string>
#include <FlexLexer.h>
#include "do.h"
using namespace std;
int main(int argc, char **argv)
{
const char *sql = "update insert select delete update insert select delete";
const char *sql2 = "delete select insert update delete select insert update";
myLexer *myl = new myLexer(sql);
//進行語法解析
yyparse((void *)myl);
delete myl;
return 0;
}
有了可重入和參數傳遞基礎,我們就可以很方便的設計自己的線程安全詞法和語法分析程序,所有的信息可以通過
傳遞參數搞定。
利用flex & bison (lex & yacc)創建可重入(線程安全)的詞法分析和語法解析器
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.