【问题标题】:can't find a simple error when parsing with YACC使用 YACC 解析时找不到简单的错误
【发布时间】:2012-03-24 22:27:39
【问题描述】:

我正在尝试在 Pascal 语言上制作一个非常简单的 YACC 解析器,它只包括整数声明、一些基本表达式和 if-else 语句。但是,我几个小时都找不到错误,我很快就会发疯。终端说Error at line:0,但这是不可能的!我将 flex 和 byacc 用于解析器。如果您能帮助我,我将非常高兴。如您所见,这是我的 lex 文件;

%{
#include <stdio.h>
#include <string.h>
#include "y.tab.h"
extern int yylval;
int linenum=0;
%}

digit   [0-9]
letter  [A-Za-z]

%%
if              return IF;
then                return THEN;
else                return ELSE;
for             return FOR;
while               return WHILE;
PROGRAM             return PROGRAM_SYM;
BEGIN               return BEGIN_SYM;
VAR             return VAR_SYM;
END             return END_SYM;
INTEGER             return INTEGER_SYM;
{letter}({letter}|{digit})* return identifier;
[0-9]+              return NUMBER;
[\<][\=]            return CON_LE;
[\>][\=]            return CON_GE;
[\=]                return CON_EQ;          
[\:][\=]            return ASSIGNOP;
;               return semiColon;
,               return comma;
\n              {linenum++;}
.               return (int) yytext[0];
%%

这是我的 Yacc 文件

%{
#include <stdio.h>
#include <string.h>
#include "y.tab.h"
extern FILE *yyin;
extern int linenum;
%}

%token PROGRAM_SYM VAR_SYM BEGIN_SYM END_SYM INTEGER_SYM NUMBER
%token identifier INTEGER ASSIGNOP semiColon comma THEN
%token IF ELSE FOR WHILE
%token CON_EQ CON_LE CON_GE GE LE

%left '*' '/'
%left '+' '-'

%start program

%%

program: PROGRAM_SYM identifier semiColon VAR_SYM dec_block BEGIN_SYM statement_list END_SYM '.'
     ;

dec_block:
        dec_list semiColon;

dec_list:
        dec_list dec
        |
        dec
        ;

dec: 
        int_dec_list
        ;

int_dec_list:   
        int_dec_list int_dec ':' type
        |
        int_dec ':' type
        ;

int_dec:
        int_dec comma identifier
        |
        identifier
        ;

type:
    INTEGER_SYM
    ;

statement_list:
        statement_list statement
        |
        statement
        ;

statement:
        assignment_list
        |
        expression_list
        |
        selection_list
        ;

assignment_list:
        assignment_list assignment
        |
        assignment      
        ;

assignment:

        identifier ASSIGNOP expression_list
        ;

expression_list:
        expression_list expression semiColon
        |
        expression semiColon
        ;


expression:
        '(' expression ')'
        |
        expression '*' expression
        |
        expression '/' expression
        |
        expression '+' expression
        |
        expression '-' expression
        |
        factor
        ;

factor:     
        identifier
        |
        NUMBER
        ;


selection_list:
        selection_list selection
        |
        selection
        ;

selection:
        IF '(' logical_expression ')' THEN statement_list ELSE statement_list
        ;


logical_expression:
        logical_expression '=' expression
        |
        logical_expression '>' expression
        |
        logical_expression '<' expression
        ;


%%
void yyerror(char *s){
    fprintf(stderr,"Error at line: %d\n",linenum);
}
int yywrap(){
    return 1;
}
int main(int argc, char *argv[])
{
    /* Call the lexer, then quit. */
    yyin=fopen(argv[1],"r");
    yyparse();
    fclose(yyin);
    return 0;
}

最后我在输入时在第一行出现错误;

PROGRAM myprogram;

VAR

i:INTEGER;

i3:INTEGER;

j:INTEGER;

BEGIN

i := 3;

j := 5;

i3 := i+j*2;

i := j*20;

if(i>j)

then i3 := i+50+(45*i+(40*j));

else i3 := i+50+(45*i+(40*j))+i+50+(45*i+(30*j));

END.

【问题讨论】:

  • 您使用的是哪个版本的 Yacc?当我使用bison 编译您的代码时,我会收到有关grammar.y: warning: 3 useless nonterminals and 7 useless rulesgrammar.y: conflicts: 8 shift/reduce 的警告(以及介于两者之间的特定错误消息)。您是否打算在运行程序之前修复其中的任何一个? (FWIW:当我不顾警告运行程序时,我会收到与您相同的错误消息。)
  • 它应该是byacc,即使我遇到了 13 个移位/减少冲突,它们也不应该成为问题,因为我说它由非常简单的语法组成,顺便说一下 Error at line 0。我什至尝试写树形但无法意识到问题
  • 它太愚蠢了,它在第 0 行给出了错误!!我试图解决这个问题几个小时,但没有任何改善......
  • 请注意,发布的示例不是有效的 Pascal。在这种情况下,一个“;”在 else 之前是不允许的,所以如果它被解析为正确,则解析器是错误的。这是 Pascal 对悬空 else 问题的解决方案/破解的一部分

