Flex/bison 语法错误

Var*_*yan 5 yacc lex bison lexical-analysis flex-lexer

我正在尝试编写一个能够使用以下输入的语法:

begin #this is a example
    x = 56;

    while x > 0 do
    begin
    point 15.6 78.96;
    end;
end;
Run Code Online (Sandbox Code Playgroud)

这是 lexer.l 文件:

%option noyywrap

%{
#include "parser.h"
#include <stdlib.h>
#include <stdio.h>
const char * const unrecognizedToken = "Unrecognized token";

%}


NewLine                 \n
WhiteSpaces             [\r\t\f\v ]+
Semicolon                    ;

digit       [0-9]
number1     {digit}+\.?([eE][-+]?{digit}+)?
number2     {digit}*\.{digit}+([eE][-+]?{digit}+)?
double_number      {number1}|{number2}

BEGIN                   "begin"
END                     "end"
WHILE                   "while"
DO                      "do"
POINT                   "point"

%x POINT_DEFINITIONS

%%

{WhiteSpaces} {
    printf("WhiteSpaces");
    printf("\n");
} 

{NewLine}     {
    printf("NewLine");
    printf("\n");
} 


{WHILE} {
    printf("While");
    printf("\n");
    return TOKEN_WHILE;
}

{BEGIN} {
    printf("TOKEN_BEGIN");
    printf("\n");
    return TOKEN_BEGIN;
}

{END}   {
    printf("TOKEN_END");
    printf("\n");
    return TOKEN_END;
}

{DO}    { 
    printf("DO");
    printf("\n");
    return TOKEN_DO;
}

{POINT}                                   { 
    printf("POINT_START");
    printf("\n");
    BEGIN POINT_DEFINITIONS; 
    return TOKEN_POINT; 
}

<POINT_DEFINITIONS>{double_number}             {
    printf("POINT_DEFINITIONS %s", yytext);
    printf("\n");
    yylval.dval = atof(yytext);
    return TOKEN_DOUBLE;
}  

<POINT_DEFINITIONS>{WhiteSpaces}             {
    printf("WhiteSpaces");
    printf("\n");
}  

[a-zA-Z_][a-zA-Z0-9_]* { 
    printf("TOKEN_IDENTIFIER");
    printf("\n");
    yylval.name = strdup(yytext);
    return TOKEN_IDENTIFIER;
}

[()=;]  { 
    printf("yytext = %s", yytext);
    printf("\n");
    return *yytext;
}

[*/+\-<>] { 
    printf("TOKEN_OPERATOR");
    printf("\n");
    yylval.op = *yytext; 
    return TOKEN_OPERATOR;
}

[-]?[0-9]+    {
    printf("TOKEN_VALUE");
    printf("\n");
    yylval.val = atoi(yytext); 
    return TOKEN_VALUE;
 }

#.*         {
    printf("COMMENT");
    printf("\n");
    /*comment*/
}

.           { printf("%s", unrecognizedToken); }
Run Code Online (Sandbox Code Playgroud)

这是 parser.y 文件:

%error-verbose
%{
#define YYDEBUG 1
%}

%union {
    int val;
    double dval;
    char op;
    char* name;
}

%token TOKEN_BEGIN TOKEN_END TOKEN_WHILE TOKEN_DO TOKEN_POINT TOKEN_OPERATOR TOKEN_VALUE TOKEN_IDENTIFIER TOKEN_DOUBLE 
%start program

%{
void yyerror(const char* const message);

%}

%%

program: statement';';

block: TOKEN_BEGIN statements TOKEN_END { printf("rule block\n"); };

statements:
      statement';' statements { printf("rule statements\n"); }
    |;

statement: 
    | assignment
    | command
    | whileStmt
    | block;

assignment: TOKEN_IDENTIFIER '=' TOKEN_VALUE {
    printf("rule Assignment\n"); 
} ;

whileStmt: TOKEN_WHILE condition TOKEN_DO block {printf("rule While\n");};

condition: TOKEN_IDENTIFIER { printf("rule token_identifier\n"); }
    | TOKEN_VALUE { printf("rule token_value\n"); }
    | condition TOKEN_OPERATOR condition { printf("rule condition TOKEN_OPERATOR condition\n"); };

command: TOKEN_POINT TOKEN_DOUBLE TOKEN_DOUBLE { printf("rule Command\n"); };

%%

#include <stdlib.h>

void yyerror(const char* const message)
{
    printf("Parse error:%s\n", message);
    exit(1);
}

