如果我使用 Flex/Bison (Lex/Yacc) 更改 2 行的顺序,则打印到输出文件不起作用

Printing to output file does not work if I change the order of 2 lines with Flex/Bison (Lex/Yacc)

我正在尝试为一种简单的格式化语言构建一个编译器,但我现在想做的是在 Bison 识别时向输出文件打印一条消息

beginDocument docProperties endDocument

我将首先展示一个 .y 文件,其中的功能与预期的一样。但是,我将仅更改 2 行代码的顺序并打印到输出文件不再有效!

这是工作正常的 .y 文件:

%{
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "y.tab.h"
    void yyerror(const char *);
    int yylex(void);

    /* "Connect" with the output file  */
    extern FILE *yyout;
    extern int  yylineno;

    /* Integers related to user-defined document properties */
    int tabSize, linesPerPage, charsPerLine, linesPrinted, pageNumber = 1;

    char* title; /* char* title, author, date; is wrong. In C that would have been char *title, *author, *date because typically in C it's int *p while in C++ it's int* p */
    char* author; 
    char* date;

    /* An array with counters of how many times each of the 5 document properties appears in the input file. The order of the properties is defined in the enum below */
    int docPropertyCounters[5];

    /* An enumerated list with the 5 document properties */
    typedef enum {PAGE_SETUP, TAB_SIZE, DOC_TITLE, DOC_AUTHOR, DOC_DATE} document_property;

    /* Takes a document_property enum (basically an integer) and returns the corresponding document property as a string. The order is based on the enum. */
    static inline char *stringFromDocPropertyEnum(const document_property indexOfProperty) {
        static char *strings[] = { "\pagesetup{}", "\tabsize()", "\title{}", "\author{}", "\date{}"};
        return strings[indexOfProperty];
    }

    /* Checks for all possible errors in document properties */
    void dealWithDocPropertyErrors() {
        for (int i = 0; i < sizeof(docPropertyCounters)/sizeof(docPropertyCounters[0]); i++) {
            if (docPropertyCounters[i] < 1) 
                /* yyerror() is not used in this function because the line number does not need to be shown */
                fprintf(stderr, "SYNTAX ERROR: Your source file does not contain the required document property %s", stringFromDocPropertyEnum(i)); 
            else if (docPropertyCounters[i] > 1) 
                fprintf(stderr, "SYNTAX ERROR: Your source file contains more than one instance of the document property %s", stringFromDocPropertyEnum(i));
        }
        exit(-1);
    }

    /* Returns the string it takes as an argument excluding the first and last characters */
    char* removeFirstAndLastChar(char* string) {
        string = string + 1; // Removes the first character
        int i = 0;
        for (; string[i] != '[=13=]'; i++);
        string[i - 1] = '[=13=]';
        return string;
    }
%}

%union { 
    int iValue;      /* integer value */ 
    char* sValue;    /* C-String */ 
}; 

%error-verbose /* sometimes provides better error reporting. Useful sometimes when debugging */

%start file /* defining the start condition */

%token BSLASH LBRACE RBRACE LPAREN RPAREN COMMA

%token BEGIN_ END DOCUMENT /* BEGIN seems to be a reserved word so BEGIN_ was used instead */

%token PAGESETUP TABSIZE TITLE AUTHOR DATE

%token <iValue> INTEGER

%token <sValue> DDMMYYYYDATE STRING

%%

file: beginDocument docProperties endDocument
            { 
                fprintf(yyout, "Message");
                dealWithDocPropertyErrors();
            }
          | /* An empty document is parsed to an empty document, no errors generated */
          ;

beginDocument: BSLASH BEGIN_ LBRACE DOCUMENT RBRACE;

docProperties: docProperties docProperty
               | /* empty */
               ;                

    /* required properties... there should be one instance of each in the input file */ 
docProperty:    pageSetupProperty { docPropertyCounters[PAGE_SETUP]++; }
                | tabSizeProperty { docPropertyCounters[TAB_SIZE]++; }
                | titleProperty   { docPropertyCounters[DOC_TITLE]++; }
                | authorProperty  { docPropertyCounters[DOC_AUTHOR]++; }
                | dateProperty    { docPropertyCounters[DOC_DATE]++; }
                ;   

pageSetupProperty: BSLASH PAGESETUP LBRACE INTEGER COMMA INTEGER RBRACE
                   {
                       linesPerPage = ;
                       charsPerLine = ;
                   }
                   ;