标签: parsing pascal yacc lex parser-generator


【解决方案1】:

对于调试语法,YYDEBUG 是您的朋友。要么将#define YYDEBUG 1 粘贴在.y 文件顶部的%{..%} 中,要么使用-DYYDEBUG 编译,然后在调用main 之前粘贴yydebug = 1;main 中,您将获取有关解析器正在查看哪些令牌以及它正在对它们做什么的大量信息......

【讨论】:

  • 是的,这是实现解析器功能的另一种方法!非常感谢
【解决方案2】:

您的词法分析器将空格和制表符作为标记返回,但语法无法识别它们。

添加解析器规则:

[ \t\r]    { }

这会让您在遇到错误之前转到第 6 行而不是第 0 行。您收到该错误是因为您不允许在声明之间使用分号:

dec_block:
        dec_list semiColon;

dec_list:
        dec_list dec
        |
        dec
        ;

dec:
        int_dec_list
        ;

应该是这样的:

dec_block:
        dec_block dec
        |
        dec
        ;

dec:
        int_dec_list semiColon
        ;

这样做会让你进入输入的第 14 行。

顺便说一句,我做的第一件事就是确保词法分析器告诉我它在做什么,方法是修改如下规则:

if              { printf("IF\n"); return IF; }

在长期代码中,我会在运行时选择诊断输出。


您在期望分号的位置有一个普遍的问题。还不清楚您是否应该在 statement 的规则中允许 expression_list (或者,也许,'还没有' - 当你有函数调用时这可能是合适的,但允许 3 + 2 / 4 作为'声明'不是很有帮助)。


这个语法到达输入的末尾:

%{
#include <stdio.h>
#include <string.h>
#include "y.tab.h"
extern FILE *yyin;
extern int linenum;
%}

%token PROGRAM_SYM VAR_SYM BEGIN_SYM END_SYM INTEGER_SYM NUMBER
%token identifier INTEGER ASSIGNOP semiColon comma THEN
%token IF ELSE FOR WHILE
%token CON_EQ CON_LE CON_GE GE LE

%left '*' '/'
%left '+' '-'

%start program

%%

program: PROGRAM_SYM identifier semiColon VAR_SYM dec_block BEGIN_SYM statement_list END_SYM '.'
     ;

dec_block:
        dec_block dec
        |
        dec
        ;

dec:
        int_dec_list semiColon
        ;

int_dec_list:
        int_dec_list int_dec ':' type
        |
        int_dec ':' type
        ;

int_dec:
        int_dec comma identifier
        |
        identifier
        ;

type:
    INTEGER_SYM
    ;

statement_list:
        statement_list statement
        |
        statement
        ;

statement:
        assignment
        |
        selection
        ;

assignment:
        identifier ASSIGNOP expression semiColon
        ;

expression:
        '(' expression ')'
        |
        expression '*' expression
        |
        expression '/' expression
        |
        expression '+' expression
        |
        expression '-' expression
        |
        factor
        ;

factor:
        identifier
        |
        NUMBER
        ;

selection:
        IF '(' logical_expression ')' THEN statement_list ELSE statement_list
        ;

logical_expression:
        expression '=' expression
        |
        expression '>' expression
        |
        expression '<' expression
        ;

%%
void yyerror(char *s){
    fprintf(stderr,"Error at line: %d\n",linenum);
}
int yywrap(){
    return 1;
}
int main(int argc, char *argv[])
{
    /* Call the lexer, then quit. */
    yyin=fopen(argv[1],"r");
    yyparse();
    fclose(yyin);
    return 0;
}

主要更改包括删除assignment_listexpression_list,并修改logical_expression,使扩展的两侧为expression,而不是LHS为logical_expression(当时从未有原始定义,导致出现警告问题)。

仍有问题需要解决; selection 中的 expression_list 应该更加严格,以准确反映 Pascal 的语法。 (您需要一个可以是单个语句或 BEGIN、语句列表、END 的块。)

【讨论】:

  • 是的,这段代码更令人满意,但你确定它显示在 6_ 处吗?导致它在我的终端上显示为 3?
  • 好的,现在它显示在 7 处,我将添加这些 printf 函数。你是正确的,这种方式会更有帮助
  • 正如我所说,我只是从一个简单的例子开始。所以这段代码会随时间改变(例如,如果我尝试解析函数调用)。而且我尝试了那些毫无意义的陈述来检查它是否给出错误。但即使没有警告,编译器也会继续编译。这就是为什么我让简单的计算作为陈述
  • 我终于解决了这个问题。它只是半冒号,我把它们放在错误的地方来解析,我意识到在logical_expression它不能到达非终端我只是在它的末尾添加表达式。并使用 printf 函数来查看将哪些令牌作为 YACC 的参数。所以非常感谢乔纳森的关心!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-21
  • 1970-01-01
相关资源
最近更新 更多