我正在尝试使用flex和bison编写语法识别器,以确定输入字符串是否在L(G)中,其中语言是以下语言的并集:
L(G)= {a ^ i b ^ j c ^ k d ^ l e ^ m},其中i,j,k,l,m> 0且i = m和k = l
和
L(G)= {e ^ i d ^ j c ^ k b ^ l a ^ m}其中i,j,k,l,m> 0且i = 2m k = 3l和j = 2
现在我可以正常工作了,但是仅当在语言中使用标记时才可以。如果我包含任何其他令牌,它似乎会被忽略,并且测试会基于其他允许的令牌通过或失败。这是有问题的,因为即使“ f”不在语言中,它也允许诸如“ abcdef”之类的字符串通过解析。
我正在测试的错误输入是“ abcdef”。 “ abcde”部分是正确的,并且给出正确的输出,但是在末尾添加“ f”会导致yyerror(“ syntax error”)的语法错误消息,以及从main到“祝贺;解析成功”的打印语句打印。
使用“ fabcde”可以完成与上述相同的操作。它给了我错误,但也给了我成功的打印说明。我正在使用“ if(yyparse()== 0))”来打印main中的成功语句,并且我认为这可能是罪魁祸首,尽管当我将打印语句移至中时遇到了同样的问题。 y文件,只是在main中使用了yyparse()和return(1)。
这是我的.in文件(包括负号):
%%
a return A;
b return B;
c return C;
d return D;
e return E;
. yyerror("syntax error\n\nSorry, Charlie, input string not in L(G)\n"); /* working but still prints success message too */
%%
这是我的.y文件(包括减号):
%token A
%token B
%token C
%token D
%token E
%% /* Grammar Rules */
string: as bs cs ds es
{
if(($1 == $5) && ($3 == $4)) {
return(0);
}
else
{
return(-1);
}
}
;
string: es ds cs bs as
{
if(($1 == (2 * $5) && ($3 == (3 * $4)) && ($2 = 2)) {
return(0);
}
else
{
return(-1);
}
}
;
as: A as {$$ = $2 +1;}
;
as: A {$$ = 1;}
;
bs: B bs {$$ = $2 +1;}
;
bs: B {$$ = 1;}
;
cs: C cs {$$ = $2 +1;}
;
cs: C {$$ = 1;}
;
ds: D ds {$$ = $2 +1;}
;
ds: D {$$ = 1;}
;
es: E es {$$ = $2 +1;}
;
es: E {$$ = 1;}
;
%%
我的.c文件很简单,如果yyparse()== 0,则返回“祝贺;解析成功”,否则返回“输入字符串不在L(G)中”。
当输入字符串仅包含a,b,c,d和e时,一切工作都很好。我只需要弄清楚如果输入字符串中除了标记之外,没有任何成功说明的话,如何使解析器给出语法错误而不显示成功语句。
这是一张有助于显示我的问题的图片: The first two parses work as intended. The third one shows my issue.
答案 0 :(得分:1)
如果(f)lex规则未返回任何内容,则它匹配的标记将被忽略。这适用于注释,但不适用于您想出错的标记。如果您将全部弹性规则更改为
. return *yytext;
然后将返回输入中所有无法识别的字符(换行符除外,这是唯一.
不匹配的字符),并且很可能会导致解析器发出Syntax error
消息(以及一个yyparse返回失败。如果您的语法包含文字字符标记(例如'#'
以匹配该字符),则它当然会匹配。
答案 1 :(得分:0)
bison / yacc生成的解析器希望解析整个正确的输入,直到并包括输入结束标记,然后才返回成功指示(返回值为0)。
当然,如果输入在语法上不正确,则解析器可能会提前返回错误指示(语法错误的值始终为1,如果内存不足则为2)。在这种情况下,解析器返回之前,它将清理其内部状态并释放所有分配的内存。
让解析器执行此操作很重要。从bison / yacc解析器中的语义动作返回的消息充其量是不明智的(因为这几乎肯定是内存泄漏),并且还可能产生混乱,因为它可能会在产生错误消息后成功返回。
例如,考虑输入abcdea
的情况,它是一个有效的字符串,后跟一个无效的a
。 string
的语义动作很可能在解析器尝试处理最后一个a
之前之前运行,这是因为解析器表压缩(为了保存而推迟了错误操作)表条目)。但是您的语义动作实际上返回0,从而绕过了解析器的错误报告和清理。如果输入为abcdef
,而您的扫描仪为无效令牌调用yyerror
(这也不是一个好主意),那么操作顺序将是:
string
语义操作,该操作返回0。再次,语义动作中的return
语句绕过了正确的错误处理和清理操作。
所以不要那样做。如果要报告语义操作中的错误,请使用YYABORT
,它将干净地终止解析并返回错误。另一方面,如果您的最高产量是正确的,则什么也不做。然后,解析器将验证下一个输入令牌是否为输入结束标记,并返回成功。