用于检测重声明与多声明的Yacc语义分析器出现解析错误
Hey there, let's work through this syntax error issue together—this is a super common pitfall when pairing Lex and Yacc, so we'll get to the bottom of it quickly!
First, let's break down the core issues that are likely causing your errors:
1. Misaligned Token Handling Between Lex and Yacc
Your space-related errors hint at a mismatch in how you're treating whitespace:
- If your Lex file is ignoring spaces (e.g., with a rule like
[ \t\n]+ ;), then Yacc never receives aSPACEtoken. AddingSPACEto your Yacc rules will immediately cause syntax errors, because Yacc expects a token that Lex never sends. - If you try to return
SPACEas a token from Lex, you have to explicitly account for it everywhere whitespace is allowed in your grammar (between type and identifier, commas and identifiers, etc.), which gets messy fast. The standard approach here is to just ignore whitespace in Lex.
2. Keyword vs. Identifier Priority in Lex
Another critical mistake: if your Lex rule for identifiers comes before your keyword rules (like int/float), Lex will treat "int" as an identifier instead of a type token. This breaks your entire grammar, since Yacc won't recognize the type specifier.
3. Grammar Rule Structure
Your Yacc rules need to properly model the declaration syntax: type_specifier identifier_list ';', where identifier_list handles multiple comma-separated variables. If your rules don't account for the comma-separated list correctly, Yacc will throw syntax errors when parsing a,b,c.
Fix Example: Working Lex + Yacc Files
Let's walk through corrected code that handles your test input (int a,b,c; float a;) and catches the redeclaration error.
Lex File (lex.l)
%{ #include "y.tab.h" #include <string.h> // Global to track the current declaration type (passed to Yacc) char* current_type; %} %% // Match keywords FIRST (before identifiers!) "int" { current_type = strdup(yytext); return TYPE; } "float" { current_type = strdup(yytext); return TYPE; } // Match identifiers [a-zA-Z_][a-zA-Z0-9_]* { yylval.str = strdup(yytext); return ID; } // Match punctuation "," { return ','; } ";" { return ';'; } // Ignore whitespace (no token returned) [ \t\n]+ ; // Catch invalid characters . { fprintf(stderr, "Unknown character: %c\n", *yytext); } %% int yywrap() { return 1; }
Yacc File (yacc.y)
%{ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #define MAX_VARS 100 char* var_table[MAX_VARS]; // Stores declared variable names char* var_types[MAX_VARS]; // Stores variable types int var_count = 0; // Check if a variable is already declared bool is_declared(char* id) { for (int i = 0; i < var_count; i++) { if (strcmp(var_table[i], id) == 0) { return true; } } return false; } // Add variable to the table (or error on redeclaration) void add_var(char* id, char* type) { if (is_declared(id)) { fprintf(stderr, "Error: Variable '%s' redeclared (line %d)\n", id, yylineno); exit(1); } if (var_count >= MAX_VARS) { fprintf(stderr, "Error: Variable table full\n"); exit(1); } var_table[var_count] = strdup(id); var_types[var_count] = strdup(type); var_count++; } %} %union { char* str; } %token TYPE %token <str> ID %% // Top-level rule: program is one or more declarations program: declaration program | declaration ; // Single declaration: type + comma-separated IDs + semicolon declaration: TYPE id_list ';' { free(current_type); } ; // Handle list of identifiers (single ID or ID + comma + list) id_list: ID { add_var($1, current_type); free($1); } | ID ',' id_list { add_var($1, current_type); free($1); } ; %% int yyerror(char* msg) { fprintf(stderr, "Error: Syntax error - %s (line %d)\n", msg, yylineno); return 1; } int main() { yyparse(); printf("All declarations are valid!\n"); return 0; }
Key Fixes Explained
- Keyword Priority: Lex matches
int/floatbefore identifiers, ensuring they're treated as type tokens instead of variable names. - Whitespace Handling: Lex ignores spaces entirely, so Yacc never has to deal with
SPACEtokens—this keeps the grammar clean. - Proper Grammar Structure: The
id_listrule correctly handles comma-separated variables, so Yacc can parsea,b,cwithout syntax errors. - Redeclaration Check: The semantic actions in Yacc check each identifier against the variable table before adding it, catching the
float a;redeclaration error correctly.
How to Test
Compile with:
lex lex.l yacc -d yacc.y gcc lex.yy.c y.tab.c -o semantic_analyzer
Run with your test input:
echo "int a,b,c; float a;" | ./semantic_analyzer
You'll get the correct redeclaration error:
Error: Variable 'a' redeclared (line 1)
内容的提问来源于stack exchange,提问作者Hamsa

