flex详解

来源:互联网 发布:金融专业书籍知乎 编辑:程序博客网 时间:2024/05/19 07:09

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

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 如果银行卡里的钱突然没有了怎么办 微信绑定的银行卡怎么没有了怎么办 西亚超市商品退货赠品不退怎么办 淘宝发布宝贝推荐橱窗位不够怎么办 抖音和微信软件冲突怎么办 淘宝开过店想给别家做客服怎么办 学网上开店别人不主动教你怎么办 淘宝店铺身份信息复核逾期了怎么办 一件代发进货后如果没卖出去怎么办 国外供货商收了钱不发货怎么办 企业被注销之后淘宝企业店怎么办 在淘宝上买了东西店家关门了怎么办 在淘宝上买东西被店家骂了怎么办 淘宝店家错发了两次货怎么办 我的身份证注销了淘宝支付宝怎么办 买了空调坏了店家不管怎么办 支付宝充话费没到账也不退款怎么办 淘宝上店家关铺无法赔偿怎么办 未发货申请退款卖家拒绝怎么办 淘宝订单未发货买家恶意退款怎么办 淘宝被限制购买还有没退款的怎么办 复制粘贴了打字在复制下页怎么办? 得了抑郁症没办法还贷款了怎么办 淘宝东西退了商家不退钱怎么办 京东买东西付款后显示未付款怎么办 不能吃小龙虾的人吃了怎么办 闲鱼退货卖家收到货不退款怎么办 没收到货申请退款卖家不同意怎么办 咸鱼买家不确认收货老退款怎么办 买家买了确认收货然后退款怎么办 买家收确认收货后还申请退款怎么办 淘宝收到货后买家故意说少货怎么办 对面商铺放很大声音乐怎么办 在店面住隔壁商铺一直放音乐怎么办 淘宝卖家把买家评论删了怎么办 淘宝店铺上当顾客说再看看怎么办 车辆摇号申请说手机号注册怎么办 如果在微商手上买到假东西怎么办? 登陆微信显示版本过低该怎么办 苹果手机登陆微信版本过低怎么办 微信版本过低无法登怎么办录