tabSizeProperty: BSLASH TABSIZE LPAREN INTEGER RPAREN
                 {
                    tabSize = ;
                 }
                 ;

titleProperty: BSLASH TITLE LBRACE STRING RBRACE
               {
                   /*  is copied into title excluding the quotation marks at the beginning and end of the string */
                   title = removeFirstAndLastChar(); /*  is a "pseudo-variable" and can be passed in like this */
               }
               ;

authorProperty: BSLASH AUTHOR LBRACE STRING RBRACE
                {
                    author = removeFirstAndLastChar();
                }
                ;

dateProperty: BSLASH DATE LBRACE DDMMYYYYDATE RBRACE
              {
                  date = removeFirstAndLastChar();
              }
              ;

endDocument: BSLASH END LBRACE DOCUMENT RBRACE
             {
                // Freeing memory created by strdup()
                free(title);
                free(author);
                free(date);
             }
             ;
%%

int yywrap(void) {
    return 1;
}

void yyerror(const char* str) 
{
    fprintf(stderr,"SYNTAX ERROR near line [%d]: %s\n", yylineno, str);
}

以此为输入文件,

\begin {document}

\tabsize( 5)
\pagesetup{30,100 }
\title{"Why I Love Compiler Design"}
\author{"COMP421 Student"}
\date{29/12/2016}

\end{document}

我得到这个输出:

Message

这正是我所期望的。

假设我想在处理文档属性错误后打印消息,我把打印消息的行和它后面的一行的顺序改一下,也就是我做的:

dealWithDocPropertyErrors();
fprintf(yyout, "Message");

我现在得到一个相同输入的空输出文件。我在编译期间没有得到任何 warnings/errors。

我正在使用 Windows 10 和 Flex 2.5.4a。我的 .l 文件在下面

%{
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "y.tab.h"
    void yyerror(const char *);
    int yylex(void);
    extern FILE *yyout; /* "Connect" with the output file  */
    extern int  yyparse();
%}
/* %option noyywrap;     Tells flex not to declare the function yywrap() which is only useful in the case of more than 1 input file */
    /* Allows printing the line number (of an error) */
%option yylineno
    /* Catches some errors */
%option nodefault
    /* Tells flex not to generate code for the input() and unput() functions which I will not be using */
%option nounput
%option noinput
    /* Prints the tokens flex recognizes to the console. Useful when debugging and avoids having to write printf() statements for that */
%option debug

%%

^\ { return BSLASH; } /* every backslash has to be at the start of a line */
\{  { return LBRACE; }
\}  { return RBRACE; }
\(  { return LPAREN; }
\)  { return RPAREN; }
,   { return COMMA; }

begin    { return BEGIN_; }
end      { return END; }
document { return DOCUMENT; }

pagesetup { return PAGESETUP; }
tabsize   { return TABSIZE; }
title     { return TITLE; }
author    { return AUTHOR; }
date      { return DATE; }

(((0[1-9]|[12][0-9]|30)[-/ ]?(0[13-9]|1[012])|31[-/ ]?(0[13578]|1[02])|(0[1-9]|1[0-9]|2[0-8])[-/ ]?02)[-/ ]?[0-9]{4}|29[-/ ]?02[-/ ]?([0-9]{2}(([2468][048]|[02468][48])|[13579][26])|([13579][26]|[02468][048]|0[0-9]|1[0-6])00))  { yylval.sValue = strdup(yytext); return DDMMYYYYDATE; }
[0-9]*[1-9][0-9]*   { yylval.iValue = atoi(strdup(yytext)); return INTEGER; } /* strdup() is string duplicate. yytext must be copied because of its temporary nature */ 
\".*\"              { yylval.sValue = strdup(yytext); return STRING; } 

    /* skip whitespace which is not part of a string. [ \t\r\n]+ is better than [ \t\r\n] performance-wise */
[ \t\r\n]+ ;

    /* anything else is an error */
. yyerror("invalid character");

%%

int main(int argc, char *argv[]) {
    if ( argc != 3)
        yyerror("ERROR You need 2 args: inputFileName outputFileName");
    else {
        yyin = fopen(argv[1], "r");
        yyout = fopen(argv[2], "w");
        yyparse();
        fclose(yyin);
        fclose(yyout);
    }

    return 0;
}

这不是 lex/yacc 问题。

函数dealWithDocPropertyErrors()调用C库函数exit(),终止进程,永不returns。