【问题标题】:Parser gives a unknown type name error for entry_t解析器为 entry_t 提供未知类型名称错误
【发布时间】:2021-11-11 04:15:19
【问题描述】:

我正在编辑我的第一个解析器,而且我对编译器设计非常陌生。我正在使用 散列表 来存储令牌。我在 symboltable.h 文件中创建了一个名为 struct_t 的结构。

当我尝试在 %union 下的 .y 文件中创建一个新的 entry_t 以在 lex 文件中使用时。但它在编译时出现错误:

parser.y:17:2: 错误:未知类型名称 'entry_t' entry_t** 条目;

parser.y 文件:

%{
    #include <stdlib.h>
    #include <stdio.h>
    #include "symboltable.h"

    entry_t** symbol_table;
    entry_t** constant_table;

    double Evaluate (double lhs_value,int assign_type,double rhs_value);
    int current_dtype;
    int yyerror(char *msg);
%}

%union
{
    double dval;
    entry_t** entry;
    int ival;
}

%token <entry> IDENTIFIER

 /* Constants */
%token <dval> DEC_CONSTANT HEX_CONSTANT
%token STRING

 /* Logical and Relational operators */
%token LOGICAL_AND LOGICAL_OR LS_EQ GR_EQ EQ NOT_EQ

 /* Short hand assignment operators */
%token MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN SUB_ASSIGN
%token LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN
%token INCREMENT DECREMENT

 /* Data types */
%token SHORT INT LONG LONG_LONG SIGNED UNSIGNED CONST

 /* Keywords */
%token IF FOR WHILE CONTINUE BREAK RETURN

%type <dval> expression
%type <dval> sub_expr
%type <dval> constant
%type <dval> unary_expr
%type <dval> arithmetic_expr
%type <dval> assignment_expr
%type <entry> lhs
%type <ival> assign_op

%start starter

%left ','
%right '='
%left LOGICAL_OR
%left LOGICAL_AND
%left EQ NOT_EQ
%left '<' '>' LS_EQ GR_EQ
%left '+' '-'
%left '*' '/' '%'
%right '!'


%nonassoc UMINUS
%nonassoc LOWER_THAN_ELSE
%nonassoc ELSE


%%

 /* Program is made up of multiple builder blocks. */
starter: starter builder
             |builder;

 /* Each builder block is either a function or a declaration */
builder: function|
       declaration;

 /* This is how a function looks like */
function: type IDENTIFIER '(' argument_list ')' compound_stmt;

 /* Now we will define a grammar for how types can be specified */

type :data_type pointer
    |data_type;

pointer: '*' pointer
    |'*'
    ;

data_type :sign_specifier type_specifier
    |type_specifier
    ;

sign_specifier :SIGNED
    |UNSIGNED
    ;

type_specifier :INT                    {current_dtype = INT;}
    |SHORT INT                         {current_dtype = SHORT;}
    |SHORT                             {current_dtype = SHORT;}
    |LONG                              {current_dtype = LONG;}
    |LONG INT                          {current_dtype = LONG;}
    |LONG_LONG                         {current_dtype = LONG_LONG;}
    |LONG_LONG INT                     {current_dtype = LONG_LONG;}
    ;

 /* grammar rules for argument list */
 /* argument list can be empty */
argument_list :arguments
    |
    ;
 /* arguments are comma separated TYPE ID pairs */
arguments :arguments ',' arg
    |arg
    ;

 /* Each arg is a TYPE ID pair */
arg :type IDENTIFIER
   ;

 /* Generic statement. Can be compound or a single statement */
stmt:compound_stmt
    |single_stmt
    ;

 /* The function body is covered in braces and has multiple statements. */
compound_stmt :'{' statements '}'
    ;

statements:statements stmt
    |
    ;

 /* Grammar for what constitutes every individual statement */
single_stmt :if_block
    |for_block
    |while_block
    |declaration
    |function_call ';'
    |RETURN ';'
    |CONTINUE ';'
    |BREAK ';'
    |RETURN sub_expr ';'
    ;

for_block:FOR '(' expression_stmt  expression_stmt ')' stmt
    |FOR '(' expression_stmt expression_stmt expression ')' stmt
    ;

