现在的位置: 首页 > 综合 > 正文

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

2012年01月08日 ⁄ 综合 ⁄ 共 4234字 ⁄ 字号 评论关闭

 利用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;
}

   有了可重入和参数传递基础,我们就可以很方便的设计自己的线程安全词法和语法分析程序,所有的信息可以通过
传递参数搞定。

抱歉!评论已关闭.