我正在用C ++编程,使用Flex lexer,我需要识别两个不同的标记,但两者都共享一些符号。我需要识别(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;
}
%%
我如何解决这个问题?
问候。
答案 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; }
我不禁想到,返回DIST
代替NOT
然后LPAREN
会使解析变得更复杂而不是更简单。
没有模式匹配换行符(至少在原始扫描程序中)。在OP中,这是[ \0\0]
,这对我来说似乎很奇怪;没有理由重复\0
,并且无论如何\0
几乎没有出现在输入中,而换行符很常见。我想你希望扫描在空白处终止;我希望[[:space:]] { return END; }
可以做到这一点。如果你在输入流中确实有一个NUL字符,我添加的默认规则将“正确”处理它。
此默认规则将字符代码作为任何其他不匹配字符的标记号返回(包括换行符,但请参阅上文。)如果LEFT_PAR
,RIGHT_PAR
和{{ 1}}分别具有值NOT
,'('
和')'
。如果您正在使用'!'
进行解析,则可以通过不使用命名标记来获得单字符标记来实现此结果;你可以把bison
放在制作中,甚至不要声明它是一个标记。
如果所有这些都不适合您的解析模型,只需使用原始规则。
使用'('
将{arith_piece}
匹配的标记(字母和数字序列,或单个算术运算符)添加到累积标记中。 (有关尾随上下文的解释,请参阅注释5.)我们无条件地切换到yymore()
开始条件,如果我们已经处于该开始条件,则不执行任何操作,以便我们可以在输出逻辑之前输出累积的标记运营商。 (见注6)。
<IN_ARITH>
将此标记为“危险的尾随上下文”,但它会正常工作;在这种情况下,您可以忽略警告消息。
此规则只有在以下规则不匹配时才能匹配,并且由于上一个规则的尾随上下文将匹配任何后续字符,因此该规则只有在输入结尾处匹配时才匹配。所以在这条规则的行动中,我们知道我们在EOF。这个复杂的游戏是必要的,因为flex
在yytext
规则中无效,即使之前的令牌与<<EOF>>
一起保留。
与yymore()
不匹配的任何内容 - 也就是说,{arith_piece}
启动条件中的模式可以匹配的任何内容 - 需要“发送”累积的令牌然后是处理就像在那个开始条件下一样。但是,如果没有推送解析器,则无法从单个扫描操作发送两个令牌,因此我们只需发送累积的算术字符串,然后切换到<INITIAL>
启动条件。我们使用<INITIAL>
来调整累积字符串的长度,有效地删除我们刚刚扫描的令牌;这将迫使它在yyless
开始条件下重新扫描,这将发送相应的令牌。
答案 1 :(得分:0)
[a-zA-Z0-9<>=]+ {
这真的是你想要的吗?通常,您不应在标识符中包含<
,>
或=
等特殊字符。为什么你没有像为(
等那样为他们定义具体规则?然后,Flex的最长匹配规则将在看到SYMBOL
时返回IFF
,SYMBOL
和a<->b
。
请同时检查the Flex manual以获取文件结束规则,以便将此规则替换为:
[ \0\0] {
return END;
}