编译并发 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 确实是未定义的;一种可能是错误被默默地忽略了。)
由于 yylval
是 YYSTYPE
的实例,而不是指向 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
是正确的。
备注
出于某种原因,可重入 bison 解析器的使用被错误地称为“并发 YACC”。这在两个方面是错误的:首先,生成的解析器不是并发的(尽管因为它是可重入的,如果操作不引入竞争条件,它可以并发使用),其次因为 YACC 中没有该功能;这是一个 bison
扩展。
快速 Google 搜索揭示了短语“Concurrent YACC”的两种用法。其中一个在 entry in ESR's blog 的评论中,描述了他在几十年前,在 bison
存在之前编写的工具,用于使 yacc
解析器可重入。另一个是普纳大学开设的并发编程课程的三年级编程作业,其中使用了短语“Concurrent YACC”,好像它是有意义的。
我猜这个问题源自其中的第二个问题,这可能意味着课程作业包括对含义的解释。但就其价值而言,ESR 确实概述了将可重入 bison 解析器正确桥接到可重入 flex 词法分析器所涉及的步骤。所以我建议你看看它,虽然我不赞同 ESR 对 %bison-bridge
的描述是有问题的。 (如果他说“记录不当的杂乱无章”,我会 100% 站在一边。)
我正在尝试使用 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 确实是未定义的;一种可能是错误被默默地忽略了。)
由于 yylval
是 YYSTYPE
的实例,而不是指向 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
是正确的。
备注
出于某种原因,可重入 bison 解析器的使用被错误地称为“并发 YACC”。这在两个方面是错误的:首先,生成的解析器不是并发的(尽管因为它是可重入的,如果操作不引入竞争条件,它可以并发使用),其次因为 YACC 中没有该功能;这是一个
bison
扩展。快速 Google 搜索揭示了短语“Concurrent YACC”的两种用法。其中一个在 entry in ESR's blog 的评论中,描述了他在几十年前,在
bison
存在之前编写的工具,用于使yacc
解析器可重入。另一个是普纳大学开设的并发编程课程的三年级编程作业,其中使用了短语“Concurrent YACC”,好像它是有意义的。我猜这个问题源自其中的第二个问题,这可能意味着课程作业包括对含义的解释。但就其价值而言,ESR 确实概述了将可重入 bison 解析器正确桥接到可重入 flex 词法分析器所涉及的步骤。所以我建议你看看它,虽然我不赞同 ESR 对
%bison-bridge
的描述是有问题的。 (如果他说“记录不当的杂乱无章”,我会 100% 站在一边。)