обработка ошибок в YACC - PullRequest
3 голосов
/ 21 марта 2012

привет, я пытаюсь сделать простой парсер и использую lex и yacc. дело в том, что я хочу напечатать свои собственные сообщения об ошибках, а не символ error, используемый yacc, который печатает syntax error. например это мой код yacc;

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

%token INTRSW IDENTIFIER INTEGER ASSIGNOP SEMICOLON DOUBLEVAL DOUBLERSW COMMA 
%token IF ELSE WHILE FOR
%token CLOSE_BRA OPEN_BRA CLOSE_PARA OPEN_PARA EQ LE GE
%token SUM MINUS MULTIP DIV

%left OPEN_BRA OPEN_PARA
%left MULTIP DIV
%left SUM MINUS

%union 
{
        int number;
        char* string;
}

%token <number> INTEGER
%token <string> IDENTIFIER

%%
program: 
    statement_list
    ;

statement_list:
        statement_list statement
        |
        statement
        ;

statement:
    if_statement OPEN_BRA statement_list CLOSE_BRA
    |
    if_statement 
    |
    assignment_block
    |
    single_assignment 
    ;

if_statement:
    IF OPEN_PARA condition_statement CLOSE_PARA
    ;

condition_statement:
    logical_expression
    ;

logical_expression:
    expression EQ expression
    |
    expression LE expression
    |
    expression GE expression
    ;

expression:
    double
    |
    IDENTIFIER
    |
    OPEN_PARA expression CLOSE_PARA
    |
    expression MULTIP expression
    |
    expression DIV expression
    |
    expression SUM expression
    |
    expression MINUS expression
    ;

assignment_block:
    integer_assignment_block
    | 
    double_assignment_block
    ;

integer_assignment_block:
    INTRSW integer_assignment_list SEMICOLON
    ;

double_assignment_block:
    DOUBLERSW double_assignment_list SEMICOLON
    ;

integer_assignment_list:
    integer_assignment
    |
    integer_assignment_list COMMA integer_assignment
    ;

double_assignment_list:
    double_assignment
    |
    double_assignment_list COMMA double_assignment
    ;

single_assignment:
    IDENTIFIER ASSIGNOP double SEMICOLON
    |
    IDENTIFIER ASSIGNOP IDENTIFIER SEMICOLON
    |
    error ';' { printf("You made en error"); }  
    ;

integer_assignment:
    IDENTIFIER ASSIGNOP INTEGER
    |
    IDENTIFIER
    ;

double_assignment:
    IDENTIFIER ASSIGNOP double
    |
    IDENTIFIER
    ;

double:
    DOUBLEVAL
    |
    INTEGER 
    ;

%%
void yyerror(char *s){
    fprintf(stderr,"%s Error at line: %d\n",s, 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;
}

так что в этот блок я добавляю ошибку.

single_assignment:
    IDENTIFIER ASSIGNOP double SEMICOLON
    |
    IDENTIFIER ASSIGNOP IDENTIFIER SEMICOLON
    |
    error ';' { printf("You made en error"); }  
    ;

поэтому я пишу a = 7 (без ';') в строке, которая просто говорит syntax error Error at line:7. так где же мое сообщение об ошибке_? Я буду очень очень рад, если вы сможете помочь мне по этому вопросу. и все равно спасибо

1 Ответ

5 голосов
/ 21 марта 2012

, поэтому я пишу a = 7 (без ';') в строке, это просто говорит синтаксическая ошибка Ошибка в строке: 7.так где же мое сообщение об ошибке _?

Ваше действие error говорит Yacc сбросить токены, пока он не найдет ';'маркер.Пока это не произойдет, оно не может уменьшиться по этому правилу.

Кроме того, вы должны выполнить yyerrok; где-то в теле правила, чтобы сообщить парсеру, что восстановление выполнено.При желании вы также можете использовать yyclearin;, чтобы также выбросить токен, вызвавший ошибку.Делать это сложно, потому что вы предполагаете, что токен неуместен.На самом деле это может быть правильный токен, но что-то еще, прежде чем его не будет!Например, вы видите точку с запятой, потому что закрывающая скобка была опущена и т. Д.

Действия по ошибке не будут заменять поведение вызова yyerror.Когда возникает синтаксическая ошибка, синтаксический анализатор вызовет yyerror с сообщением (обычно «синтаксическая ошибка»), а затем рассматриваются ошибки производства.Создание ошибок не является чем-то вроде «пользовательских переопределений синтаксических ошибок».(Похоже, вы ожидаете, что «синтаксическая ошибка» будет заменена вашим собственным общим сообщением об ошибке «Вы допустили ошибку».)

Если вы создаете ошибку, если вы можете догадаться о ее природе,Вы можете распечатать дополнительную диагностику, которая более полезна.

Одна полезная вещь - это переменная yychar, которая сообщает вам жетон предпросмотра.Вы можете проверить это в правиле устранения ошибок и попытаться угадать, что пошло не так, основываясь на его значении.Вы можете проверить это не только для своих собственных типов токенов, но и для значения YYEOF, которое указывает, что синтаксическая ошибка происходит из-за достижения не какого-то плохого токена, а конца ввода.

Я написал парсер вкоторый в некоторых случаях с ошибками я просто взял токен из yychar, преобразовал его в описательное имя и распечатал сообщение «неожиданно в».Это лучше, чем ничего;он сообщает пользователю, на каком токене синтаксис отличается от ожидаемого.

Кроме того, синтаксические анализаторы yacc могут производить диагностику даже при правильном разборе!(Очевидно; например, как еще вы реализовали бы предупреждения для языка.) В основном вам нужна некоторая центральная функция сообщения об ошибках, которую вы можете вызывать самостоятельно и которая также будет вызываться yyerror.Эта функция должна установить флаг, указывающий, произошли ли фатальные ошибки (или вести подсчет количества ошибок, предупреждений и т. Д.).Например, вы можете выбросить дерево синтаксического анализа и сохранить программу с состоянием сбоя завершения, если были фатальные ошибки, даже если синтаксический анализатор восстановился после любых синтаксических ошибок.

...