您的位置:首页 > 其它

用flex & bison (lex & yacc)创建可重入(线程安全)的词法分析和语法解析器

2011-08-23 14:39 357 查看
使用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;

}

有了可重入和参数传递基础,我们就可以很方便的设计自己的线程安全词法和语法分析程序,所有的信息可以通过传递参数搞定。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: