【问题标题】:How i can disable maximal munch rule in Lex?如何在 Lex 中禁用最大咀嚼规则?
【发布时间】:2014-12-30 11:37:18
【问题描述】:

假设我想处理某些模式并在输出文件中保留其他文本(VHDL 代码)。

为此,我需要在最后编写一个主规则

(MY_PATTERN){
// do something with my pattern
}

(.*){
return TOK_VHDL_CODE;

}

这个策略的问题是 MY_PATTERN 在这种情况下是无用的,并且将与 .* 通过最大咀嚼规则匹配。

那么我怎样才能得到这个功能呢?

【问题讨论】:

    标签: c regex compiler-construction lex


    【解决方案1】:

    简单的方法是在最后去掉默认规则中的*,然后使用

    .    { append_to_buffer(*yytext); }
    

    因此,您的默认规则会获取所有与先前规则不匹配的内容,并将其塞入缓冲区以供其他人处理。

    【讨论】:

    • 但是 Chris,你不认为在这种情况下解析会太慢,因为它必须将每个字母都视为有效标记并将其传递给语法进一步处理全文?
    • 我尝试逐字填充文本,我的意思是通过检测空格。但是当两个有效标记没有被空格分隔时它会失败(这可能是因为输入即将到来从用户端)?
    【解决方案2】:

    理论上,可以找到匹配不包含模式的字符串的正则表达式,但除了非常简单的模式外,它既不容易也不易读。

    如果您只想搜索(并响应)特定模式,您可以使用默认规则,匹配一个字符并且什么都不做:

    {Pattern1}   { /* Do something with the pattern */ }
    {Pattern2}   { /* Do something with the pattern */ }
    .|\n         /* Default rule does nothing */
    

    另一方面,如果您想对不匹配的字符串做一些事情(如您的示例中所示),则需要使用默认规则来累积字符串,并使用模式规则“发送” (返回)在对它们匹配的令牌进行操作之前累积的令牌。这意味着某些操作需要发送两个令牌,这对于标准的parser calls scanner for a token 架构来说有点尴尬,因为它需要扫描器保持某种状态。

    如果您有一个不太古老的bison 版本,您可以改用“推送解析器”,它允许扫描器调用解析器。这使得在单个操作中发送两个令牌变得容易。否则,您需要在扫描仪中构建一种状态机。

    下面是一个使用推送解析器的简单示例(其中需要模式定义等)。

    %{
      #include <stdlib.h>
      #include <string.h>
      #include "parser.tab.h"
      /* Since the lexer calls the parser and we call the lexer,
       * we pass through a parser (state) to the lexer. This is
       * how you change the `yylex` prototype:
       */
      #define YY_DECL static int yylex(yypstate* parser)
    %}
    
    pattern1   ...
    pattern2   ...
    
    /* Standard "avoid warnings" options */
    %option noyywrap noinput nounput nodefault
    
    %%
      /* Indented code before the first pattern is inserted at the beginning
       * of yylex, perfect for local variables.
       */
      size_t vhdl_length = 0;
      /* These are macros because they do out-of-sequence return on error. */
      /* If you don't like macros, please accept my apologies for the offense. */
      #define SEND_(toke, leng) do { \
        size_t leng_ = leng; \
        char* text = memmove(malloc(leng_ + 1), yytext, leng_); \
        text[leng_] = 0; \
        int status = yypush_parse(parser, toke, &text); \
        if (status != YYPUSH_MORE) return status; \
      } while(0);
      #define SEND_TOKEN(toke) SEND_(toke, yyleng)
      #define SEND_TEXT do if(vhdl_length){ \
        SEND_(TEXT, vhdl_length); \
        yytext += vhdl_length; yyleng -= vhdl_length; vhdl_length = 0; \
      } while(0);
    
    {pattern1}   { SEND_TEXT; SEND_TOKEN(TOK_1); }
    {pattern2}   { SEND_TEXT; SEND_TOKEN(TOK_2); }
      /* Default action just registers that we have one more char 
       * calls yymore() to keep accumulating the token.
       */
    .|\n      { ++vhdl_length; yymore(); }
      /* In the push model, we're responsible for sending EOF to the parser */
    <<EOF>>   { SEND_TEXT; return yypush_parse(parser, 0, 0); }
    
    %%
    
    /* In this model, the lexer drives everything, so we provide the
     * top-level interface here.
     */
    
    int parse_vhdl(FILE* in) {
      yyin = in;
      /* Create a new pure push parser */
      yypstate* parser = yypstate_new();
      int status = yylex(parser);
      yypstate_delete(parser);
      return status;
    }
    

    要真正让它与野牛一起工作,您需要提供几个额外的选项:

    parser.y

    %code requires {
      /* requires blocks get copied into the tab.h file */
      /* Don't do this if you prefer a %union declaration, of course */
      #define YYSTYPE char*
    }
    %code {
      #include <stdio.h>
      void yyerror(const char* msg) { fprintf(stderr, "%s\n", msg); }
    }
    
    %define api.pure full
    %define api.push-pull push
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-01
      • 2022-01-21
      相关资源
      最近更新 更多