免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 13745 | 回复: 9
打印 上一主题 下一主题

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

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-09-03 14:18 |只看该作者 |倒序浏览
利用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;
}

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

Parser.rar

1.67 KB, 下载次数: 361

论坛徽章:
0
2 [报告]
发表于 2007-09-03 14:43 |只看该作者
原创?

论坛徽章:
0
3 [报告]
发表于 2007-09-03 15:48 |只看该作者
yes.

论坛徽章:
0
4 [报告]
发表于 2007-09-03 15:51 |只看该作者
原帖由 converse 于 2007-9-3 14:43 发表
原创?

版主是不是想加精?

论坛徽章:
0
5 [报告]
发表于 2007-09-03 16:52 |只看该作者
一直想不通bison有%pure_parser选项支持可重入,而flex生成C代码却不去持
shit

论坛徽章:
0
6 [报告]
发表于 2008-01-31 14:03 |只看该作者

while((cmd = myl->yylex()) != 0 && (cmd2 = myl2->yylex()) != 0)
    {
        cout<< "cmd1 is:" << cmd << " cmd2 is:" << cmd2 << endl;
    }

这也能叫两个分析器同时工作? 楼主至少也应该创建两个线程才能说是可重入的.
并且两个分析器都要在死循环里工作才能说是同时在工作.

我刚刚入门在用pargen 2, 还是谢谢楼主的方法, 我试过了再来回复.

论坛徽章:
1
双子座
日期:2015-01-04 14:25:06
7 [报告]
发表于 2008-01-31 14:39 |只看该作者
flex & bison好像可以闭源使用
不过有限制
不知道楼主清楚不?

论坛徽章:
0
8 [报告]
发表于 2008-01-31 15:43 |只看该作者
原帖由 linux_prog 于 2007-9-3 14:18 发表
利用flex & bison (lex & yacc)创建可重入(线程安全)的词法分析和语法解析器
希望读者能有lex yacc基础。
来自:http://www.loveopensource.com/?p=29

    使用flex(lex)和bison(yacc)可以非常方便的创建词 ...


虽然我早就经使用它的C++版本实现了楼主所说的可重入,不过楼主的认真研究精神还是值得支持的.

论坛徽章:
0
9 [报告]
发表于 2008-11-14 23:35 |只看该作者

好像有一些问题

可以看看作者在bison部分的yylex接口

int yylex(YYSTYPE *lvalp, void *parm)
{
         myLexer *l = (myLexer *)parm;
         return l->yylex();
}
这个程序能够正常运行,仅仅是在l->yylex()里面不使用任何牵涉到任何yylval的东西,因为为了可重入在这里原来的yylval已经是lvalp指定了,而
l里面并没有任何保存lvalp的信息。

我感觉现在flex和bison在产生C++类层面上之后,交互已经相当少了,感觉貌似flex和bison要分道扬镳,看看yyFlexLexer产生接口
yyFlexLexer(istream *in,istream* out);
void yylex()
void switch_stream(istream *in,istream *out);
int yylex(istream *in,istream *out);
没有一项可以指定原来的YYSTYPE *yylval和YYLTYPE *yylloc这个和bison交互的对象

而看看bison产生类所使用的lex接口,parser里面直接调用parser::lex_()接口,而lex_()直接调用全局的yylex()
int yylex (semantic_value_type& yylval, location_type& yylloc, type1 arg1, ...)
还是使用全局的yylex???
个人感觉如果使用C++类来完成两者的结合基本上很困难。但是依然有办法可以创建可重入的,就是使用yylex中的%reentrant这个选项,使用了yyscan_t保存状态,里面可以设置yylval和yylloc这两个对象。

如果一定需要在类上面的接合,推荐使用ANTLR2.7.7版本,高版本3.x.x还没有出C++,但是出了Java版本

论坛徽章:
0
10 [报告]
发表于 2011-03-24 14:48 |只看该作者

好象编译不通过啊?

本帖最后由 shell_unixc 于 2011-03-24 14:53 编辑

[aaa@localhost test3]$ flex Command.l
[aaa@localhost test3]$ bison -d Command.y
[aaa@localhost test3]$ g++ -o do do.cpp lex.yy.cc Command.tab.c
do.cpp: In function 'int main(int, char**)':
do.cpp:21: error: 'yyparse' was not declared in this scope

要在do.cpp中增加申明:
extern int yyparse (void *YYPARSE_PARAM);
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP