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 是编译成功所必需的。上面答案中的版本应该是一个好的开始。


补充说明

您的代码中还有一些其他错误。

  1. funlang.l的第25行,少了一个;

  2. 在第 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*;这将与您的词法扫描器中的使用兼容,并且如果有人恰好提供了长标识符,也不会导致缓冲区溢出。

  3. 字符串文字的正则表达式是错误的,因为它是带引号的字符串。在 (f)lex 中,模式可以包含带引号的字符串,这被证明是非常有用的,但遗憾的是在我所知道的任何正则表达式库中都没有实现。您需要将 " 转义为 \" 才能获得您要查找的内容。它也很难阅读;我建议使用否定字符 class 来显示您要排除的字符。但这只是一个建议。

  4. 您有 %option yylineno 可以灵活地为您跟踪行号。在我的 lexer.l 中有一个 YY_USER_ACTION 的定义,它使用它来填充位置对象,以便 bison 可以使用令牌位置。如果您使用该代码,您将不需要自己跟踪行号,因此您将不需要 line_no 变量或 \n 的规则,它们可以添加到您的空白规则中。

  5. 关于空白规则,flex——与 lex 不同——通常允许没有操作的规则,但这是不正确的,可能会导致问题,特别是在某些时候你使用不同的扫描器时发电机。规则必须有行动;如果你想让一个模式什么也不做,使用动作 ; (这是通常的约定)或 {}.

  6. 不需要像 main 那样将输入读入内存。 (f)lex 扫描器处理读取文件的所有细节,无论文件有多长。