Yacc语法生成错误的终端

时间:2015-07-04 19:40:38

标签: c++ parsing grammar yacc

我已经在爱好编译器上工作了一段时间,使用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和语法的人都会非常感激。如果需要,还可以显示它解析的语法的示例文件。

谢谢!

1 个答案:

答案 0 :(得分:1)

您不能指望在flex动作之外yytext的值。

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

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

请参阅此bison FAQ entry

顺便说一句,flex文件中的以下代码段不正确:

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

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

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

+

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

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