if_block:IF '(' expression ')' stmt %prec LOWER_THAN_ELSE
                |IF '(' expression ')' stmt ELSE stmt
    ;

while_block: WHILE '(' expression   ')' stmt
        ;

declaration:type declaration_list ';'
             |declaration_list ';'
             | unary_expr ';'

declaration_list: declaration_list ',' sub_decl
        |sub_decl;

sub_decl: assignment_expr
    |IDENTIFIER                     {$1 -> data_type = current_dtype;}
    |array_index
    /*|struct_block ';'*/
    ;

/* This is because we can have empty expession statements inside for loops */
expression_stmt:expression ';'
    |';'
    ;

expression:
    expression ',' sub_expr                             {$$ = $1,$3;}
    |sub_expr                                           {$$ = $1;}
        ;

sub_expr:
    sub_expr '>' sub_expr                       {$$ = ($1 > $3);}
    |sub_expr '<' sub_expr                      {$$ = ($1 < $3);}
    |sub_expr EQ sub_expr                       {$$ = ($1 == $3);}
    |sub_expr NOT_EQ sub_expr                   {$$ = ($1 != $3);}
    |sub_expr LS_EQ sub_expr                    {$$ = ($1 <= $3);}
    |sub_expr GR_EQ sub_expr                    {$$ = ($1 >= $3);}
    |sub_expr LOGICAL_AND sub_expr              {$$ = ($1 && $3);}
    |sub_expr LOGICAL_OR sub_expr               {$$ = ($1 || $3);}
    |'!' sub_expr                               {$$ = (!$2);}
    |arithmetic_expr                            {$$ = $1;}
    |assignment_expr                            {$$ = $1;}
    |unary_expr                                 {$$ = $1;}
    /* |IDENTIFIER                                     {$$ = $1->value;}
    |constant                                   {$$ = $1;} */
        //|array_index
    ;


assignment_expr :lhs assign_op arithmetic_expr     {$$ = $1->value = Evaluate($1->value,$2,$3);}
    |lhs assign_op array_index                     {$$ = 0;}
    |lhs assign_op function_call                   {$$ = 0;}
    |lhs assign_op unary_expr                      {$$ = $1->value = Evaluate($1->value,$2,$3);}
    |unary_expr assign_op unary_expr               {$$ = 0;}
    ;

unary_expr: lhs INCREMENT                          {$$ = $1->value = ($1->value)++;}
    |lhs DECREMENT                                 {$$ = $1->value = ($1->value)--;}
    |DECREMENT lhs                                 {$$ = $2->value = --($2->value);}
    |INCREMENT lhs                                 {$$ = $2->value = ++($2->value);}

lhs:IDENTIFIER                                     {$$ = $1; if(! $1->data_type) $1->data_type = current_dtype;}
    //|array_index
    ;

assign_op:'='                                      {$$ = '=';}
    |ADD_ASSIGN                                    {$$ = ADD_ASSIGN;}
    |SUB_ASSIGN                                    {$$ = SUB_ASSIGN;}
    |MUL_ASSIGN                                    {$$ = MUL_ASSIGN;}
    |DIV_ASSIGN                                    {$$ = DIV_ASSIGN;}
    |MOD_ASSIGN                                    {$$ = MOD_ASSIGN;}
    ;

arithmetic_expr: arithmetic_expr '+' arithmetic_expr    {$$ = $1 + $3;}
    |arithmetic_expr '-' arithmetic_expr                {$$ = $1 - $3;}
    |arithmetic_expr '*' arithmetic_expr                {$$ = $1 * $3;}
    |arithmetic_expr '/' arithmetic_expr                {$$ = ($3 == 0) ? yyerror("Divide by 0!") : ($1 / $3);}
    |arithmetic_expr '%' arithmetic_expr                {$$ = (int)$1 % (int)$3;}
    |'(' arithmetic_expr ')'                            {$$ = $2;}
    |'-' arithmetic_expr %prec UMINUS                   {$$ = -$2;}
    |IDENTIFIER                                         {$$ = $1 -> value;}
    |constant                                           {$$ = $1;}
    ;

constant: DEC_CONSTANT                                  {$$ = $1;}
    |HEX_CONSTANT                                       {$$ = $1;}
    ;

array_index: IDENTIFIER '[' sub_expr ']'

