使用正则表达式识别令牌的歧义

时间:2015-01-24 17:00:51

标签: c++ regex token flex-lexer

我正在用C ++编程,使用Flex lexer,我需要识别两个不同的标记,但两者都共享一些符号。我需要识别(a<b+c*4)类型的表达式,其次我需要识别逻辑运算的<->。如果我添加a <-> ba 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;
}

%%

我如何解决这个问题?

问候。

2 个答案:

答案 0 :(得分:2)

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

尽管如此,通过使用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_PAR和{{ 1}}分别具有值NOT'('')'。如果您正在使用'!'进行解析,则可以通过不使用命名标记来获得单字符标记来实现此结果;你可以把bison放在制作中,甚至不要声明它是一个标记。

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

  4. 使用'('{arith_piece}匹配的标记(字母和数字序列,或单个算术运算符)添加到累积标记中。 (有关尾随上下文的解释,请参阅注释5.)我们无条件地切换到yymore()开始条件,如果我们已经处于该开始条件,则不执行任何操作,以便我们可以在输出逻辑之前输出累积的标记运营商。 (见注6)。

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

  5. 此规则只有在以下规则不匹配时才能匹配,并且由于上一个规则的尾随上下文将匹配任何后续字符,因此该规则只有在输入结尾处匹配时才匹配。所以在这条规则的行动中,我们知道我们在EOF。这个复杂的游戏是必要的,因为flexyytext规则中无效,即使之前的令牌与<<EOF>>一起保留。

  6. yymore()不匹配的任何内容 - 也就是说,{arith_piece}启动条件中的模式可以匹配的任何内容 - 需要“发送”累积的令牌然后是处理就像在那个开始条件下一样。但是,如果没有推送解析器,则无法从单个扫描操作发送两个令牌,因此我们只需发送累积的算术字符串,然后切换到<INITIAL>启动条件。我们使用<INITIAL>来调整累积字符串的长度,有效地删除我们刚刚扫描的令牌;这将迫使它在yyless开始条件下重新扫描,这将发送相应的令牌。

答案 1 :(得分:0)

[a-zA-Z0-9<>=]+  {

这真的是你想要的吗?通常,您不应在标识符中包含<>=等特殊字符。为什么你没有像为(等那样为他们定义具体规则?然后,Flex的最长匹配规则将在看到SYMBOL时返回IFFSYMBOLa<->b

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

[ \0\0] {
    return END;
}