【问题标题】:Yacc grammar producing incorrect terminalYacc 语法产生不正确的终端
【发布时间】:2015-07-04 19:40:37
【问题描述】:

我一直在研究一个爱好编译器,在解析阶段使用 lex 和 yacc。这对大多数事情来说都很好,但是当我添加 if 语句时,符号的产生规则现在给出堆栈上的上一个(或下一个?)项目,而不是所需的符号值。

语法如下,希望去掉不相关的规则:

%{
       ...
%}


    %define parse.error verbose


    %token ...

    %%


    Program:
            Function                                            { root->addChild($1);}      
            ;


    Function:
            Type Identifier '|' ArgumentList '|' StatementList END
                                                                { $$ = new FunctionDef($1, $2, $4, $6); }


    /******************************************/
    /* Statements and control flow ************/
    /******************************************/

    Statement:
            Expression Delimiter
            | VariableDeclaration Delimiter
            | ControlFlowStatement Delimiter
            | Delimiter
            ;

    ControlFlowStatement:
            IfStatement
            ;

    IfStatement:
            IF Expression StatementList END                       { $$ = new IfStatement($2, $3); }
            | IF Expression StatementList ELSE StatementList END  { $$ = new IfStatement($2, $3, $5);}
            ;

    VariableDeclaration:
            Type Identifier                                     { $$ = new VariableDeclaration($1, $2);}
            | Type Identifier EQUALS Expression                 { $$ = new VariableDeclaration($1, $2, $4);}
            ;

    StatementList:
            StatementList Statement                             { $1->addChild($2);             }
            | Statement                                         { $$ = new GenericList($1);     }
            ;


    Delimiter:
            ';'
            | NEWLINE
            ;
    Type:
           ...
Expression:
    ...

    PostfixExpression:
            Value '[' Expression ']'                            { std::cout << "TODO: indexing operators ([ ])" << std::endl;}
            | Value '.' SYMBOL                                  { std::cout << "TODO: member access" << std::endl;}
            | Value INCREMENT                                   { $$ = new UnaryExpression(UNARY_POSTINC, $1);  }
            | Value DECREMENT                                   { $$ = new UnaryExpression(UNARY_POSTDEC, $1);  }
            | Value '(' ')'                                     { $$ = new FunctionCall($1, NULL);    }
            | Value '(' ExpressionList ')'                      { $$ = new FunctionCall($1, $3);                }
            | Value
            ;


    Value:
            BININT                                              { $$ = new Integer(yytext, 2);                  }
            | HEXINT                                            { $$ = new Integer(yytext, 16);                 }
            | DECINT                                            { $$ = new Integer(yytext);                     }
            | FLOAT                                             { $$ = new Float(yytext);                       }
            | SYMBOL                                            { $$ = new Symbol(yytext);                      }
            | STRING                                            { $$ = new String(yytext);                      }
            | LambdaFunction
            | '(' Expression ')'                                { $$ = $2;                                      }
            | '[' ExpressionList ']'                            { $$ = $2;}
            ;

    LambdaFunction:
            ...


    %%

我无法弄清楚控制流代码可以制作符号: 规则匹配未归类为 lex 定义中的符号的内容:

symbol                      [a-zA-Z_]+(alpha|digit)*
...
{symbol}                    {return SYMBOL;}

任何了解 yacc 和一般语法的人的帮助将不胜感激。如果需要,还可以显示它解析的语法示例文件。

谢谢!

【问题讨论】:

  • TL;DR!将语法缩小到支持有问题的规则所需的最低限度。例如,您可能不需要完整的表达式层次结构或大多数其他语句或声明。如果它有效,则为表达式再添加一个级别,并继续直到出现错误。如果即使使用最少的语法也会出现错误,至少您消除了许多可能的来源,并且可以编辑您的问题以仅包含最少的语法。
  • 我自己也在想同样的事情,可能发布时间太长,但我决定谨慎行事,不知道错误在哪里!你是对的,虽然我可以不用表达式堆栈等。
  • 关于剥离语法以查找问题根源,我确实这样做了,发现它是控制流代码,但我不知道如何或为什么。跨度>
  • 再看IF规则,你确定要一个语句作为条件吗?现在它允许无限递归(它允许if if if if if if ... 等等)。
  • 这实际上是我忘记取出的遗留测试,我已经在黑暗中跌跌撞撞了一段时间!它应该是表达,但这仍然没有帮助:/感谢您的回复。

标签: c++ parsing grammar yacc


【解决方案1】:

你不能指望在弹性动作之外的yytext 的值。

Bison 语法通常会在决定如何继续之前读取前瞻标记,因此在 bison 操作中,yytext 已被替换为前瞻标记的标记值。 (不过,您也不能指望这一点:有时不需要前瞻令牌。)

因此,您需要在 flex 操作返回之前制作 yytext 的副本,并将该副本放入 yylval 语义联合中,以使该副本可用于野牛语法。

看到这个bison FAQ entry


顺便说一句,您的 flex 文件中的以下 sn-p 不正确:

symbol                      [a-zA-Z_]+(alpha|digit)*

在那个正则表达式中,alphadigit 只是普通字符串,所以它和[a-zA-Z_]+("alpha"|"digit")* 相同,这意味着它会匹配,例如a_digitdigitdigit,而不是a_123。 (如果没有+ 后面的部分,它会匹配a_digitdigitdigit,所以我认为这不是你的意图。)

总的来说,我认为使用 Posix 字符类比使用手写字符类或定义的符号更好,所以我会这样写

symbol    [[:alpha:]_]([[:alnum:]_]*[[:alnum:]])?

假设您的意图是符号可以以下划线开头但不能以下划线结尾,以数字结尾但不能以数字开头。使用 Posix 字符类需要您使用正确的语言环境(几乎可以肯定是 C 语言环境)执行 flex,但字符范围也是如此,因此使用自记录 Posix 类不会丢失任何内容.

(当然,我不知道你对{alpha}{digit} 的定义是什么,但在我看来它们与[[:alpha:]][[:digit:]] 相同,在这种情况下它们是多余的,或者与 Posix 类不同,在这种情况下它们会让读者感到困惑。)

【讨论】:

  • 我不知道 yytext 值有时在 lex 规则之外不正确,我认为这可能是问题所在,将很快进行测试。感谢您非常彻底的回答,我想您可能刚刚保存了我的爱好项目!我正准备认输!我也会记住 lex 规则,这可能会导致问题。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-15
  • 2014-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多