由扫描器或解析器引起的 Lex 和 Yacc 中的语法错误
Syntax Error in Lex and Yacc caused by scanner or parser
我是 Lex 和 Yacc 的新手。我尝试学习语法规则和语义动作。我试图编写一个基本上执行赋值、函数声明、函数调用和打印语句的解析器。问题是,在我提供输入后,我得到的输出是 语法错误 。所以我认为我的语法导致了这个,但我不确定。这是我的文件:
scanner.flx:
%option noyywrap
%option yylineno
%{
#include "parser.tab.h"
%}
IDENT [a-zA-Z_][a-zA-Z0-9_]*
INT -?[0-9]+
STRING "[\S\s]*?"
UNFSTRING "[\S\s]*?[^"]$
%%
"int" return tINT;
"string" return tSTRING;
"return" return tRETURN;
"print" return tPRINT;
"(" return tLPAR;
")" return tRPAR;
"," return tCOMMA;
"%" return tMOD;
"=" return tASSIGNM;
"-" return tMINUS;
"+" return tPLUS;
"/" return tDIV;
"*" return tSTAR;
";" return tSEMI;
"{" return tLBRAC;
"}" return tRBRAC;
{IDENT} return tIDENT;
{INT} return tINTVAL;
{STRING} return tSTRINGVAL;
{UNFSTRING} return tUNFSTRING;
[ \t\n]+
. { /* pass any other character to the parser */
return yytext[0];
}
%%
parser.y:
%{
#include <stdio.h>
void yyerror (const char *s)
{
printf ("%s\n", s);
}
%}
%token tINT tSTRING tRETURN tPRINT tLPAR tRPAR tCOMMA tMOD tASSIGNM tMINUS tPLUS tDIV tSTAR tSEMI tLBRAC tRBRAC tIDENT tINTVAL tSTRINGVAL tUNFSTRING
%left '='
%left '+' '-'
%left '*' '/'
%left '(' ')'
%%
CVD19 : stmtlst
;
stmtlst : stmtlst stmt
| stmt
;
stmt : funcDecl
| varDecl
| assgnmt
| callfunc
| printstmt
;
funcDecl : type tIDENT '(' ')' '{' funcbody return '}' { printf("FUNCTION "); }
| type tIDENT '(' funcparams ')' '{' funcbody return '}' { printf("FUNCTION W/PARAMS "); }
;
funcbody : varDecl
| assgnmt
| callfunc
| printstmt
;
return : tRETURN expr ';'
;
funcparams : funcparams ',' type tIDENT
| type tIDENT
;
varDecl : type vars '=' expr ';'
;
type : tINT { printf("INT TYPE "); }
| tSTRING { printf("STRING TYPE "); }
;
assgnmt : tIDENT '=' expr ';' { printf("ASSIGNMENT"); }
;
callfunc : tIDENT '(' ')' ';' { printf("FUNCTION CALL"); }
| tIDENT '(' vars ')' ';' { printf("FUNCTION W/PARAMs CALL"); }
;
printstmt : tPRINT '(' expr ')' ';' { printf("PRINTSTMT 1"); }
| tPRINT '(' callfunc ')' ';' { printf("PRINTSTMT 2"); }
;
vars : vars ',' tIDENT
| tIDENT { printf("IDENT "); }
;
expr : value
| expr '+' expr { $$ = + ; }
| expr '-' expr { $$ = - ; }
| expr '*' expr { $$ = * ; }
| expr '/' expr { $$ = / ; }
;
value : tINTVAL { printf("INTVAL "); }
| tSTRINGVAL { printf("STRINGVAL "); }
| tUNFSTRING { printf("UNFSTRING "); }
/*| tIDENT MIGHT BE PROBLEMATIC { $$ = ; }*/
;
%%
int main ()
{
if (yyparse()) {
// parse error
printf("ERROR\n");
return 1;
}
else {
// successful parsing
printf("OK\n");
return 0;
}
}
当我尝试在 MacOS 终端中 运行 我的文件时,我正常使用这些命令:
flex scanner.flx
-- NO PROBLEMS --
bison -d parser.y
-- NO PROBLEMS --
gcc -o program lex.yy.c parser.tab.c -ll
-- WARNING --
parser.tab.c:1330:16: warning: implicit declaration of function 'yylex' is invalid in C99
[-Wimplicit-function-declaration]
yychar = YYLEX;
^
parser.tab.c:686:16: note: expanded from macro 'YYLEX'
# define YYLEX yylex ()
^
1 warning generated.
Here is around line 1330 in parser.tab.c:
/* First try to decide what to do without reference to look-ahead token. */
yyn = yypact[yystate];
if (yyn == YYPACT_NINF)
goto yydefault;
/* Not known => get a look-ahead token if don't already have one. */
/* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
if (yychar == YYEMPTY)
{
YYDPRINTF ((stderr, "Reading a token: "));
yychar = YYLEX; /* THIS IS LINE 1330 <=============================================
}
if (yychar <= YYEOF)
{
yychar = yytoken = YYEOF;
YYDPRINTF ((stderr, "Now at end of input.\n"));
}
else
{
yytoken = YYTRANSLATE (yychar);
YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
}
这是我的输入:
input1:
int num = 123;
output1:
INT TYPE IDENT syntax error
ERROR
input2:
print("str");
output2:
syntax error
ERROR
input3:
int func(int i) {
i = 5;
return i;
}
output3:
INT TYPE IDENT syntax error
ERROR
你的基本问题是你在语法中使用字符文字(这很好)但没有在你的词法分析器中返回它们(这不好)。
而不是
"(" return tLPAR;
")" return tRPAR;
// etc.
让这些字符落入您的后备规则:
. { return yytext[0]; }
那么你也可以摆脱这些单字符标记的 %token
定义,因为你使用的是单字符文字。
不幸的是,您不能使用更长的令牌来做到这一点。因此,您的关键字令牌必须保持原样。
此外,您的字符串规则完全错误。请阅读 (f)lex regular expressions 的文档,而不是依赖其他一些正则表达式语法。 Flex 不识别 \S
和 \s
转义符。它没有实现非贪婪重复(*?
)。它确实使用 "
作为特殊语法(意思是引用的文字字符串)——事实上,您已经在其他规则中使用过它,所以您不应该期望 "
成为您的规则中的常规字符STRING
格式。 $
不能在宏中使用(事实上,没有充分的理由在此扫描仪定义中使用宏;我总是建议避免使用它们,除非有充分的理由。)
一个可能的字符串操作是:
["]([^"]|\.|\\n)*["] { return tSTRINGVAL; }
我强烈建议您阅读 bison 手册中关于 debugging your grammar 的章节,尤其是关于如何启用解析器跟踪的部分,它比插入 printf
调用更准确和信息更丰富进入你的解析器动作。 (事实上,这会告诉你你的问题。)
您的问题与编译器产生的警告无关,但您应该解决这个问题。发生这种情况是因为您没有在野牛序言中声明 yylex
。将其放在 yyerror
:
的定义之前
int yylex(void);
让编译器知道yylex
的原型是什么。 (你必须声明它,因为野牛不会为你做那件事。)
我是 Lex 和 Yacc 的新手。我尝试学习语法规则和语义动作。我试图编写一个基本上执行赋值、函数声明、函数调用和打印语句的解析器。问题是,在我提供输入后,我得到的输出是 语法错误 。所以我认为我的语法导致了这个,但我不确定。这是我的文件:
scanner.flx:
%option noyywrap
%option yylineno
%{
#include "parser.tab.h"
%}
IDENT [a-zA-Z_][a-zA-Z0-9_]*
INT -?[0-9]+
STRING "[\S\s]*?"
UNFSTRING "[\S\s]*?[^"]$
%%
"int" return tINT;
"string" return tSTRING;
"return" return tRETURN;
"print" return tPRINT;
"(" return tLPAR;
")" return tRPAR;
"," return tCOMMA;
"%" return tMOD;
"=" return tASSIGNM;
"-" return tMINUS;
"+" return tPLUS;
"/" return tDIV;
"*" return tSTAR;
";" return tSEMI;
"{" return tLBRAC;
"}" return tRBRAC;
{IDENT} return tIDENT;
{INT} return tINTVAL;
{STRING} return tSTRINGVAL;
{UNFSTRING} return tUNFSTRING;
[ \t\n]+
. { /* pass any other character to the parser */
return yytext[0];
}
%%
parser.y:
%{
#include <stdio.h>
void yyerror (const char *s)
{
printf ("%s\n", s);
}
%}
%token tINT tSTRING tRETURN tPRINT tLPAR tRPAR tCOMMA tMOD tASSIGNM tMINUS tPLUS tDIV tSTAR tSEMI tLBRAC tRBRAC tIDENT tINTVAL tSTRINGVAL tUNFSTRING
%left '='
%left '+' '-'
%left '*' '/'
%left '(' ')'
%%
CVD19 : stmtlst
;
stmtlst : stmtlst stmt
| stmt
;
stmt : funcDecl
| varDecl
| assgnmt
| callfunc
| printstmt
;
funcDecl : type tIDENT '(' ')' '{' funcbody return '}' { printf("FUNCTION "); }
| type tIDENT '(' funcparams ')' '{' funcbody return '}' { printf("FUNCTION W/PARAMS "); }
;
funcbody : varDecl
| assgnmt
| callfunc
| printstmt
;
return : tRETURN expr ';'
;
funcparams : funcparams ',' type tIDENT
| type tIDENT
;
varDecl : type vars '=' expr ';'
;
type : tINT { printf("INT TYPE "); }
| tSTRING { printf("STRING TYPE "); }
;
assgnmt : tIDENT '=' expr ';' { printf("ASSIGNMENT"); }
;
callfunc : tIDENT '(' ')' ';' { printf("FUNCTION CALL"); }
| tIDENT '(' vars ')' ';' { printf("FUNCTION W/PARAMs CALL"); }
;
printstmt : tPRINT '(' expr ')' ';' { printf("PRINTSTMT 1"); }
| tPRINT '(' callfunc ')' ';' { printf("PRINTSTMT 2"); }
;
vars : vars ',' tIDENT
| tIDENT { printf("IDENT "); }
;
expr : value
| expr '+' expr { $$ = + ; }
| expr '-' expr { $$ = - ; }
| expr '*' expr { $$ = * ; }
| expr '/' expr { $$ = / ; }
;
value : tINTVAL { printf("INTVAL "); }
| tSTRINGVAL { printf("STRINGVAL "); }
| tUNFSTRING { printf("UNFSTRING "); }
/*| tIDENT MIGHT BE PROBLEMATIC { $$ = ; }*/
;
%%
int main ()
{
if (yyparse()) {
// parse error
printf("ERROR\n");
return 1;
}
else {
// successful parsing
printf("OK\n");
return 0;
}
}
当我尝试在 MacOS 终端中 运行 我的文件时,我正常使用这些命令:
flex scanner.flx
-- NO PROBLEMS --
bison -d parser.y
-- NO PROBLEMS --
gcc -o program lex.yy.c parser.tab.c -ll
-- WARNING --
parser.tab.c:1330:16: warning: implicit declaration of function 'yylex' is invalid in C99 [-Wimplicit-function-declaration] yychar = YYLEX; ^ parser.tab.c:686:16: note: expanded from macro 'YYLEX' # define YYLEX yylex () ^ 1 warning generated.
Here is around line 1330 in parser.tab.c:
/* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a look-ahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; /* THIS IS LINE 1330 <============================================= } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); }
这是我的输入:
input1:
int num = 123;
output1:
INT TYPE IDENT syntax error ERROR
input2:
print("str");
output2:
syntax error ERROR
input3:
int func(int i) { i = 5; return i; }
output3:
INT TYPE IDENT syntax error ERROR
你的基本问题是你在语法中使用字符文字(这很好)但没有在你的词法分析器中返回它们(这不好)。
而不是
"(" return tLPAR;
")" return tRPAR;
// etc.
让这些字符落入您的后备规则:
. { return yytext[0]; }
那么你也可以摆脱这些单字符标记的 %token
定义,因为你使用的是单字符文字。
不幸的是,您不能使用更长的令牌来做到这一点。因此,您的关键字令牌必须保持原样。
此外,您的字符串规则完全错误。请阅读 (f)lex regular expressions 的文档,而不是依赖其他一些正则表达式语法。 Flex 不识别 \S
和 \s
转义符。它没有实现非贪婪重复(*?
)。它确实使用 "
作为特殊语法(意思是引用的文字字符串)——事实上,您已经在其他规则中使用过它,所以您不应该期望 "
成为您的规则中的常规字符STRING
格式。 $
不能在宏中使用(事实上,没有充分的理由在此扫描仪定义中使用宏;我总是建议避免使用它们,除非有充分的理由。)
一个可能的字符串操作是:
["]([^"]|\.|\\n)*["] { return tSTRINGVAL; }
我强烈建议您阅读 bison 手册中关于 debugging your grammar 的章节,尤其是关于如何启用解析器跟踪的部分,它比插入 printf
调用更准确和信息更丰富进入你的解析器动作。 (事实上,这会告诉你你的问题。)
您的问题与编译器产生的警告无关,但您应该解决这个问题。发生这种情况是因为您没有在野牛序言中声明 yylex
。将其放在 yyerror
:
int yylex(void);
让编译器知道yylex
的原型是什么。 (你必须声明它,因为野牛不会为你做那件事。)