【发布时间】: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