function_call: IDENTIFIER '(' parameter_list ')'
             |IDENTIFIER '(' ')'
             ;

parameter_list:
              parameter_list ','  parameter
              |parameter
              ;

parameter: sub_expr
                    |STRING

        ;
%%

#include "lex.yy.c"
#include <ctype.h>


double Evaluate (double lhs_value,int assign_type,double rhs_value)
{
    switch(assign_type)
    {
        case '=': return rhs_value;
        case ADD_ASSIGN: return (lhs_value + rhs_value);
        case SUB_ASSIGN: return (lhs_value - rhs_value);
        case MUL_ASSIGN: return (lhs_value * rhs_value);
        case DIV_ASSIGN: return (lhs_value / rhs_value);
        case MOD_ASSIGN: return ((int)lhs_value % (int)rhs_value);
    }
}

int main(int argc, char *argv[])
{
    symbol_table = create_table();
    constant_table = create_table();

    yyin = fopen(argv[1], "r");

    if(!yyparse())
    {
        printf("\nParsing complete\n");
    }
    else
    {
            printf("\nParsing failed\n");
    }


    printf("\n\tSymbol table");
    display(symbol_table);


    fclose(yyin);
    return 0;
}

int yyerror(char *msg)
{
    printf("Line no: %d Error message: %s Token: %s\n", yylineno, msg, yytext);
}

lexl.l 文件

%{

#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include "y.tab.h"

int cmnt_strt = 0;

%}
%option yylineno
letter [a-zA-Z]
digit [0-9]
ws  [ \t\r\f\v]+
identifier (_|{letter})({letter}|{digit}|_){0,31}
hex [0-9a-f]

 /* Exclusive states */
%x CMNT
 /*%x PREPROC*/

%%
  /* Keywords*/
"int"                             {return INT;}
"long"                            {return LONG;}
"long long"                       {return LONG_LONG;}
"short"                           {return SHORT;}
"signed"                          {return SIGNED;}
"unsigned"                        {return UNSIGNED;}
"for"                             {return FOR;}
"while"                           {return WHILE;}
"break"                           {return BREAK;}
"continue"                        {return CONTINUE;}
"if"                              {return IF;}
"else"                            {return ELSE;}
"return"                          {return RETURN;}

{identifier}                      {yylval.entry = insert(symbol_table, yytext, INT_MAX); return  IDENTIFIER;}
{ws}                              ;
[+\-]?[0][x|X]{hex}+[lLuU]?        { yylval.dval = (int)strtol(yytext, NULL, 16); return  HEX_CONSTANT;}
[+\-]?{digit}+[lLuU]?              {yylval.dval = atoi(yytext); return  DEC_CONSTANT;}

"/*"                              {cmnt_strt = yylineno; BEGIN CMNT;}
<CMNT>.|{ws}                      ;
<CMNT>\n                          {yylineno++;}
<CMNT>"*/"                        {BEGIN INITIAL;}
<CMNT>"/*"                        {printf("Line %3d: Nested comments are not valid!\n",yylineno);}
<CMNT><<EOF>>                     {printf("Line %3d: Unterminated comment\n", cmnt_strt); yyterminate();}
 /*^"#include"                       {BEGIN PREPROC;}*/
 /*<PREPROC>"<"[^<>\n]+">"            {return HEADER_FILE;}*/
 /*<PREPROC>{ws}                       ;*/
 /*<PREPROC>\"[^"\n]+\"              {return HEADER_FILE;}*/
 /*<PREPROC>\n                       {yylineno++; BEGIN INITIAL;}*/
 /*<PREPROC>.                        {printf("Line %3d: Illegal header file format \n",yylineno);}*/
"//".*                            ;

\"[^\"\n]*\"     {

  if(yytext[yyleng-2]=='\\') /* check if it was an escaped quote */
  {
    yyless(yyleng-1);       /* push the quote back if it was escaped */
    yymore();
  }
  else{
  insert( constant_table, yytext, INT_MAX);
  return STRING;
  }
 }

\"[^\"\n]*$                     {printf("Line %3d: Unterminated string %s\n",yylineno,yytext);}
{digit}+({letter}|_)+           {printf("Line %3d: Illegal identifier name %s\n",yylineno,yytext);}
\n                              {yylineno++;}

