如何告诉 flex 和 bison 等待新的输入?
How to tell flex and bison to wait for new input?
我正在尝试一个示例来检查 flex
和 bison
中 for
循环的正确性。这是 flex
文件:
%{
#include "ch.tab.h"
extern int yylval;
%}
%%
for {return (FOR);}
"(" {return (OPBR);}
")" {return (CLBR);}
";" {return (SEMIC);}
"=" {return (EQU);}
"<"|">" {return (RELOP);}
"++" {return (INC);}
"--" {return (DEC);}
[a-zA-Z]+ {yylval=yytext[0];return(ID);}
[0-9]+ {yylval=atoi(yytext);return(NUM);}
%%
int yywrap()
{
return 1;
}
和bison
文件:
%{
#include <stdio.h>
int flag=0;
%}
%token FOR OPBR CLBR SEMIC RELOP EQU ID NUM RELOP INC DEC
%%
S:FOR OPBR E1 SEMIC E2 SEMIC E3 CLBR {printf("Accepted!");flag=1;}
;
E1: ID EQU ID
| ID EQU NUM
;
E2: ID RELOP ID
| ID RELOP NUM
;
E3: ID INC
| ID DEC
;
%%
int main()
{
return yyparse();
}
yyerror(const char *msg)
{
if(flag==0);
printf("Not Accepted!");
}
一切都很好运行唯一的第一次。当我 运行 一个例子 for(i = 0; i < 2; i++)
时,它第一次打印 Accepted
,当我再次 运行 相同的例子而不关闭 command prompt
我得到 Not Accepted!
.为什么?
有没有办法让 exe
只要我输入正确的语法,它就会一直说 Accepted
并等待另一个新输入?
因为您的语法只接受一个 for
语句,因此它必须是整个输入。
如果你希望能够接受多个语句,你需要你的语法来产生多个语句。所以你可以使用 non-terminal 比如:
input: /* Empty */ | input S;
这将接受任意数量(包括 0)的 S
s。 (当然,你需要它作为开始符号,所以你要么需要一个 %start input
指令,要么你需要将该产生式放在任何其他产生式之前。)
只要输入了有效的语句,它就会继续解析,但是当它无法解析语句时就会失败。您可能更喜欢生成一条错误消息,但在遇到不正确的语句时继续解析。一种简单的方法是使用错误生成,例如:
S: error SEMIC { yyerror("Invalid for statement"); }
它将通过丢弃标记直到找到分号来进行错误恢复,然后恢复正常解析。 (在该操作中,我假设您修改 yyerror
以打印作为参数传递的字符串,而不是忽略它。)
顺便说一下,正常的 bison 风格是使用大写字母表示标记 (FOR
),使用小写字母表示非终结符 (statement
),以及 single-character 文字表示 single-character 代币 (';'
).
要使 single-character 字面量与 flex 扫描仪一起使用,您需要 return 扫描仪操作中的字符而不是标记名称:
";" { return ';'; }
无需编写大量样板规则即可做到这一点的常用方法是在 flex 规则的末尾放置 catch-all 回退词法规则:
. { return yytext[0]; }
我正在尝试一个示例来检查 flex
和 bison
中 for
循环的正确性。这是 flex
文件:
%{
#include "ch.tab.h"
extern int yylval;
%}
%%
for {return (FOR);}
"(" {return (OPBR);}
")" {return (CLBR);}
";" {return (SEMIC);}
"=" {return (EQU);}
"<"|">" {return (RELOP);}
"++" {return (INC);}
"--" {return (DEC);}
[a-zA-Z]+ {yylval=yytext[0];return(ID);}
[0-9]+ {yylval=atoi(yytext);return(NUM);}
%%
int yywrap()
{
return 1;
}
和bison
文件:
%{
#include <stdio.h>
int flag=0;
%}
%token FOR OPBR CLBR SEMIC RELOP EQU ID NUM RELOP INC DEC
%%
S:FOR OPBR E1 SEMIC E2 SEMIC E3 CLBR {printf("Accepted!");flag=1;}
;
E1: ID EQU ID
| ID EQU NUM
;
E2: ID RELOP ID
| ID RELOP NUM
;
E3: ID INC
| ID DEC
;
%%
int main()
{
return yyparse();
}
yyerror(const char *msg)
{
if(flag==0);
printf("Not Accepted!");
}
一切都很好运行唯一的第一次。当我 运行 一个例子 for(i = 0; i < 2; i++)
时,它第一次打印 Accepted
,当我再次 运行 相同的例子而不关闭 command prompt
我得到 Not Accepted!
.为什么?
有没有办法让 exe
只要我输入正确的语法,它就会一直说 Accepted
并等待另一个新输入?
因为您的语法只接受一个 for
语句,因此它必须是整个输入。
如果你希望能够接受多个语句,你需要你的语法来产生多个语句。所以你可以使用 non-terminal 比如:
input: /* Empty */ | input S;
这将接受任意数量(包括 0)的 S
s。 (当然,你需要它作为开始符号,所以你要么需要一个 %start input
指令,要么你需要将该产生式放在任何其他产生式之前。)
只要输入了有效的语句,它就会继续解析,但是当它无法解析语句时就会失败。您可能更喜欢生成一条错误消息,但在遇到不正确的语句时继续解析。一种简单的方法是使用错误生成,例如:
S: error SEMIC { yyerror("Invalid for statement"); }
它将通过丢弃标记直到找到分号来进行错误恢复,然后恢复正常解析。 (在该操作中,我假设您修改 yyerror
以打印作为参数传递的字符串,而不是忽略它。)
顺便说一下,正常的 bison 风格是使用大写字母表示标记 (FOR
),使用小写字母表示非终结符 (statement
),以及 single-character 文字表示 single-character 代币 (';'
).
要使 single-character 字面量与 flex 扫描仪一起使用,您需要 return 扫描仪操作中的字符而不是标记名称:
";" { return ';'; }
无需编写大量样板规则即可做到这一点的常用方法是在 flex 规则的末尾放置 catch-all 回退词法规则:
. { return yytext[0]; }