int main()
{
    yyparse();
}
Run Code Online (Sandbox Code Playgroud)

但是我收到以下错误消息:

Parse error:syntax error, unexpected $end, expecting ';'
Run Code Online (Sandbox Code Playgroud)

编译成这样:

 flex -o lexer.c lexer.l
 bison -v -d -o parser.c parser.y
 gcc parser.c lexer.c -o parser -g -DYYDEBUG=1
Run Code Online (Sandbox Code Playgroud)

运行解析器:

./parser < example
Run Code Online (Sandbox Code Playgroud)

你能帮我找出问题所在吗?为什么语法不能接受上面的例子作为输入?

Jon*_*ler 3

你的问题出在你的词法分析器中(与你的语法是否有问题无关 \xe2\x80\x94 我没有分析语法,因为我首先遇到了词法分析器中的问题,这就足够了以防止语法工作)。

\n\n

我添加了一个main()测试lexer.l

\n\n
%%\n\nYYSTYPE yylval;\n\nint main(void)\n{\n    int token;\n    while ((token = yylex()) != 0)\n        printf("Token: %d (%s)\\n", token, yytext);\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后我在您的示例代码上运行它以查看令牌流是否正确生成。我得到的输出是:

\n\n
TOKEN_BEGIN\nToken: 258 (begin)\nWhiteSpaces\nCOMMENT\nNewLine\nWhiteSpaces\nTOKEN_IDENTIFIER\nToken: 265 (x)\nWhiteSpaces\nyytext = =\nToken: 61 (=)\nWhiteSpaces\nTOKEN_VALUE\nToken: 264 (56)\nyytext = ;\nToken: 59 (;)\nNewLine\nNewLine\nWhiteSpaces\nWhile\nToken: 260 (while)\nWhiteSpaces\nTOKEN_IDENTIFIER\nToken: 265 (x)\nWhiteSpaces\nTOKEN_OPERATOR\nToken: 263 (>)\nWhiteSpaces\nTOKEN_VALUE\nToken: 264 (0)\nWhiteSpaces\nDO\nToken: 261 (do)\nNewLine\nWhiteSpaces\nTOKEN_BEGIN\nToken: 258 (begin)\nNewLine\nWhiteSpaces\nPOINT_START\nToken: 262 (point)\nWhiteSpaces\nPOINT_DEFINITIONS 15.6\nToken: 266 (15.6)\nWhiteSpaces\nPOINT_DEFINITIONS 78.96\nToken: 266 (78.96)\n;\nWhiteSpaces\nend;\nend;\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如您所看到的,最后返回到主程序的令牌是 78.96。

\n\n

当您识别出一个 POINT 时,您就启动了 POINT_DEFINITIONS 状态。然而,一旦处于这种状态,你就永远处于这种状态;你永远不会回到初始状态。您可能需要向 POINT_DEFINITIONS 启动状态添加一条规则来识别分号并执行BEGIN INITIAL;

\n\n
<POINT_DEFINITIONS>{Semicolon} {\n    printf("Semicolon in POINT_DEFINITION state\\n");\n    BEGIN INITIAL;\n    return *yytext;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

完成此操作后,输出的尾部为:

\n\n
...\nTOKEN_BEGIN\nToken: 258 (begin)\nNewLine\nWhiteSpaces\nPOINT_START\nToken: 262 (point)\nWhiteSpaces\nPOINT_DEFINITIONS 15.6\nToken: 266 (15.6)\nWhiteSpaces\nPOINT_DEFINITIONS 78.96\nToken: 266 (78.96)\nSemicolon in POINT_DEFINITION state\nToken: 59 (;)\nNewLine\nWhiteSpaces\nTOKEN_END\nToken: 259 (end)\npunctuation: yytext = ;\nToken: 59 (;)\nNewLine\nTOKEN_END\nToken: 259 (end)\npunctuation: yytext = ;\nToken: 59 (;)\nNewLine\n
Run Code Online (Sandbox Code Playgroud)\n

  • 我编译了语法以获得“parser.h”。存在一次转变/减少冲突。我从未将词法分析器与语法集成,因为我的第一次检查(词法分析器是否行为不当)是正确的起点。由于语法从未从词法分析器收到最后五个标记(分号、结束、分号、结束、分号),因此当它收到 EOF 并且这五个标记丢失时,它会抱怨也就不足为奇了。IOW,只要正确处理了移位/归约冲突,我就没有理由认为语法存在问题。我已经澄清了我的答案的介绍。 (2认同)