Yacc 语法产生不正确的终端

Yacc grammar producing incorrect terminal

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

下面给出了语法,希望不相关的规则被删除:

%{
       ...
%}


    %define parse.error verbose


    %token ...

    %%


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


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


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

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

    ControlFlowStatement:
            IfStatement
            ;

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

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

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


    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, );  }
            | Value DECREMENT                                   { $$ = new UnaryExpression(UNARY_POSTDEC, );  }
            | Value '(' ')'                                     { $$ = new FunctionCall(, NULL);    }
            | Value '(' ExpressionList ')'                      { $$ = new FunctionCall(, );                }
            | 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 ')'                                { $$ = ;                                      }
            | '[' ExpressionList ']'                            { $$ = ;}
            ;

    LambdaFunction:
            ...


    %%

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

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

非常感谢了解 yacc 和一般语法的人提供的任何帮助。如有必要,还可以显示它解析的语法示例文件。

谢谢!

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

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

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

看到这个bison FAQ entry


顺便说一下,您的 flex 文件中的以下片段是不正确的:

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

那个正则表达式中,alphadigit只是普通的字符串,所以和[a-zA-Z_]+("alpha"|"digit")*是一样的,意思就是会匹配,比如a_digitdigitdigit 但不是 a_123。 (如果没有 + 之后的部分,它会匹配 a_digitdigitdigit,所以我认为这不是您的意图。)

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

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

假设您的意图是一个符号可以以下划线开头但不能以下划线结尾,并且可以以数字结尾但不能以数字开头。使用 Posix 字符 类 要求您使用正确的语言环境执行 flex -- 几乎可以肯定是 C 语言环境 -- 但是 字符范围 也是如此,所以什么都没有使用自我记录 Posix 类.

丢失

(当然,我不知道你对 {alpha}{digit} 的定义是什么,但在我看来它们与 [[:alpha:]][[:digit:]],在这种情况下它们是多余的,或者不同于 Posix 类,在这种情况下它们与 reader 混淆。)