flex-bison 忽略空格
flex-bison ignore whitespace
我正在尝试使用 flex-bison 实现一个简单的词法分析器和解析器。
我只想解析这些:
- 一个
- a,b
- a,b
- 一个,b
- a,b,c
- a,b, c
- .....
只是一个用逗号分隔的序列,可能包含也可能不包含 space。
所以这是我的语法:
KEY_SET : KEY
{
printf("keyset 1");
}
| KEY COMMA KEY_SET
{
printf("keyset 2");
};
声明KEY
,COMMA
为token
.//%token
但每当我按回车键或任何白色space。
时,它都会给我 语法错误
所以我什至在 flex 中声明了 IGNORE [ \t\n]
。
在解析器中我添加了一个新规则:
IGNORE_BLOCK : IGNORE
{
printf("\n...ignoring...\n")
};
但这甚至没有发挥作用。
它让我一直报语法错误。
我该如何解决?
词法分析器:
%{
#include "y.tab.h"
%}
%option noyywrap
COMMA [,]
KEY [[:alpha:][:alnum:]*]
IGNORE [ \t\n]
%%
{COMMA} {return COMMA;}
{KEY} {return KEY;}
{IGNORE} {return IGNORE;}
. {printf("Exiting...\n");exit(0);}
%%
解析器:
%{
#include<stdio.h>
void yyerror (char const *s);
int yywrap();
//int extern yylex();
%}
%token COMMA
%token KEY
%token IGNORE
%%
KEY_SET : KEY
{
printf("keyset 1");
}
| KEY COMMA KEY_SET
{
printf("keyset 2");
};
IGNORE_BLOCK : IGNORE
{
printf("\n...ignoring...\n")
};
%%
int main(int argc, char **argv)
{
while(1)
{
printf("****************\n");
yyparse();
char ign;
scanf("%c",&ign);
}
return 0;
}
int yywrap()
{
return 1;
}
void yyerror (char const *s) {
fprintf (stderr, "%s\n", s);
}
我用来构建的命令:
flex test.l
bison -dy test.y
gcc lex.yy.c y.tab.c -o test.exe
您的 flex 文件包含一系列规则,每个规则由一个模式和一个动作组成。与流行的看法相反,您不需要在使用模式之前“声明”它们。
如果你想在你的词法分析器中忽略空格,你需要一个什么都不做的规则。
您的按键模式有误,我已修复;您的模式不会接受超过一个字母的键。此外,在您的扫描仪中调用 exit
是非常糟糕的风格。让解析器处理错误。
%{
#include "y.tab.h"
%}
%option noyywrap
%%
/* Removed the COMMA rule. See text below. */
/* "," {return COMMA;} */
/* Compare this pattern with the one you used */
[[:alpha:]][[:alnum:]]* {return KEY;}
/* Recognise and ignore whitespace. */
[[:space:]]+ ; /* Do nothing */
/* Send unrecognised input to the parser. */
. {return *yytext;}
您的解析器不需要 IGNORE
,这毫无意义,因为语法不会生成它。 Bison 可能警告过你。
您可以通过其他方式简化解析器:
yywrap
是不需要的,因为你的词法分析器有 %option noyywrap
.
- 如果您只是从词法分析器中删除
","
模式(因为后备规则
. { return *yytext; }
将对任何 single-character 文字正确工作。
对于测试,您可能希望一次解析一行而不是忽略语法错误。
我还建议您在调用 bison 时不要使用“遗留”标志 -y
;该标志只能用于旧的现有 yacc 语法文件,因为它可能会干扰现代 bison 功能。如果没有-y
,bison会将生成的C代码写入<i>filename</i>.tab.c
,生成的header到 <i>文件名</i>.tab.h
。如果你不喜欢那些名字,你可以使用 -o
标志来指定生成的 C 代码的名称(并且 header 将具有相同的名称,扩展名更改为 .h
).
这可能会产生这样的结果:
(请注意,我将 KEY_SET
更改为 key_set
,因为语法中通常的风格是 ALL_CAPS 是标记,而 non-terminals 是 lower-case。我还将它从 right-recursive 更改为 left-recursive 以避免如果您的生产操作打印 KEY
标记的值时您会注意到的问题,假设您的词法分析器已给它一个值。)
文件parser.y
%{
#include<stdio.h>
void yyerror (char const *s);
int yylex(void);
/* Defined in the flex file */
void set_input(const char* input);
%}
%token KEY
%%
key_set : KEY { printf("keyset 1\n"); }
| key_set ',' KEY { printf("keyset 2\n"); };
%%
int main(int argc, char **argv)
{
char buffer[BUFSIZ];
while (1)
{
printf("****************\n");
char* input = fgets(buffer, sizeof buffer, stdin);
if (buffer == NULL) break;
set_input(input);
yyparse();
}
return 0;
}
void yyerror (char const *s) {
fprintf (stderr, "%s\n", s);
}
文件lexer.l
%{
#include "parser.tab.h"
%}
%option noinput nounput nodefault yylineno
%option noyywrap
%%
[[:alpha:]][[:alnum:]]* {return KEY;}
[[:space:]]+ ; /* Do nothing */
. {return *yytext;}
%%
static YY_BUFFER_STATE flex_buffer;
void set_input(const char* input) {
yy_delete_buffer(flex_buffer);
flex_buffer = yy_scan_string(input);
}
构建过程:
flex lexer.l
bison -d parser.y
gcc lex.yy.c parser.tab.c -o parser.exe
你的语法不允许空输入,但没关系。但是,出于测试目的,您可能希望在循环中添加一个测试,该循环读取输入行以仅在该行不为空时调用解析器。
我正在尝试使用 flex-bison 实现一个简单的词法分析器和解析器。
我只想解析这些:
- 一个
- a,b
- a,b
- 一个,b
- a,b,c
- a,b, c
- .....
只是一个用逗号分隔的序列,可能包含也可能不包含 space。 所以这是我的语法:
KEY_SET : KEY
{
printf("keyset 1");
}
| KEY COMMA KEY_SET
{
printf("keyset 2");
};
声明KEY
,COMMA
为token
.//%token
但每当我按回车键或任何白色space。
时,它都会给我 语法错误所以我什至在 flex 中声明了 IGNORE [ \t\n]
。
在解析器中我添加了一个新规则:
IGNORE_BLOCK : IGNORE
{
printf("\n...ignoring...\n")
};
但这甚至没有发挥作用。
它让我一直报语法错误。
我该如何解决?
词法分析器:
%{
#include "y.tab.h"
%}
%option noyywrap
COMMA [,]
KEY [[:alpha:][:alnum:]*]
IGNORE [ \t\n]
%%
{COMMA} {return COMMA;}
{KEY} {return KEY;}
{IGNORE} {return IGNORE;}
. {printf("Exiting...\n");exit(0);}
%%
解析器:
%{
#include<stdio.h>
void yyerror (char const *s);
int yywrap();
//int extern yylex();
%}
%token COMMA
%token KEY
%token IGNORE
%%
KEY_SET : KEY
{
printf("keyset 1");
}
| KEY COMMA KEY_SET
{
printf("keyset 2");
};
IGNORE_BLOCK : IGNORE
{
printf("\n...ignoring...\n")
};
%%
int main(int argc, char **argv)
{
while(1)
{
printf("****************\n");
yyparse();
char ign;
scanf("%c",&ign);
}
return 0;
}
int yywrap()
{
return 1;
}
void yyerror (char const *s) {
fprintf (stderr, "%s\n", s);
}
我用来构建的命令:
flex test.l
bison -dy test.y
gcc lex.yy.c y.tab.c -o test.exe
您的 flex 文件包含一系列规则,每个规则由一个模式和一个动作组成。与流行的看法相反,您不需要在使用模式之前“声明”它们。
如果你想在你的词法分析器中忽略空格,你需要一个什么都不做的规则。
您的按键模式有误,我已修复;您的模式不会接受超过一个字母的键。此外,在您的扫描仪中调用 exit
是非常糟糕的风格。让解析器处理错误。
%{
#include "y.tab.h"
%}
%option noyywrap
%%
/* Removed the COMMA rule. See text below. */
/* "," {return COMMA;} */
/* Compare this pattern with the one you used */
[[:alpha:]][[:alnum:]]* {return KEY;}
/* Recognise and ignore whitespace. */
[[:space:]]+ ; /* Do nothing */
/* Send unrecognised input to the parser. */
. {return *yytext;}
您的解析器不需要 IGNORE
,这毫无意义,因为语法不会生成它。 Bison 可能警告过你。
您可以通过其他方式简化解析器:
yywrap
是不需要的,因为你的词法分析器有%option noyywrap
.- 如果您只是从词法分析器中删除
","
模式(因为后备规则
将对任何 single-character 文字正确工作。. { return *yytext; }
对于测试,您可能希望一次解析一行而不是忽略语法错误。
我还建议您在调用 bison 时不要使用“遗留”标志 -y
;该标志只能用于旧的现有 yacc 语法文件,因为它可能会干扰现代 bison 功能。如果没有-y
,bison会将生成的C代码写入<i>filename</i>.tab.c
,生成的header到 <i>文件名</i>.tab.h
。如果你不喜欢那些名字,你可以使用 -o
标志来指定生成的 C 代码的名称(并且 header 将具有相同的名称,扩展名更改为 .h
).
这可能会产生这样的结果:
(请注意,我将 KEY_SET
更改为 key_set
,因为语法中通常的风格是 ALL_CAPS 是标记,而 non-terminals 是 lower-case。我还将它从 right-recursive 更改为 left-recursive 以避免如果您的生产操作打印 KEY
标记的值时您会注意到的问题,假设您的词法分析器已给它一个值。)
文件parser.y
%{
#include<stdio.h>
void yyerror (char const *s);
int yylex(void);
/* Defined in the flex file */
void set_input(const char* input);
%}
%token KEY
%%
key_set : KEY { printf("keyset 1\n"); }
| key_set ',' KEY { printf("keyset 2\n"); };
%%
int main(int argc, char **argv)
{
char buffer[BUFSIZ];
while (1)
{
printf("****************\n");
char* input = fgets(buffer, sizeof buffer, stdin);
if (buffer == NULL) break;
set_input(input);
yyparse();
}
return 0;
}
void yyerror (char const *s) {
fprintf (stderr, "%s\n", s);
}
文件lexer.l
%{
#include "parser.tab.h"
%}
%option noinput nounput nodefault yylineno
%option noyywrap
%%
[[:alpha:]][[:alnum:]]* {return KEY;}
[[:space:]]+ ; /* Do nothing */
. {return *yytext;}
%%
static YY_BUFFER_STATE flex_buffer;
void set_input(const char* input) {
yy_delete_buffer(flex_buffer);
flex_buffer = yy_scan_string(input);
}
构建过程:
flex lexer.l
bison -d parser.y
gcc lex.yy.c parser.tab.c -o parser.exe
你的语法不允许空输入,但没关系。但是,出于测试目的,您可能希望在循环中添加一个测试,该循环读取输入行以仅在该行不为空时调用解析器。