yacc 停止执行 shift&& reduce 一旦无法从 yylex() 获取更多符号
yacc stop doing shift&& reduce once cannot get any more symbol from yylex()
这是我的代码:
%{
#include<string.h>
#include "y.tab.h"
#define DEBUG 0
void yyerror(char* s);
void debug(char* string) {
if (DEBUG) {
printf(string);
}
}
%}
selector "selector"[0-9]+
positive "+"
negtive "-"
contains "."
before "->"
or "||"
and "&&"
delim [ /n/t]
ws {delim}*
%%
{selector} { debug("Lex:SELECTOR\n"); yylval.string = yytext; return SELECTOR; }
{positive} { debug("Lex:POSITIVE\n"); return POSITIVE; }
{negtive} { debug("Lex:NEGTIVE\n"); return NEGTIVE; }
{contains} { debug("Lex:BELONGS\n"); return CONTAINS; }
{before} { debug("Lex:BEFORE\n"); return BEFORE; }
{or} { debug("Lex:OR\n"); return OR; }
{and} { debug("Lex:AND\n"); return AND; }
{ws} ;
. { debug("Lex Parser Error"); exit(1); }
%%
.y:
%{
#include <stdio.h>
#define YYDEBUG 0
int yyparse(void);
void yyerror(char* s);
%}
%union {
char *string;
}
%token <string> SELECTOR
%token POSITIVE
%token NEGTIVE
%left CONTAINS
%left BEFORE
%token OR
%token AND
%%
logical_expr : assertion { printf("[reduce] L->A\n"); }
| logical_expr AND assertion { printf("[reduce] L->L && A\n");}
| logical_expr OR assertion { printf("[reduce] L->L || A\n"); }
;
assertion : POSITIVE prefix_relation { printf("[reduce] A->+P\n"); }
| NEGTIVE prefix_relation { printf("[reduce] A->-P\n"); }
;
prefix_relation : prefix_relation BEFORE contain_relation { printf("[reduce] P->P->C\n"); }
| contain_relation { printf("[reduce] P->C\n");;}
;
contain_relation : contain_relation CONTAINS SELECTOR { printf("[reduce] C->C.S[%s]\n", ); }
| SELECTOR { printf("[reduce] C->S[%s]\n", ); }
;
%%
int main()
{
return yyparse();
}
void yyerror(char* s)
{
fprintf(stderr,"%s",s);
}
int yywrap()
{
return 1;
}
我的输入字符串是:+selector1.selector2||-selector4->selector4
此输入的解析树预计为:
我的 yacc 生成的程序输出如下:
[reduce] C->S[selector1] // stack: +C
[reduce] C->C.S[selector2] // stack: +C
[reduce] P->C // stack: +P
[reduce] A->+P // stack: A
[reduce] L->A // stack: L
[reduce] C->S[selector3] // stack: L||-C
[reduce] P->C // stack: L||-P
[reduce] C->S[selector4] // stack: L||-P->C
似乎程序停止执行 shift&& reduce 一旦无法从 yylex() 获取更多符号,但我希望它减少堆栈中剩余的符号,因此 L||-P->C
,以便我可以生成我的代码中的整个解析树。
我的预期输出是:
[reduce] C->S[selector1] // stack: +C
[reduce] C->C.S[selector2] // stack: +C
[reduce] P->C // stack: +P
[reduce] A->+P // stack: A
[reduce] L->A // stack: L
[reduce] C->S[selector3] // stack: L||-C
[reduce] P->C // stack: L||-P
[reduce] C->S[selector4] // stack: L||-P->C
[reduce] P->P->C // stack: L||-P
[reduce] A->-P // stack: L||A
[reduce] L->L||A // stack: L
您的扫描仪 (flex) 定义存在一些问题。
您的默认 flex 规则只调用 exit
而没有任何错误消息,(除非 DEBUG
已定义且非零)因此任何词法错误都会导致程序静默停。在这种情况下调用 yyerror
并生成可见的错误消息会好得多。
正如 EJP 在评论中指出的那样,您的 delim
定义使用 /n
和 /t
而不是 \n
和 \t
, 所以它既不匹配换行符也不匹配制表符。换行符也不会与您的默认规则匹配,因此它将落入 flex 生成的默认规则,该规则只是将不匹配的字符打印到 stdout
。 (最好包含 %option nodefault
,如果某些输入匹配您的规则 none,这将导致 flex 产生错误消息。)
您的 selector
规则集 yylval.string = yytext
。您不能这样做,因为 yytext
指向扫描仪的内部存储,并且它指向的字符串将在下次调用 yylex
时被修改。如果你想将匹配的令牌从扫描器传递给解析器,你必须复制令牌,然后你需要确保你 free
当你不再需要字符串时为其分配的存储空间,为了避免内存泄漏。
您需要注意,由 bison
或 yacc
生成的解析器通常需要在执行归约之前读取先行标记。因此,直到扫描器 returns END
标记时,您期望的最后一系列减少才会执行,它只会在读取文件结尾时执行。因此,如果您正在交互式地测试您的解析器,您将不会看到最终的缩减,直到您通过键入 ctrl D(在 Unix 上)发出文件结束信号。
作为最后的说明,flex
和 bison
都能够生成调试输出,指示哪些规则匹配 (flex) 以及一系列的移位、减少和其他解析器操作 (bison ).使用这些功能比尝试实现自己的调试输出更简单、更可靠。
这是我的代码:
%{
#include<string.h>
#include "y.tab.h"
#define DEBUG 0
void yyerror(char* s);
void debug(char* string) {
if (DEBUG) {
printf(string);
}
}
%}
selector "selector"[0-9]+
positive "+"
negtive "-"
contains "."
before "->"
or "||"
and "&&"
delim [ /n/t]
ws {delim}*
%%
{selector} { debug("Lex:SELECTOR\n"); yylval.string = yytext; return SELECTOR; }
{positive} { debug("Lex:POSITIVE\n"); return POSITIVE; }
{negtive} { debug("Lex:NEGTIVE\n"); return NEGTIVE; }
{contains} { debug("Lex:BELONGS\n"); return CONTAINS; }
{before} { debug("Lex:BEFORE\n"); return BEFORE; }
{or} { debug("Lex:OR\n"); return OR; }
{and} { debug("Lex:AND\n"); return AND; }
{ws} ;
. { debug("Lex Parser Error"); exit(1); }
%%
.y:
%{
#include <stdio.h>
#define YYDEBUG 0
int yyparse(void);
void yyerror(char* s);
%}
%union {
char *string;
}
%token <string> SELECTOR
%token POSITIVE
%token NEGTIVE
%left CONTAINS
%left BEFORE
%token OR
%token AND
%%
logical_expr : assertion { printf("[reduce] L->A\n"); }
| logical_expr AND assertion { printf("[reduce] L->L && A\n");}
| logical_expr OR assertion { printf("[reduce] L->L || A\n"); }
;
assertion : POSITIVE prefix_relation { printf("[reduce] A->+P\n"); }
| NEGTIVE prefix_relation { printf("[reduce] A->-P\n"); }
;
prefix_relation : prefix_relation BEFORE contain_relation { printf("[reduce] P->P->C\n"); }
| contain_relation { printf("[reduce] P->C\n");;}
;
contain_relation : contain_relation CONTAINS SELECTOR { printf("[reduce] C->C.S[%s]\n", ); }
| SELECTOR { printf("[reduce] C->S[%s]\n", ); }
;
%%
int main()
{
return yyparse();
}
void yyerror(char* s)
{
fprintf(stderr,"%s",s);
}
int yywrap()
{
return 1;
}
我的输入字符串是:+selector1.selector2||-selector4->selector4
此输入的解析树预计为:
我的 yacc 生成的程序输出如下:
[reduce] C->S[selector1] // stack: +C
[reduce] C->C.S[selector2] // stack: +C
[reduce] P->C // stack: +P
[reduce] A->+P // stack: A
[reduce] L->A // stack: L
[reduce] C->S[selector3] // stack: L||-C
[reduce] P->C // stack: L||-P
[reduce] C->S[selector4] // stack: L||-P->C
似乎程序停止执行 shift&& reduce 一旦无法从 yylex() 获取更多符号,但我希望它减少堆栈中剩余的符号,因此 L||-P->C
,以便我可以生成我的代码中的整个解析树。
我的预期输出是:
[reduce] C->S[selector1] // stack: +C
[reduce] C->C.S[selector2] // stack: +C
[reduce] P->C // stack: +P
[reduce] A->+P // stack: A
[reduce] L->A // stack: L
[reduce] C->S[selector3] // stack: L||-C
[reduce] P->C // stack: L||-P
[reduce] C->S[selector4] // stack: L||-P->C
[reduce] P->P->C // stack: L||-P
[reduce] A->-P // stack: L||A
[reduce] L->L||A // stack: L
您的扫描仪 (flex) 定义存在一些问题。
您的默认 flex 规则只调用
exit
而没有任何错误消息,(除非DEBUG
已定义且非零)因此任何词法错误都会导致程序静默停。在这种情况下调用yyerror
并生成可见的错误消息会好得多。正如 EJP 在评论中指出的那样,您的
delim
定义使用/n
和/t
而不是\n
和\t
, 所以它既不匹配换行符也不匹配制表符。换行符也不会与您的默认规则匹配,因此它将落入 flex 生成的默认规则,该规则只是将不匹配的字符打印到stdout
。 (最好包含%option nodefault
,如果某些输入匹配您的规则 none,这将导致 flex 产生错误消息。)您的
selector
规则集yylval.string = yytext
。您不能这样做,因为yytext
指向扫描仪的内部存储,并且它指向的字符串将在下次调用yylex
时被修改。如果你想将匹配的令牌从扫描器传递给解析器,你必须复制令牌,然后你需要确保你free
当你不再需要字符串时为其分配的存储空间,为了避免内存泄漏。您需要注意,由
bison
或yacc
生成的解析器通常需要在执行归约之前读取先行标记。因此,直到扫描器 returnsEND
标记时,您期望的最后一系列减少才会执行,它只会在读取文件结尾时执行。因此,如果您正在交互式地测试您的解析器,您将不会看到最终的缩减,直到您通过键入 ctrl D(在 Unix 上)发出文件结束信号。
作为最后的说明,flex
和 bison
都能够生成调试输出,指示哪些规则匹配 (flex) 以及一系列的移位、减少和其他解析器操作 (bison ).使用这些功能比尝试实现自己的调试输出更简单、更可靠。