【问题标题】:Ambiguity with regular expressions to recognize tokens使用正则表达式识别标记的歧义
【发布时间】:2015-03-23 13:23:14
【问题描述】:

我使用 C++ 编程,使用 Flex 词法分析器,我需要识别两个不同的标记,但它们都共享一些符号。我需要识别(a<b+c*4)类型的表达式,其次我需要识别逻辑运算<->。如果我输入a <-> b (a iff b),词法分析器会将其视为a<->b,但我想得到a<->b

//REGEX FOR MATH EXPRESSIONS.
[a-zA-Z0-9<>=]+
//REGEX FOR THE <-> LOGIC OPERATOR
"<->"

这是我的弹性代码:

%option noyywrap
%{
    #include <iostream>
    #include "parser.tab.c"
    using namespace std;
%}

%%

[a-zA-Z0-9<>=]+  {
    yylval = strdup(yytext);
    return SYMBOL;
}

"&&" {
    return AND;
}

"||" {
    return OR;
}

"!" {
    return NOT;
}

"!(" {
    return DIST;
}

[ \0\0] {
    return END;
}

"("     {
    return LEFT_PAR;
}

")"     {
    return RIGHT_PAR;
}

"->"    {
    return THEN;
}

"<->"   {
    return IFF;
}

%%

我该如何解决这个问题?

您好。

【问题讨论】:

    标签: c++ regex token flex-lexer


    【解决方案1】:

    我想只解析表达式语法的一部分似乎有点奇怪。尝试在扫描仪中识别表达式确实不合适,尽管它肯定可以做到。就个人而言,我只是将其留给解析器来处理表达式的算术部分(也许通过将标记重新组合成一个字符串),特别是因为无论如何它都需要进行干预以处理括号。

    尽管如此,可以通过使用yymore() 来累积令牌,或者通过自己在字符串累加器中累积令牌来强制 flex 完成这项工作。无论哪种方式,当您找到其他标记(例如,您的逻辑运算符之一)时,您最终将不得不“发送”累积的表达式;如果您使用推送解析器(绝对是我的偏好),这会容易得多,但可以使用开始条件来完成。

    为了不需要字符串累加器对象或bison 的特定版本(并且不知道您是如何处理令牌的),这里有一个基于开始条件的解决方案,它使用 flex 来累积令牌:

    %x IN_ARITH
    arith_piece     [[:alnum:]]+|[-+<>=]
    %%
                                int symbol_leng = 0;
    
    "&&"                      { return AND;                    }
    "||"                      { return OR;                     }
    "->"                      { return THEN;                   }
    "<->"                     { return IFF;                    }
    "!("                      { return DIST;      /* Note 1 */ }
    " "                       { return END;       /* Note 2 */ }
    .|\n                      { return yytext[0]; /* Note 3 */ }
    
    <*>{arith_piece}          { BEGIN(INITIAL);   /* Note 5 */
                                yylval = strdup(yytext);
                                return SYMBOL;                 }
    
    <*>{arith_piece}/(.|\n)   { BEGIN(IN_ARITH);  /* Note 4 */
                                symbol_leng = yyleng; 
                                yymore();                      }
    
    <IN_ARITH>"->"|"<->"|.|\n { BEGIN(INITIAL);   /* Note 6 */
                                yyless(symbol_leng);
                                yylval = strdup(yytext);
                                yylval[symbol_leng] = 0;
                                return SYMBOL;                 }
    

    注意事项

    1. 我不禁想到,返回 DIST 而不是 NOT 然后 LPAREN 会使解析更复杂而不是更简单。

    2. 没有模式匹配换行符(至少在原始扫描仪中)。在 OP 中,这是[ \0\0],这对我来说似乎很奇怪;没有理由重复\0,无论如何\0 几乎不会出现在输入中,而换行符很常见。我想您希望扫描在空白处终止;我会更喜欢[[:space:]] { return END; },它会做到这一点。万一您在输入流中确实有 NUL 字符,我添加的默认规则将“正确”处理它。

    3. 此默认规则返回字符代码作为任何其他不匹配字符的标记号(包括换行符,但见上文。)如果 LEFT_PARRIGHT_PARNOT 具有值 '(',这将完美运行、')''!'。如果您使用bison 进行解析,您可以通过完全不使用命名标记来实现单字符标记;您可以将'(' 放入生产中,甚至无需声明它是令牌。

      如果所有这些都不适合您的解析模型,请使用原始规则。

    4. {arith_piece} 匹配的标记(字母和数字序列,或单个算术运算符)只是使用yymore() 添加到累积标记中。 (有关尾随上下文的解释,请参见注释 5。)我们无条件地切换到 &lt;IN_ARITH&gt; 开始条件,如果我们已经处于该开始条件,它什么也不做,这样我们就可以在输出逻辑运算符之前输出累积的令牌。 (见注 6)。

      flex 将此标记为“危险的尾随上下文”,但它会正常工作;在这种情况下,您可以忽略警告消息。

    5. 这条规则只有在下一条不匹配的情况下才能匹配,并且由于前一条规则的尾随上下文将匹配任何后面的字符,所以这条规则只有在匹配正确的情况下才能匹配到输入的末尾.因此,在此规则的操作中,我们知道我们处于 EOF。这个复杂的游戏是必要的,因为yytext&lt;&lt;EOF&gt;&gt; 规则中无效,即使之前的令牌与yymore() 一起保留。

    6. 任何与{arith_piece} 不匹配的东西——即任何可以被&lt;INITIAL&gt; 开始条件中的模式匹配的东西——都需要“发送”累积的令牌,然后按原样处理处于那个开始状态。但是,如果没有推送解析器,就不可能从一次扫描操作中发送两个令牌,所以这里我们只发送累加的算术字符串,并切换到&lt;INITIAL&gt; 开始条件。我们使用yyless来调整累加字符串的长度,有效去除我们刚刚扫描的token;这将强制它在&lt;INITIAL&gt; 开始条件下重新扫描,这将发送相应的令牌。

    【讨论】:

      【解决方案2】:
      [a-zA-Z0-9<>=]+  {
      

      这真的是你想要的吗?通常你不应该在标识符中允许特殊字符,如&lt;&gt;=。为什么不像( 等那样为它们定义特定规则?那么Flex的最长匹配规则会在看到a&lt;-&gt;b时返回SYMBOLIFFSYMBOL

      还请检查 the Flex manual 以获取文件结束规则以将此规则替换为:

      [ \0\0] {
          return END;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-01
        • 2017-09-28
        • 1970-01-01
        • 1970-01-01
        • 2015-06-09
        • 1970-01-01
        • 2011-06-18
        • 1970-01-01
        相关资源
        最近更新 更多