woaidongmao

文章均收录自他人博客,但不喜标题前加-[转贴],因其丑陋,见谅!~
随笔 - 1469, 文章 - 0, 评论 - 661, 引用 - 0
数据加载中……

flex详解

flex
 
此篇不会讲述规则表达式,自从.net流行之后,大量的原本只是在unix才使用的规则表达式现在广泛使用在各种系统中。略.

1.内置变量
  yy_create_buffer:
见后面的缓冲管理
  yy_delete_buffer:
见后面的缓冲管理
  yy_flex_debug:
见后面的缓冲管理
  yy_init_buffer:
见后面的缓冲管理
  yy_flush_buffer:
见后面的缓冲管理
  yy_load_buffer_state:
见后面的缓冲管理
  yy_switch_to_buffer:
见后面的缓冲管理
 
  yyin:
输入缓冲流的文件指针,可以被替换以实现解析某个自定义的文件
  yyleng:
当前匹配字串的长度
  yylex:
解析函数,接口
  yylineno:
当前匹配的文件行号
  yyout:  
输出流的指针
  yyrestart:
手动调用yyrestart.会重启解析
            yyrestart( yyin );
一般是打开某个文件之后,yyrestart(yyin)再解析.
  yytext
: 当前匹配的字串
  yywrap
: 解析一个文件完毕之后,会调用yywrap:返回1表示结束,0表示继续(此时最好重新打开yyin或者重置yyin)

2. 几个重要函数:
1). yymore(): yymore()
的含义是,当当前匹配的字串之后,想把后面配置的字串附加到这个字串后面,组成新的token返回.
 
比如:
            %%
          mega-    ECHO; yymore();
          kludge   ECHO;
 
如果:“mega-kludge" the following will write "mega-mega-kludge" to the output
 
为什么呢? 首先遇到 mega-,接着被more了一下,因此就会把kludga附加到mega-后面,而后面的kludge的动作又是打印,因此会打印出:mega-mega-kludge

2). yyless(): yyless()的含义是:当当前的匹配之后,我想只返回前面几个字符,并且把后面回退到输入
 
比如:
          %%
          foobar    ECHO; yyless(3);
          [a-z]+    ECHO;
        input "foobar" the following will write out     "foobarbar":
  
为什么呢? foobar输入之后,匹配foobar,ECHO打印出来,接着yyless(3),则输入流变为bar(yytextfoo).接着再匹配,于是匹配    [a-z]+,因此再次打印出bar.
  
3).BEGIN: flex
下一个起始解析状态。见第3节,flex的状态.

4).REJECT:  相当于拒绝此匹配,让系统重新找下一个匹配。
"abcd", it
     will write "abcdabcaba" to the output:

          %%
          a        |
          ab       |
          abc      |
          abcd     ECHO; REJECT;
          .|\n     /* eat up any unmatched character */  
        
5).unput(c):
c重新放到输入流。

6).input(): 读取输入流下一个字符

7).yyrestart(): 该函数迫使yylex重新解析。yyrestart有个函数指针流,可以再打开之后,重新使用yyrestart().


3.
解析源管理:
    1).
默认是从yyin获取,而yyin则是stdout,也可以是其它文件。
        if ( ! yyin )
         yyin = stdin;
       
        if ( ! yyout )
         yyout = stdout;
   
    2).
如果你打开了一个文件,并把yyin指向此文件,则从该文件中读取.比如:
   
main中:
     FILE* fp = NULL;
     fp = fopen("hell.txt", "r");
     yyin = fp;
    
之后再使用 yylex()
   
flexhell.txt中读取信息并解析.
   
    3).
从字符串中解析
       
先使用下列函数,转化缓冲,之后再使用 yylex()
       a. yy_scan_string(char*).
使用了yy_scan_string(char*)之后,flex会把char*放到yy的输入缓冲中(会调用到yy_switch_to_buffer.)
       b. yy_scan_bytes(const char *base, int len);
       c. yy_scan_buffer(char *base, yy_size_t size)
      
这几个函数内部都使用的是缓冲切换的创建等函数,见后面的章节.

    4).利用EOF内置规则,重新打开多个文件输入:  
   
比如:
     <<EOF>>  {
          if ( *++filelist )
              yyin = fopen( *filelist, "r" );
          else
             yyterminate();
          }
    5).
多缓冲问题:
        a.
此问题可以按上面的 <<EOF>>或者 yywrap解决。
        b.
另外一种形式,比如:#include <iostream>或者类似于这种,这种形式的话,则不能使用yywrap<<EOF>>来解决了。
          
这就需要用到在flex动作中手动切换缓冲。flex对每个缓冲有个缓冲输入流指针,指向当前位置,各个被切换的缓冲互不相干扰,这恰好很好地解决了文件包含另外一个文件,而子文件也许要yylex的这种场合.
          
这就需要使用到flex底层的缓冲管理了.见下节
   

4. flex的缓冲管理:
    flex
本质上都是对缓冲输入流进行yylex词法分析. 缓冲是个结构体,每个缓冲有个缓冲输入流指针,指向当前位置,各个被切换的缓冲互不相干扰,而相关yyin,yyrestart,yy_create_buffer,yy_scan_string系列函数都是操纵flex底层缓冲的.
    flex
缓冲是一个结构体:   
我们以下面的词法规则为例子:(来自flex官方网站的注解)
     /* the "incl" state is used for picking up the name
      * of an include file
      */
     %x incl
    
     %{
     #define MAX_INCLUDE_DEPTH 10
     YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
     int include_stack_ptr = 0;
     %}
    
     %%
     include             BEGIN(incl);
    
     [a-z]+              ECHO;
     [^a-z\n]*\n?        ECHO;
    
     <incl>[ \t]*      /* eat the whitespace */
     <incl>[^ \t\n]+   { /* got the include file name */
             if ( include_stack_ptr >= MAX_INCLUDE_DEPTH )
                 {
                 fprintf( stderr, "Includes nested too deeply" );
                 exit( 1 );
                 }
    
             include_stack[include_stack_ptr++] =
                 YY_CURRENT_BUFFER;
    
             yyin = fopen( yytext, "r" );
    
             if ( ! yyin )
                 error( ... );
    
             yy_switch_to_buffer(
                 yy_create_buffer( yyin, YY_BUF_SIZE ) );
    
             BEGIN(INITIAL);
             }
    
     <<EOF>> {
             if ( --include_stack_ptr < 0 )
                 {
                 yyterminate();
                 }
    
             else
                 {
                 yy_delete_buffer( YY_CURRENT_BUFFER );
                 yy_switch_to_buffer(
                      include_stack[include_stack_ptr] );
                 }
             }   
    YY_BUFFER_STATE
就是一个缓冲。该lex文法使用到了<incl>,这个是状态,见4节的flex的状态管理.目前只需要知道它是个状态即可.incl状态下才进行[ \t]*的规则匹配.
    <<EOF>>