"--"                            {return DECREMENT;}
"++"                            {return INCREMENT;}
 /* "->"                            {return PTR_SELECT;} */
"+="                      {return ADD_ASSIGN;}
"-="                      {return SUB_ASSIGN;}
"*="                      {return MUL_ASSIGN;}
"/="                      {return DIV_ASSIGN;}
"%="                      {return MOD_ASSIGN;}


"&&"                            {return LOGICAL_AND;}
"||"                            {return LOGICAL_OR;}
"<="                            {return LS_EQ;}
">="                            {return GR_EQ;}
"=="                            {return EQ;}
"!="                          {return NOT_EQ;}

.                         {return yytext[0];}

%%
/*
int main()
{
  yyin=fopen("test2.c","r");

  constant_table=create_table();
  symbol_table = create_table();

  yylex();

  printf("\n\tSymbol table");
  display(symbol_table);
  printf("\n\tConstants Table");
  display(constant_table);

  printf("NOTE: Please refer tokens.h for token meanings\n");
} */

symboltable.h 文件

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>

#define HASH_TABLE_SIZE 100

/* struct to hold each entry */
struct entry_s
{
    char* lexeme;
    double value;
    int data_type;
    struct entry_s* successor;
};

typedef struct entry_s entry_t;

/* Create a new hash_table. */
entry_t** create_table()
{
    entry_t** hash_table_ptr = NULL; // declare a pointer

    /* Allocate memory for a hashtable array of size HASH_TABLE_SIZE */
    if( ( hash_table_ptr = malloc( sizeof( entry_t* ) * HASH_TABLE_SIZE ) ) == NULL )
        return NULL;

    int i;

    // Intitialise all entries as NULL
    for( i = 0; i < HASH_TABLE_SIZE; i++ )
    {
        hash_table_ptr[i] = NULL;
    }

    return hash_table_ptr;
}

/* Generate hash from a string. Then generate an index in [0, HASH_TABLE_SIZE) */
uint32_t hash( char *lexeme )
{
    size_t i;
    uint32_t hash;

    /* Apply jenkin's hash function
    * https://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time
    */
    for ( hash = i = 0; i < strlen(lexeme); ++i ) {
        hash += lexeme[i];
        hash += ( hash << 10 );
        hash ^= ( hash >> 6 );
    }
    hash += ( hash << 3 );
    hash ^= ( hash >> 11 );
    hash += ( hash << 15 );

    return hash % HASH_TABLE_SIZE; // return an index in [0, HASH_TABLE_SIZE)
}

/* Create an entry for a lexeme, token pair. This will be called from the insert function */
entry_t *create_entry( char *lexeme, int value )
{
    entry_t *newentry;

    /* Allocate space for newentry */
    if( ( newentry = malloc( sizeof( entry_t ) ) ) == NULL ) {
        return NULL;
    }
    /* Copy lexeme to newentry location using strdup (string-duplicate). Return NULL if it fails */
    if( ( newentry->lexeme = strdup( lexeme ) ) == NULL ) {
        return NULL;
    }

    newentry->value = value;
    newentry->successor = NULL;

    return newentry;
}

/* Search for an entry given a lexeme. Return a pointer to the entry of the lexeme exists, else return NULL */
entry_t* search( entry_t** hash_table_ptr, char* lexeme )
{
    uint32_t idx = 0;
    entry_t* myentry;

    // get the index of this lexeme as per the hash function
    idx = hash( lexeme );

    /* Traverse the linked list at this idx and see if lexeme exists */
    myentry = hash_table_ptr[idx];

    while( myentry != NULL && strcmp( lexeme, myentry->lexeme ) != 0 )
    {
        myentry = myentry->successor;
    }

    if(myentry == NULL) // lexeme is not found
        return NULL;

    else // lexeme found
        return myentry;

}

