编译并发 YACC 程序时出错

Error while compiling Concurrent YACC program

我正在尝试使用 Concurrent YACC 构建一个基本计算器。我已经通过静态创建线程来尝试代码。但是当我想动态指定要创建多少个线程时,解析器似乎有问题。这是我的代码的内容。

aa.y 文件

%{
#include <stdio.h>
#include <pthread.h>
#include <string.h>
void * scanner;
FILE *yyin;
#define YYSTYPE int
%}

%token digit
%lex-param {void * scanner}
%parse-param {void * scanner}
%start list
%token NUMBER
%left '+' '-'
%left '*' '/' '%'
%left UMINUS 
%union {int i;}
%%

list:                      
    |
    list stat '\n'
    |
    list error '\n'{ yyerrok; }
    ;

stat:   expr { printf("Thread = %d ... Ans = %d\n",pthread_self(),);}
    ;

expr:   '(' expr ')'{ $$ = ; }
    |
    expr '*' expr { $$ =  * ; }
    |
    expr '/' expr { $$ =  / ; }
    |
    expr '+' expr { $$ =  + ; }
    |
    expr '-' expr { $$ =  - ; }
    |
    '-' expr %prec UMINUS { $$ = -; }
    |
    NUMBER
    ;

%%

struct struct_arg
{
    unsigned char* file;
};

int yyerror()
{
    return 1;
}

void *parse(void *arguments)
{
    struct struct_arg *args = (struct struct_arg *)arguments;
    unsigned char* filename;
    filename = args -> file;
    yyin = fopen(filename,"r+");
    if(yyin == NULL)
   {

   }
   else
   {
       yylex_init(&scanner);
       yyset_in(yyin,scanner);
       yyparse(scanner);
       yylex_destroy(scanner);
       printf("Thread = %d\n",pthread_self());
   }

   fclose(yyin);
}

int main(int argc, char *argv[])
{
    int num;
    printf("How many threads you want to create??\n");
    scanf("%d", &num);

    int error, count = 0;
    FILE *fp[num], *file_pointer;
    char line[256];
    size_t len = 0;
    char read;

    file_pointer = fopen("test.txt", "r");

    while (fgets(line, sizeof(line), file_pointer))
    {
        char file_name[32] = "test_";
        char dummy[4];
        char dummy2[5] = ".txt";
        sprintf(dummy, "%d", count);
        strcat(file_name, dummy);
        strcat(file_name, dummy2);
        fp[count] = fopen(file_name, "a");
        fprintf(fp[count], "%s", line);
        fclose(fp[count]);
        count++;
        if(count == num)
        {
            count = 0;
        }
    }

    struct struct_arg arguments[num];
    int i = 0;
    while(i < num)
    {
        char file_name[32] = "test_";
        char dummy[4];
        char dummy2[5] = ".txt";
        sprintf(dummy, "%d", i);
        strcat(file_name, dummy);
        strcat(file_name, dummy2); 
        arguments[i].file = file_name;
        i++;
    }

    pthread_t tid[num];
    int j = 0;
    while(j < num)
    {
        error = pthread_create(&(tid[j]), NULL, &parse, (void *) &arguments[j]);
        j++;
    }

    int n = 0;
    while(n < num)
    {
        pthread_join(tid[n], NULL);
        n++;
    }

    int temp, k = 0;
    while(k < num)
    {
        char file_name[32] = "test_";
        char dummy[4];
        char dummy2[5] = ".txt";
        sprintf(dummy, "%d", k);
        strcat(file_name, dummy);
        strcat(file_name, dummy2); 
        temp = remove(file_name);
        k++;
    }

    return 0;
}

aa.l

%{
#include <stdio.h>
#include "y.tab.h"
extern int scanner;
%}
%option reentrant
%option noyywrap
NUMBER      [0-9]+
%%

" "         ;
{NUMBER}    {
                yylval->i = atoi(yytext);
                return(NUMBER);
            }
[^0-9\b]    {
                return(yytext[0]);
            }

我的编译步骤是

yacc -d aa.y
lex aa.l
cc lex.yy.c y.tab.c -o aa.exe -pthread

产生的错误是

aa.l: In function 'yylex':
aa.l:13:23: error: invalid type argument of '->' (have 'YYSTYPE')
            yylval->i = atoi(yytext);

谁能指出我做错了什么??

这是一个简单的编译器错误,它(间接)是您未请求可重入(“纯”)野牛解析器的结果。 [注1]

由于解析器不可重入,它使用 YYSTYPE 类型的全局 yylval。您的 %union 声明将创建一个 YYSTYPE 作为联合类型的声明,该联合类型将被放置在生成的头文件 y.tab.h 中,它实际上看起来像这样(省略了一些不重要的细节):

#ifndef YYSTYPE
  typedef union yystype {
    int i;
  } YYSTYPE;
  extern YYSTYPE yylval;
#endif

该代码也将被放入 y.tab.c,但它将在 从您的 bison 定义的 %{...} 部分插入的 C 段之后。你 #define YYSTYPE int,结果在 y.tab.c yylval 中有类型 int,而在 `yy.lex.c 中,它是联合类型。那是未定义的行为 (UB),这就是你在 C 中说“错错错了”的方式。(但 UB 确实是未定义的;一种可能是错误被默默地忽略了。)

由于 yylvalYYSTYPE 的实例,而不是指向 YYSTYPE 的指针,因此引用成员 i 的正确方法是 yylval.i,而不是 yylval->i。因此编译器错误。

在您的 bison 文件中,您没有声明任何非终结符具有类型。由于您包含了 %union 声明,因此 bison 要求您告诉它使用其语义值的任何终端或非终端的类型(使用 </code>、<code> 等)或分配给($$)。因此,当您尝试通过 bison 传递文件时,您应该会收到一堆错误。另一方面,如果您声明了类型,那么 bison 生成的解析器将包含对 yylval.i 的引用,这也会产生编译器错误,因为您的 #define YYSTYPE 有效地绕过了联合声明。 (Bison 不知道 #define 因为它不解析包含的 C 代码。所以它不能生成错误消息。但它肯定是一个错误。)

如果您告诉 bison 生成一个可重入解析器,那么生成的解析器会调用 yylex 并带有一个 YYSTYPE* 类型的附加参数;如果您还在 flex 定义中提供了 %option bison-bridge,那么 flex 会生成一个 yylex 的声明,并带有一个 YYSTYPE* 类型的附加参数,它将成为 yylval 的值。在那种情况下,yylval 将是一个指针,而不是一个实例,并且 yylval->i 是正确的。


备注

  1. 出于某种原因,可重入 bison 解析器的使用被错误地称为“并发 YACC”。这在两个方面是错误的:首先,生成的解析器不是并发的(尽管因为它是可重入的,如果操作不引入竞争条件,它可以并发使用),其次因为 YACC 中没有该功能;这是一个 bison 扩展。

    快速 Google 搜索揭示了短语“Concurrent YACC”的两种用法。其中一个在 entry in ESR's blog 的评论中,描述了他在几十年前,在 bison 存在之前编写的工具,用于使 yacc 解析器可重入。另一个是普纳大学开设的并发编程课程的三年级编程作业,其中使用了短语“Concurrent YACC”,好像它是有意义的。

    我猜这个问题源自其中的第二个问题,这可能意味着课程作业包括对含义的解释。但就其价值而言,ESR 确实概述了将可重入 bison 解析器正确桥接到可重入 flex 词法分析器所涉及的步骤。所以我建议你看看它,虽然我不赞同 ESR 对 %bison-bridge 的描述是有问题的。 (如果他说“记录不当的杂乱无章”,我会 100% 站在一边。)