见上面的描述,指某个输入流到了末尾则到了这个状态.

    BEGIN(INITIAL)类似于BEGIN(0),表示状态从开头解析(0表示不带状态的解析,也就说规则前没有<>这个标记的状态.


   
上面的文法可以知道:
    a.
在遇到include之后,跳到incl状态。
    b.
incl状态中,跳过空白的字符,得到文件名(include file),先把当前的flexBuffer保存到数组栈,然后打开新的文件,并把flex的当前输入流切换到刚打开的新文件的输入流.
    c.
切换到INITIAL状态(没有<>在规则前的默认的状态)
    d.
这里用到了几个宏或者函数: yy_switch_to_buffer, yy_create_bufferYY_CURRENT_BUFFERyy_delete_buffer.
   
这些函数看名字就应该知道其作用了。 
       
4. flex
的状态(Start conditions).
   
上面的缓冲管理已经涉及到状态管理了。flex的状态管理相当于普通词法的扩展。通过flex的状态,大大扩充了词法分析本身的功能。
   
比如:
     a.    <STRING>[^"]*        { /* eat up the string body ... */
                 ...
                 }
    
表示在STRING状态下才进行   [^"]*匹配。
     b.      <INITIAL,STRING,QUOTE>\.        { /* handle an escape ... */
                 ...
                 }
    
表示在INITIAL,STRING,QUOTE才匹配。\.  
     flex
的状态怎么使用呢?
     a.
首先定义:%开头(flex的申明本质上所有的都是以%开头)定义,2: %s,%x,其中%s = %x+INITIAL,也就说是%s的状态为%x定义的+INITIAL状态
     b.
在规则域中,使用<状态>规则,比如 comment是个状态,则有 <comment>.\,其中.\是个lex规则文法,comment则就是一个状态了.
     c.
有几个特殊内置的状态。INITIAL,*.比如: <*>规则,则表示这个规则在任何状态下有效. <INITIAL>是个默认状态。
     d.
某个规则可以支持多个状态,使用隔开。比如<INITIAL,STRING,QUOTE>规则.如果和<<EOF>>重用一个规则的话,则是<quote><<EOF>>
     e.flex
还提供了一套相关函数:
     yy_push_state, yy_pop_state, yy_top_state, BEGIN()    
    
可以说有了状态的支持,flex的功能更加强大了,简单的文法分析甚至可以不借助于yacc/bison来做了。
    
   
一个完整的例子:
     %x str
    
     %%
             char string_buf[MAX_STR_CONST];
             char *string_buf_ptr;
    
     \"      string_buf_ptr = string_buf; BEGIN(str);
    
     <str>\"        { /* saw closing quote - all done */
             BEGIN(INITIAL);
             *string_buf_ptr = '\0';
             /* return string constant token type and
              * value to parser
              */
             }
    
     <str>\n        {
             /* error - unterminated string constant */
             /* generate error message */
             }
    
     <str>\\[0-7]{1,3} {
             /* octal escape sequence */
             int result;
    
             (void) sscanf( yytext + 1, "%o", &result );
    
             if ( result > 0xff )
                     /* error, constant is out-of-bounds */
    
             *string_buf_ptr++ = result;
             }
    
     <str>\\[0-9]+ {
             /* generate error - bad escape sequence; something
              * like '\48' or '\0777777'
              */
             }
    
     <str>\\n  *string_buf_ptr++ = '\n';
     <str>\\t  *string_buf_ptr++ = '\t';
     <str>\\r  *string_buf_ptr++ = '\r';
     <str>\\b  *string_buf_ptr++ = '\b';
     <str>\\f  *string_buf_ptr++ = '\f';
    
     <str>\\(.|\n)  *string_buf_ptr++ = yytext[1];
    
     <str>[^\\\n\"]+        {
             char *yptr = yytext;
    
             while ( *yptr )
                     *string_buf_ptr++ = *yptr++;
             }
   
   
5. flex C++
的支持
   
编译时,使用flex -+ 文件,就可以得到.cc的文件,而且flex也会生成C++相关类,对应的类和方法有:
    FlexLexer:
成员方法有:
        a. yylex(), YYText(), YYLeng(),lineno(), set_debug(),debug(),
        b.
构造函数yyFlexLexer( istream* arg_yyin = 0, ostream* arg_yyout = 0 )
        c.
缓冲:switch_streams(istream* new_in = 0, ostream* new_out = 0),yylex( istream* new_in = 0, ostream* new_out = 0 )
       
       
等等。
  
例子:
         // An example of using the flex C++ scanner class.
    
     %{
     int mylineno = 0;
     %}
    
     string  \"[^\n"]+\"
    
     ws      [ \t]+
    
     alpha   [A-Za-z]
     dig     [0-9]
     name    ({alpha}|{dig}|\$)({alpha}|{dig}|[_.\-/$])*
     num1    [-+]?{dig}+\.?([eE][-+]?{dig}+)?
     num2    [-+]?{dig}*\.{dig}+([eE][-+]?{dig}+)?
     number  {num1}|{num2}
    
     %%
    
     {ws}    /* skip blanks and tabs */
    
     "/*"    {
             int c;
    
             while((c = yyinput()) != 0)
                 {
                 if(c == '\n')
                     ++mylineno;
    
                 else if(c == '*')
                     {
                     if((c = yyinput()) == '/')
                         break;
                     else
                         unput(c);
                     }
                 }
             }
    
     {number}  cout << "number " << YYText() << '\n';
    
     \n        mylineno++;
    
     {name}    cout << "name " << YYText() << '\n';
    
     {string}  cout << "string " << YYText() << '\n';
    
     %%
    
     Version 2.5               December 1994                      
    
     int main( int /* argc */, char** /* argv */ )
         {
         FlexLexer* lexer = new yyFlexLexer;
         while(lexer->yylex() != 0)
             ;
         return 0;
         }    

posted on 2008-11-23 00:54 肥仔 阅读(7547) 评论(2)  编辑 收藏 引用 所属分类: LEX & YACC

评论

# re: flex详解  回复  更多评论   

flex也就是帮你词法之后将剩下的事情全部丢给你了。因为“剩下的事情”用的是C/C++,所以从各种意义上来看flex不仅仅是type 3 grammar,但是这是借助其他工具完成的,譬如说C/C++。
2008-11-23 16:31 | 陈梓瀚(vczh)

# re: flex详解  回复  更多评论   

从各种意义上来看flex不仅仅是type 3 grammar,
http://www.uggeinkaufenboots.com/
2010-10-26 17:09 | ugg boots buy

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理