C 编译器 (GCC) 在使用 Flex 和 Bison 时给出了几个错误
C compiler (GCC) gives several errors when using Flex and Bison
我正在尝试为我设计的一种小型语言制作编译器。我试图将 Flex 用作词法分析器生成器,将 Bison 用作解析器生成器。我已经阅读了关于 Bison 的维基百科页面,以及关于让 Flex 和 Bison 很好地协同工作的 Whosebug 上的几篇文章。出于某种原因,我仍然遇到错误。
这是生成文件:
CC = gcc
CFLAGS = -O2 -Wall -Wextra -Wpedantic -lfl
INFILES = main.c Parser.c Lexer.c
OUTFILE = language
default: Lexer.c Parser.c
$(CC) $(CFLAGS) $(INFILES) -o $(OUTFILE)
Lexer.c: funlang.l
flex funlang.l
Parser.c: funlang.y Lexer.c
bison -d -Wcounterexamples funlang.y
clean:
rm Lexer.* Parser.* language
main.c:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "eval.h"
int main(int argc, char *argv[]){
if(argc < 2){
printf("%s error: No input file supplied\n", argv[0]);
return 0;
}
if(argc > 3){
printf("%s error: Too many arguments supplied\n", argv[0]);
return 0;
}
FILE *file = fopen(argv[1], "r");
if(file == NULL){
printf("Error opening file %s\n", argv[1]);
return 0;
}
fseek(file, 0, SEEK_END);
size_t input_length = (size_t) ftell(file);
rewind(file);
size_t bytes_read = 0;
char *input = (char *) malloc(sizeof(char) * input_length);
bytes_read = fread(input, 1, input_length, file);
if(bytes_read != input_length){
puts("Error reading file");
return 0;
}
fclose(file);
/* I'm still not doing anything with the parser or lexer yet, because I've not gotten them to work */
return 0;
}
eval.h:
#include "Parser.h"
#include "Lexer.h"
funlang.l:
%option outfile="Lexer.c" header-file="Lexer.h"
%option bison-bridge bison-locations never-interactive reentrant
%option warn nodefault nounistd yylineno noinput nounput
%option noyywrap batch
%{
#include <stdint.h>
#include <string.h>
#include "Parser.h"
#include "Lexer.h"
size_t line_count = 1;
%}
int_literal ((-)?((0x)[0-9A-Fa-f]+|[0-9]+))
str_literal ("[A-Za-z0-9 \t!#-&(-/:-@[-_{-~]*")
whitespace [ \t\r]
identifier ([A-Za-z])([A-Za-z0-9]+)
%%
"func" {return FUNCTION_KEYWORD; }
"if" {return IF; }
"else" {return ELSE; }
"int" {return INT_KEYWORD }
"str" {return STR_KEYWORD; }
"bool" {return BOOL_KEYWORD; }
"true" {return BOOL_LITERAL; }
"false" {return BOOL_LITERAL; }
{int_literal} {yylval.val = (intmax_t) strtol(yytext, (char **) NULL, 0);
return INT_LITERAL; }
{str_literal} {return STR_LITERAL; }
"->" {return GIVES_TYPE; }
{identifier} {yylval.name = strdup(yytext);
return IDENTIFIER; }
\n {line_count++; }
{whitespace}
. {printf("Error, unrecognized char at line #%li", line_count);
return OTHER; }
%%
和funlang.y:
%define api.pure full
%locations
%param {yyscan_t scanner}
%code top{
#include <stdio.h>
#include <stdint.h>
}
%code requires{
typedef void* yyscan_t;
}
%code{
int yylex(YYSTYPE* yylvalp, YYLTYPE* yyllocp, yyscan_t scanner);
}
%{
#include "Parser.h"
%}
%token FUNCTION_KEYWORD IF ELSE INT_KEYWORD STR_KEYWORD BOOL_KEYWORD
%token BOOL_LITERAL STR_LITERAL GIVES_TYPE OTHER
%token <val> INT_LITERAL
%token <name> IDENTIFIER
%output "Parser.c"
%defines "Parser.h"
%union{
char name[16];
intmax_t val;
};
%%
function: FUNCTION_KEYWORD IDENTIFIER '(' parameters ')' GIVES_TYPE INT_KEYWORD '{' statement '}'
| FUNCTION_KEYWORD IDENTIFIER '(' parameters ')' GIVES_TYPE STR_KEYWORD '{' statement '}'
| FUNCTION_KEYWORD IDENTIFIER '(' parameters ')' GIVES_TYPE BOOL_KEYWORD '{' statement '}';
parameters: parameter
| %empty;
parameter: INT_KEYWORD IDENTIFIER
| parameter ',' INT_KEYWORD IDENTIFIER
| STR_KEYWORD IDENTIFIER
| parameter ',' STR_KEYWORD IDENTIFIER
| BOOL_KEYWORD IDENTIFIER
| parameter ',' BOOL_KEYWORD IDENTIFIER;
call_parameters: parameter
| %empty;
call_parameter: IDENTIFIER
| parameter ',' IDENTIFIER;
array_list: INT_LITERAL
| IDENTIFIER
| function_call
| indexing_expression
| STR_LITERAL
| BOOL_LITERAL
| array_list ',' IDENTIFIER
| array_list ',' function_call
| array_list ',' indexing_expression
| array_list ',' INT_LITERAL
| array_list ',' STR_LITERAL
| array_list ',' BOOL_LITERAL;
var_definition: INT_KEYWORD IDENTIFIER '=' INT_LITERAL ';'
| INT_KEYWORD IDENTIFIER '=' math_expression ';'
| INT_KEYWORD IDENTIFIER '=' function_call ';'
| INT_KEYWORD IDENTIFIER ';'
| INT_KEYWORD indexing_expression '=' array_list ';'
| INT_KEYWORD indexing_expression ';'
| STR_KEYWORD IDENTIFIER '=' STR_LITERAL ';'
| STR_KEYWORD IDENTIFIER '=' function_call ';'
| STR_KEYWORD IDENTIFIER ';'
| STR_KEYWORD indexing_expression '=' array_list ';'
| STR_KEYWORD indexing_expression ';'
| BOOL_KEYWORD IDENTIFIER '=' BOOL_LITERAL ';'
| BOOL_KEYWORD IDENTIFIER '=' function_call ';'
| BOOL_KEYWORD IDENTIFIER ';'
| BOOL_KEYWORD indexing_expression '=' array_list ';'
| BOOL_KEYWORD indexing_expression ';';
function_call: IDENTIFIER '(' call_parameters ')';
boolean_statement: '(' IDENTIFIER "==" IDENTIFIER ')'
| '(' IDENTIFIER ')'
| '(' indexing_expression ')'
| '(' boolean_statement ')'
| '(' boolean_statement "&&" boolean_statement ')'
| '(' boolean_statement "||" boolean_statement ')'
| '(' '!' boolean_statement ')'
| '(' function_call ')';
BINARY_OPERATOR: '+'| '-' | '*' | '/' | '&' | '|';
math_expression: '(' BINARY_OPERATOR INT_LITERAL INT_LITERAL ')'
| '(' BINARY_OPERATOR INT_LITERAL IDENTIFIER ')'
| '(' BINARY_OPERATOR IDENTIFIER INT_LITERAL ')'
| '(' BINARY_OPERATOR INT_LITERAL function_call ')'
| '(' BINARY_OPERATOR function_call INT_LITERAL ')'
| '(' BINARY_OPERATOR function_call function_call ')'
| '(' BINARY_OPERATOR INT_LITERAL math_expression ')'
| '(' BINARY_OPERATOR math_expression INT_LITERAL ')'
| '(' BINARY_OPERATOR math_expression IDENTIFIER ')'
| '(' BINARY_OPERATOR IDENTIFIER math_expression ')'
| '(' BINARY_OPERATOR math_expression function_call ')'
| '(' BINARY_OPERATOR function_call math_expression ')'
| '(' BINARY_OPERATOR math_expression math_expression ')';
indexing_expression: IDENTIFIER '[' INT_LITERAL ']'
| IDENTIFIER '[' IDENTIFIER ']';
statement: IF boolean_statement '{' statement '}'
| ELSE '{' statement '}'
| "return" function_call ';'
| "return" IDENTIFIER ';'
| "return" INT_LITERAL ';'
| "return" STR_LITERAL ';'
| "return" BOOL_LITERAL ';'
| var_definition;
%%
我遇到的最大问题是,在编译所有内容时,C 编译器会在几个阶段指出 'yyin'、'yyout'、'yyleng' 和其他几个阶段未申报。例如,Lexer.c:800:10: error: ‘yyin’ undeclared (first use in this function)
如果我遗漏了任何重要信息,或者如果其他方面不清楚,我深表歉意。我会尽力澄清任何含糊之处。提前谢谢你
未定义的符号错误都是你输入的结果
#include "Lexer.h"
在您生成的词法分析器中。你绝不能那样做;词法分析器头信息已经包含在生成的词法分析器中,包含两次会导致错误。
同样,你不应该把
#include "Parser.h"
进入生成的解析器。同样,生成的解析器已经有了必要的定义,双重包含可能会导致问题。
我试图在 中显示可重入 bison/flex 项目的轮廓。该答案谨慎地仅使用必要的包含,并试图解释一些基本原理。
另请注意,yyerror
是编译成功所必需的。上面答案中的版本应该是一个好的开始。
补充说明
您的代码中还有一些其他错误。
在funlang.l
的第25行,少了一个;
在第 31 行和第 36 行的操作中,您需要考虑这一段(来自可重入解析器的链接答案):
All references to yylval in scanner actions also need to be modified, since bison's reentrant API passes pointers to the semantic value and location objects. If the semantic type is a union (normally produced by placing a %union
declaration in the bison source), then you'll need to change scanner actions which use yylval.tag
to yylval->tag
. Similarly, if you use a single semantic type, either the default type or one declared (in the bison source) with %define api.value.type, then you'll need to replace yylval = ...
with *yylval = ...
, as in the sample code above.
即使进行了该修复,yylval->name = strdup(yytext)
也无法正常工作,因为您的解析器的 %union
声明将 name
定义为固定长度的字符数组,并且您无法分配给数组.我强烈建议将 name
更改为键入 char*
;这将与您的词法扫描器中的使用兼容,并且如果有人恰好提供了长标识符,也不会导致缓冲区溢出。
字符串文字的正则表达式是错误的,因为它是带引号的字符串。在 (f)lex 中,模式可以包含带引号的字符串,这被证明是非常有用的,但遗憾的是在我所知道的任何正则表达式库中都没有实现。您需要将 "
转义为 \"
才能获得您要查找的内容。它也很难阅读;我建议使用否定字符 class 来显示您要排除的字符。但这只是一个建议。
您有 %option yylineno
可以灵活地为您跟踪行号。在我的 lexer.l
中有一个 YY_USER_ACTION
的定义,它使用它来填充位置对象,以便 bison 可以使用令牌位置。如果您使用该代码,您将不需要自己跟踪行号,因此您将不需要 line_no
变量或 \n
的规则,它们可以添加到您的空白规则中。
关于空白规则,flex——与 lex 不同——通常允许没有操作的规则,但这是不正确的,可能会导致问题,特别是在某些时候你使用不同的扫描器时发电机。规则必须有行动;如果你想让一个模式什么也不做,使用动作 ;
(这是通常的约定)或 {}
.
不需要像 main
那样将输入读入内存。 (f)lex 扫描器处理读取文件的所有细节,无论文件有多长。
我正在尝试为我设计的一种小型语言制作编译器。我试图将 Flex 用作词法分析器生成器,将 Bison 用作解析器生成器。我已经阅读了关于 Bison 的维基百科页面,以及关于让 Flex 和 Bison 很好地协同工作的 Whosebug 上的几篇文章。出于某种原因,我仍然遇到错误。 这是生成文件:
CC = gcc
CFLAGS = -O2 -Wall -Wextra -Wpedantic -lfl
INFILES = main.c Parser.c Lexer.c
OUTFILE = language
default: Lexer.c Parser.c
$(CC) $(CFLAGS) $(INFILES) -o $(OUTFILE)
Lexer.c: funlang.l
flex funlang.l
Parser.c: funlang.y Lexer.c
bison -d -Wcounterexamples funlang.y
clean:
rm Lexer.* Parser.* language
main.c:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "eval.h"
int main(int argc, char *argv[]){
if(argc < 2){
printf("%s error: No input file supplied\n", argv[0]);
return 0;
}
if(argc > 3){
printf("%s error: Too many arguments supplied\n", argv[0]);
return 0;
}
FILE *file = fopen(argv[1], "r");
if(file == NULL){
printf("Error opening file %s\n", argv[1]);
return 0;
}
fseek(file, 0, SEEK_END);
size_t input_length = (size_t) ftell(file);
rewind(file);
size_t bytes_read = 0;
char *input = (char *) malloc(sizeof(char) * input_length);
bytes_read = fread(input, 1, input_length, file);
if(bytes_read != input_length){
puts("Error reading file");
return 0;
}
fclose(file);
/* I'm still not doing anything with the parser or lexer yet, because I've not gotten them to work */
return 0;
}
eval.h:
#include "Parser.h"
#include "Lexer.h"
funlang.l:
%option outfile="Lexer.c" header-file="Lexer.h"
%option bison-bridge bison-locations never-interactive reentrant
%option warn nodefault nounistd yylineno noinput nounput
%option noyywrap batch
%{
#include <stdint.h>
#include <string.h>
#include "Parser.h"
#include "Lexer.h"
size_t line_count = 1;
%}
int_literal ((-)?((0x)[0-9A-Fa-f]+|[0-9]+))
str_literal ("[A-Za-z0-9 \t!#-&(-/:-@[-_{-~]*")
whitespace [ \t\r]
identifier ([A-Za-z])([A-Za-z0-9]+)
%%
"func" {return FUNCTION_KEYWORD; }
"if" {return IF; }
"else" {return ELSE; }
"int" {return INT_KEYWORD }
"str" {return STR_KEYWORD; }
"bool" {return BOOL_KEYWORD; }
"true" {return BOOL_LITERAL; }
"false" {return BOOL_LITERAL; }
{int_literal} {yylval.val = (intmax_t) strtol(yytext, (char **) NULL, 0);
return INT_LITERAL; }
{str_literal} {return STR_LITERAL; }
"->" {return GIVES_TYPE; }
{identifier} {yylval.name = strdup(yytext);
return IDENTIFIER; }
\n {line_count++; }
{whitespace}
. {printf("Error, unrecognized char at line #%li", line_count);
return OTHER; }
%%
和funlang.y:
%define api.pure full
%locations
%param {yyscan_t scanner}
%code top{
#include <stdio.h>
#include <stdint.h>
}
%code requires{
typedef void* yyscan_t;
}
%code{
int yylex(YYSTYPE* yylvalp, YYLTYPE* yyllocp, yyscan_t scanner);
}
%{
#include "Parser.h"
%}
%token FUNCTION_KEYWORD IF ELSE INT_KEYWORD STR_KEYWORD BOOL_KEYWORD
%token BOOL_LITERAL STR_LITERAL GIVES_TYPE OTHER
%token <val> INT_LITERAL
%token <name> IDENTIFIER
%output "Parser.c"
%defines "Parser.h"
%union{
char name[16];
intmax_t val;
};
%%
function: FUNCTION_KEYWORD IDENTIFIER '(' parameters ')' GIVES_TYPE INT_KEYWORD '{' statement '}'
| FUNCTION_KEYWORD IDENTIFIER '(' parameters ')' GIVES_TYPE STR_KEYWORD '{' statement '}'
| FUNCTION_KEYWORD IDENTIFIER '(' parameters ')' GIVES_TYPE BOOL_KEYWORD '{' statement '}';
parameters: parameter
| %empty;
parameter: INT_KEYWORD IDENTIFIER
| parameter ',' INT_KEYWORD IDENTIFIER
| STR_KEYWORD IDENTIFIER
| parameter ',' STR_KEYWORD IDENTIFIER
| BOOL_KEYWORD IDENTIFIER
| parameter ',' BOOL_KEYWORD IDENTIFIER;
call_parameters: parameter
| %empty;
call_parameter: IDENTIFIER
| parameter ',' IDENTIFIER;
array_list: INT_LITERAL
| IDENTIFIER
| function_call
| indexing_expression
| STR_LITERAL
| BOOL_LITERAL
| array_list ',' IDENTIFIER
| array_list ',' function_call
| array_list ',' indexing_expression
| array_list ',' INT_LITERAL
| array_list ',' STR_LITERAL
| array_list ',' BOOL_LITERAL;
var_definition: INT_KEYWORD IDENTIFIER '=' INT_LITERAL ';'
| INT_KEYWORD IDENTIFIER '=' math_expression ';'
| INT_KEYWORD IDENTIFIER '=' function_call ';'
| INT_KEYWORD IDENTIFIER ';'
| INT_KEYWORD indexing_expression '=' array_list ';'
| INT_KEYWORD indexing_expression ';'
| STR_KEYWORD IDENTIFIER '=' STR_LITERAL ';'
| STR_KEYWORD IDENTIFIER '=' function_call ';'
| STR_KEYWORD IDENTIFIER ';'
| STR_KEYWORD indexing_expression '=' array_list ';'
| STR_KEYWORD indexing_expression ';'
| BOOL_KEYWORD IDENTIFIER '=' BOOL_LITERAL ';'
| BOOL_KEYWORD IDENTIFIER '=' function_call ';'
| BOOL_KEYWORD IDENTIFIER ';'
| BOOL_KEYWORD indexing_expression '=' array_list ';'
| BOOL_KEYWORD indexing_expression ';';
function_call: IDENTIFIER '(' call_parameters ')';
boolean_statement: '(' IDENTIFIER "==" IDENTIFIER ')'
| '(' IDENTIFIER ')'
| '(' indexing_expression ')'
| '(' boolean_statement ')'
| '(' boolean_statement "&&" boolean_statement ')'
| '(' boolean_statement "||" boolean_statement ')'
| '(' '!' boolean_statement ')'
| '(' function_call ')';
BINARY_OPERATOR: '+'| '-' | '*' | '/' | '&' | '|';
math_expression: '(' BINARY_OPERATOR INT_LITERAL INT_LITERAL ')'
| '(' BINARY_OPERATOR INT_LITERAL IDENTIFIER ')'
| '(' BINARY_OPERATOR IDENTIFIER INT_LITERAL ')'
| '(' BINARY_OPERATOR INT_LITERAL function_call ')'
| '(' BINARY_OPERATOR function_call INT_LITERAL ')'
| '(' BINARY_OPERATOR function_call function_call ')'
| '(' BINARY_OPERATOR INT_LITERAL math_expression ')'
| '(' BINARY_OPERATOR math_expression INT_LITERAL ')'
| '(' BINARY_OPERATOR math_expression IDENTIFIER ')'
| '(' BINARY_OPERATOR IDENTIFIER math_expression ')'
| '(' BINARY_OPERATOR math_expression function_call ')'
| '(' BINARY_OPERATOR function_call math_expression ')'
| '(' BINARY_OPERATOR math_expression math_expression ')';
indexing_expression: IDENTIFIER '[' INT_LITERAL ']'
| IDENTIFIER '[' IDENTIFIER ']';
statement: IF boolean_statement '{' statement '}'
| ELSE '{' statement '}'
| "return" function_call ';'
| "return" IDENTIFIER ';'
| "return" INT_LITERAL ';'
| "return" STR_LITERAL ';'
| "return" BOOL_LITERAL ';'
| var_definition;
%%
我遇到的最大问题是,在编译所有内容时,C 编译器会在几个阶段指出 'yyin'、'yyout'、'yyleng' 和其他几个阶段未申报。例如,Lexer.c:800:10: error: ‘yyin’ undeclared (first use in this function)
如果我遗漏了任何重要信息,或者如果其他方面不清楚,我深表歉意。我会尽力澄清任何含糊之处。提前谢谢你
未定义的符号错误都是你输入的结果
#include "Lexer.h"
在您生成的词法分析器中。你绝不能那样做;词法分析器头信息已经包含在生成的词法分析器中,包含两次会导致错误。
同样,你不应该把
#include "Parser.h"
进入生成的解析器。同样,生成的解析器已经有了必要的定义,双重包含可能会导致问题。
我试图在
另请注意,yyerror
是编译成功所必需的。上面答案中的版本应该是一个好的开始。
补充说明
您的代码中还有一些其他错误。
在
funlang.l
的第25行,少了一个;
在第 31 行和第 36 行的操作中,您需要考虑这一段(来自可重入解析器的链接答案):
All references to yylval in scanner actions also need to be modified, since bison's reentrant API passes pointers to the semantic value and location objects. If the semantic type is a union (normally produced by placing a
%union
declaration in the bison source), then you'll need to change scanner actions which useyylval.tag
toyylval->tag
. Similarly, if you use a single semantic type, either the default type or one declared (in the bison source) with %define api.value.type, then you'll need to replaceyylval = ...
with*yylval = ...
, as in the sample code above.即使进行了该修复,
yylval->name = strdup(yytext)
也无法正常工作,因为您的解析器的%union
声明将name
定义为固定长度的字符数组,并且您无法分配给数组.我强烈建议将name
更改为键入char*
;这将与您的词法扫描器中的使用兼容,并且如果有人恰好提供了长标识符,也不会导致缓冲区溢出。字符串文字的正则表达式是错误的,因为它是带引号的字符串。在 (f)lex 中,模式可以包含带引号的字符串,这被证明是非常有用的,但遗憾的是在我所知道的任何正则表达式库中都没有实现。您需要将
"
转义为\"
才能获得您要查找的内容。它也很难阅读;我建议使用否定字符 class 来显示您要排除的字符。但这只是一个建议。您有
%option yylineno
可以灵活地为您跟踪行号。在我的lexer.l
中有一个YY_USER_ACTION
的定义,它使用它来填充位置对象,以便 bison 可以使用令牌位置。如果您使用该代码,您将不需要自己跟踪行号,因此您将不需要line_no
变量或\n
的规则,它们可以添加到您的空白规则中。关于空白规则,flex——与 lex 不同——通常允许没有操作的规则,但这是不正确的,可能会导致问题,特别是在某些时候你使用不同的扫描器时发电机。规则必须有行动;如果你想让一个模式什么也不做,使用动作
;
(这是通常的约定)或{}
.不需要像
main
那样将输入读入内存。 (f)lex 扫描器处理读取文件的所有细节,无论文件有多长。