Parsing 未能识别的Yacc衍生产品

Parsing 未能识别的Yacc衍生产品,parsing,syntax,yacc,lex,Parsing,Syntax,Yacc,Lex,这是一个类项目,我已经解决了99%的问题,但现在我被卡住了。语法是给你的 我有以下lex文件,可按预期工作: %{ #include "y.tab.h" %} delim [ \t\n] ws {delim}+ comment ("/*".*"*/")|("//".*\n) id [a-zA-Z]([a-zA-Z0-9_])* int_literal [0-9]* op ("&&"|"<"|"+"|"

这是一个类项目,我已经解决了99%的问题,但现在我被卡住了。语法是给你的

我有以下lex文件,可按预期工作:

%{
#include "y.tab.h"
%}
delim       [ \t\n]
ws          {delim}+
comment     ("/*".*"*/")|("//".*\n)
id          [a-zA-Z]([a-zA-Z0-9_])*
int_literal [0-9]*
op          ("&&"|"<"|"+"|"-"|"*")
class       "class"
public      "public"
static      "static"
void        "void"
main        "main"
string      "String"
extends     "extends"
return      "return"
boolean     "boolean"
if          "if"
new         "new"
else        "else"
while       "while"
length      "length"
int         "int"
true        "true"
false       "false"
this        "this"
println     "System.out.println"
lbrace      "{"
rbrace      "}"
lbracket    "["
rbracket    "]"
semicolon   ";"
lparen      "("
rparen      ")"
comma       ","
equals      "="
dot         "."
exclamation "!"

%%

{ws}        { /* Do nothing! */ }
{comment}   { /* Do nothing! */ }
{println}   {  return PRINTLN; } /* Before {period} to give this pre
cedence */
{op}        {  return OP;      }
{int_literal}   {  return INTEGER_LITERAL; }
{class}     {  return CLASS; }
{public}    {  return PUBLIC; }
{static}    {  return STATIC; }
{void}      {  return VOID; }
{main}      {  return MAIN; }
{string}    {  return STRING; }
{extends}   {  return EXTENDS; }
{return}    {  return RETURN; }
{boolean}   {  return BOOLEAN; }
{if}        {  return IF; }
{new}       {  return NEW; }
{else}      {  return ELSE; }
{while}     {  return WHILE; }
{length}    {  return LENGTH; }
{int}       {  return INT; }
{true}      {  return TRUE; }
{false}     {  return FALSE; }
{this}      {  return THIS; }
{lbrace}    {  return LBRACE; }
{rbrace}    {  return RBRACE; }
{lbracket}  {  return LBRACKET; }
{rbracket}  {  return RBRACKET; }
{semicolon} {  return SEMICOLON; }
{lparen}    {  return LPAREN; }
{rparen}    {  return RPAREN; }
{comma}     {  return COMMA; }
{equals}    {  return EQUALS; }
{dot}       {  return DOT; }
{exclamation}   {  return EXCLAMATION; }
{id}        {  return ID; }
%%

int main(void) {
  yyparse();
  exit(0);
}

int yywrap(void) {
  return 0;
}

int yyerror(void) {
  printf("Parse error. Sorry bro.\n");
  exit(1);
}
不起作用的派生有以下两种:

Statement:

 | ID EQUALS Exp SEMICOLON
 | ID LBRACKET Exp RBRACKET EQUALS Exp SEMICOLON
如果我只对文件进行lex处理并获得令牌流,那么令牌将与模式完美匹配。下面是一个输入和输出示例:

num1 = id1;
num2[0] = id2;
给出:

ID
EQUALS
ID
SEMICOLON
ID
LBRACKET
INTEGER_LITERAL
RBRACKET
EQUALS
ID
SEMICOLON
我不明白的是,这个令牌流是如何与语法完全匹配的,但是调用了yyrerror。几个小时来我一直在想这个问题,最后我放弃了。我希望能深入了解问题的原因

对于完整示例,您可以通过解析器运行以下输入:

class Minimal {
    public static void main (String[] a) {
        // Infinite loop
        while (true) {
            /* Completely useless // (embedded comment) stat
ements */
            if ((!false && true)) {
                if ((new Maximal().calculateValue(id1, i
d2) * 2) < 5) {
                    System.out.println(new int[11].l
ength < 10);
                }
                else { System.out.println(0); }
            }
            else { System.out.println(false); }
        }
    }
}

class Maximal {

    public int calculateValue(int[] id1, int id2) {
        int[] num1; int num2;
        num1 = id1;
        num2[0] = id2;
        return (num1[0] * num2) - (num1[0] + num2);
    }
}
类极小值{
公共静态void main(字符串[]a){
//无限循环
while(true){
/*完全无用//(嵌入注释)状态
元素*/
如果(!false&&true)){
if((new max().calculateValue)(id1,i
d2)*2)<5){
System.out.println(新的int[11].l
长度<10);
}
else{System.out.println(0);}
}
else{System.out.println(false);}
}
}
}
类极大{
公共int计算值(int[]id1,int id2){
int[]num1;int num2;
num1=id1;
num2[0]=id2;
返回值(num1[0]*num2)-(num1[0]+num2);
}
}
它应该正确地解析,但它在
num1=id1时出错
num2[0]=id2