/* Insert an entry into a hash table. */
entry_t* insert( entry_t** hash_table_ptr, char* lexeme, int value )
{
    entry_t* finder = search( hash_table_ptr, lexeme );
    if( finder != NULL) // If lexeme already exists, don't insert, return
        return finder ;

    uint32_t idx;
    entry_t* newentry = NULL;
    entry_t* head = NULL;

    idx = hash( lexeme ); // Get the index for this lexeme based on the hash function
    newentry = create_entry( lexeme, value ); // Create an entry using the <lexeme, token> pair

    if(newentry == NULL) // In case there was some error while executing create_entry()
    {
        printf("Insert failed. New entry could not be created.");
        exit(1);
    }

    head = hash_table_ptr[idx]; // get the head entry at this index

    if(head == NULL) // This is the first lexeme that matches this hash index
    {
        hash_table_ptr[idx] = newentry;
    }
    else // if not, add this entry to the head
    {
        newentry->successor = hash_table_ptr[idx];
        hash_table_ptr[idx] = newentry;
    }
    return hash_table_ptr[idx];
}

// Traverse the hash table and print all the entries
void display(entry_t** hash_table_ptr)
{
    int i;
    entry_t* traverser;
    printf("\n====================================================\n");
    printf(" %-20s %-20s %-20s\n","lexeme","value","data-type");
    printf("====================================================\n");

    for( i=0; i < HASH_TABLE_SIZE; i++)
    {
        traverser = hash_table_ptr[i];

        while( traverser != NULL)
        {
            printf(" %-20s %-20d %-20d \n", traverser->lexeme, (int)traverser->value, traverser->data_type);
            traverser = traverser->successor;
        }
    }
    printf("====================================================\n");

}

我无法弄清楚为什么会出现未知文件类型错误。如果可以,请指出正确的方向。

【问题讨论】:

  • symboltable.h 这样的头文件通常不应该包含函数的实现。它应该只包含它们的声明,并且一个单独的源文件symboltable.c 应该定义这些函数。该规则有例外(例如,static inline 函数),但它们并不真正适用于此。
  • 是 C 编译器抱怨还是 Yacc/Bison 抱怨?
  • C 编译器@JonathanLeffler
  • 请提供足够的代码,以便其他人更好地理解或重现问题。

标签: c compiler-construction bison flex-lexer lexical-analysis


【解决方案1】:

假设你是昨天问this question with remarkably similar code的同一个Shehan,我当时所说的仍然适用:

如果这是您第一次尝试使用多个源文件编写 C 应用程序,您可能应该花几分钟时间来回顾一下在 C 中链接多个文件的工作原理。这将为您省去很多麻烦稍后。

这正是我所指的那种挫败感。

您收到的错误来自尝试编译lex.yy.c。从lexl.l 生成的那个文件不是#include "symboltable.h",而是#include "y.tab.h"。在y.tab.h中,entry_t用于union语义类型的声明,但由于没有包含symboltable.h,所以还没有定义entry_t

但是仅仅添加#include "symboltable.h"是行不通的,所以请不要这样做。

symboltable.h 不是正确的头文件,正如a comment by @JonathanLeffler 中提到的那样。头文件不能包含函数定义,只能包含声明。此外,symboltable.h 没有包含保护,因此不会阻止它被包含两次,从而导致重复定义错误。

但是按照您的文件设置方式,您首先不应该编译lex.yy.c。您的 parser.y 文件包含该行

#include "lex.yy.c"

正如预期的那样,当您编译@​​987654339@ 时,它在正在编译的源代码中包含了lex.yy.c 的整个文本。我不相信你自己想出了这个把戏;我认为您从某个懒得尝试解释如何设置具有多个源的项目的人编写的教程中复制了它。虽然可以让简单的解析器工作,但它对你未来的服务并不好,你不应该通过学习坏习惯来开始学习解析器。

所以我再说一遍:请先学习如何创建一个包含多个源文件和头文件的 C 项目。否则,您学习如何使用 bison 和 flex 的尝试将比必要的更加困难和令人沮丧。

【讨论】:

  • 任何关于 C 编程的优秀文本都应该包含如何将项目划分为多个文件的部分。如果您的文本不包含该信息,请找到更好的信息。
  • 你可以教我吗?家教什么的。我渴望学习这一点,但找不到合适的资源来进一步发展。我的课很烂,他不做实践,只做理论。还是谢谢。
  • @ShehanFernando:如果我周日有时间,我会试着写点东西。我找不到好的在线教学资源,但我确信有教科书(从 K&R 开始)。但没有承诺。我绝对没有时间招收学生,抱歉。
  • 没关系。非常感谢您考虑:)
猜你喜欢
  • 2017-08-19
  • 2012-02-20
  • 2016-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多