PS-我知道这在语义上是不正确的MiniJava,但在语法上应该是正确的:)

您对
语句的定义没有任何错误。它们触发错误的原因是它们以
ID
开头

首先,当bison处理您的输入时,它会报告:

minijava.y: conflicts: 8 shift/reduce
转移/减少冲突并不总是一个问题,但你不能忽视它们。您需要知道是什么导致了这些问题,以及默认行为是否正确。(默认行为是选择shift而不是reduce。)

转移/减少冲突的六个原因是:

Exp: Exp OP Exp
这本身就是模棱两可的。您需要通过使用实际运算符(而不是
OP
)和插入优先规则(或特定产品)来解决这个问题。这与眼前的问题无关,而且由于(目前)第一个
Exp
或第二个获得优先权并不重要,因此默认的解决方案就可以了

其他产品来自以下产品:

VarDeclList: VarDecl VarDeclList
           | %empty
在这里,
VarDecl
可能以
ID
开头(如果类名用作类型)

vardeclist
是从
MethodDecl
生成的:

MethodDecl: ... VarDeclList StatementList ...
现在,假设我们正在解析输入;我们刚刚分析了:

int num2;
我们在看下一个标记,它是
num1
(from
num1=id1
)<代码>int num2
当然是一个
VarDecl
,因此它将与中的
VarDecl
匹配

VarDeclList: VarDecl VarDeclList
在此上下文中,
VarDeclList
可以为空,也可以以另一个声明开头。如果它是空的,我们需要立即减少它(因为我们不会再有机会:非终端需要在其右侧完成之前减少)。如果它不是空的,我们可以简单地移动第一个标记。但是我们需要根据当前的前瞻令牌做出决定,它是一个
ID

不幸的是,这对我们没有帮助。
VarDeclList
StatementList
都可以以
ID
开头,因此reduce和shift都是可行的。因此,
野牛
移动

现在,让我们假设
VarDeclList
使用左递归而不是右递归。(左递归在LR语法中几乎总是更好):

现在,当我们到达
VarDecl
的末尾时,我们只有一个选择:减少
vardeclist
。然后我们将处于以下状态:

MethodDecl: ... VarDeclList · StatementList
VarDeclList: VarDeclList · VarDecl
现在,我们看到
ID
lookhead,不知道它是启动
StatementList
还是启动
VarDecl
。但这并不重要,因为我们不需要减少这些非终端中的任何一个;我们可以等着看下一步会发生什么,然后再做出承诺

注意,在这种情况下,左递归和右递归之间有一个小的语义差异。显然,语法树是不同的:

         VDL                          VDL
        /   \                        /   \
      VDL  Decl                    Decl  VDL
     /   \                              /   \
   VDL  Decl                          Decl  VDL
    |                                        |
    λ                                        λ
然而,在实践中,最有可能采取的行动是:

VarDeclList: %empty              { $$ = newVarDeclList(); }
           | VarDeclList VarDecl { $$ = $1; appendVarDecl($$, $2); }
这很好用


顺便说一下:

1) 虽然flex允许您使用定义来简化正则表达式,但它并不要求您使用它们,而且(据我所知)没有任何地方规定使用定义是最佳实践。我很少使用定义,通常只有当我要用同一个组件编写两个正则表达式时,或者偶尔当正则表达式非常复杂,我想把它分解成几个部分时。但是,完全没有必要将flex文件与以下内容混在一起:

begin           "begin"
...
%%
...
{begin}         { return BEGIN; }
而不是更简单,更可读

"begin"         { return BEGIN; }
2) 同样地,
bison
允许您将单字符标记编写为单引号文本:
'(“
。这有许多优点,首先是它提供了更可读的语法视图。此外,您不需要声明这些标记,也不需要为它们想出一个好名字。此外,由于标记的值是字符本身,因此您的flex文件也可以简化。而不是

"+"     { return PLUS; }
"-"     { return MINUS; }
"("     { return LPAREN; }
...
你可以写:

[-+*/(){}[\]!]   { return yytext[0]; }
事实上,我通常建议不要使用该规则;只需在末尾使用一个全面的flex规则:

.                { return yytext[0]; }
这将把所有不匹配的字符作为单字符标记传递给bison;如果bison不知道该标记,它将发出语法错误。
[-+*/(){}[\]!]   { return yytext[0]; }
.                { return yytext[0]; }