如何使用Lex和Yacc代码为变量赋值?技术问题求助
Alright, let's walk through why your variable assignment isn't working and fix it step by step. There are a few key gaps in your current code that are preventing the assignment logic from functioning:
What's Wrong With Your Current Implementation?
- Missing Identifier (ID) Token in Lex: You mentioned wanting to define a rule for variable names, but your Lex code doesn't include any logic to match identifiers like
xormyVar. Without this, Lex has no idea how to handle variable names and will treat them as invalid input. - Mismatched
YYSTYPEDefinition: You intended to use a%unionin Yacc to handle both strings (for identifiers) and numbers, but your code uses#define YYSTYPE doublewhich locks the type to only doubles—so you can't store the variable name strings needed for assignment. - No Assignment Grammar Rule: Your Yacc syntax doesn't include a rule to handle
ID = expr;statements, nor do you have a way to store and retrieve variable values (a symbol table). - Overlapping Numeric Tokens: Your
integerandfloatrules overlap, which means inputs like123will always matchintegerfirst, and edge cases like.45won't be recognized as floats at all.
Fixed Lex Code (calc.l)
%{ #include <stdio.h> #include <string.h> #include <stdlib.h> #include "y.tab.h" extern YYSTYPE yylval; %} %option noyywrap delim [ \t] white {delim}+ alpha [A-Za-z] id {alpha}[A-Za-z0-9]* // Variables start with a letter, followed by letters/numbers integer [0-9]+ float_num [0-9]+\.[0-9]*|\.[0-9]+ // Covers 123.45, .45, 123. formats %% {id} { printf("got ID token: %s\n", yytext); yylval.id = strdup(yytext); // Store the variable name in yylval return ID; } {float_num} { printf("got FLOAT token: %s\n", yytext); sscanf(yytext, "%lf", &yylval.num); return FLOAT; } {integer} { printf("got INTEGER token: %s\n", yytext); sscanf(yytext, "%lf", &yylval.num); return INTEGER; } "add" { printf("got ADDITION token\n"); return ADDITION; } "sub" { printf("got SUBTRACTION token\n"); return SUBTRACTION; } "mul" { printf("got MULTIPLICATION token\n"); return MULTIPLICATION; } "div" { printf("got DIVISION token\n"); return DIVISION; } "=" { printf("got ASSIGN token\n"); return ASSIGN; } ";" { printf("got SEMI token\n"); return SEMI; } "(" { return LPAREN; } ")" { return RPAREN; } "\n" { return NEWLINE; } {white} {} . { printf("Unknown character: %c\n", *yytext); } // Catch invalid input %%
Fixed Yacc Code (calc.y)
%{ #include <stdio.h> #include <stdlib.h> #include <string.h> // Simple symbol table to store variable names and their values (linked list) typedef struct Symbol { char *name; double value; struct Symbol *next; } Symbol; Symbol *symbol_table = NULL; // Look up a variable in the symbol table; create it if it doesn't exist Symbol *lookup(char *name) { for (Symbol *s = symbol_table; s != NULL; s = s->next) { if (strcmp(s->name, name) == 0) { return s; } } // Create new variable if not found Symbol *new_sym = malloc(sizeof(Symbol)); new_sym->name = strdup(name); new_sym->value = 0.0; new_sym->next = symbol_table; symbol_table = new_sym; return new_sym; } // Clean up memory from the symbol table void free_symbol_table() { Symbol *temp; while (symbol_table != NULL) { temp = symbol_table; symbol_table = symbol_table->next; free(temp->name); free(temp); } } %} // Union to handle both identifiers (strings) and numeric values %union { char *id; double num; } // Declare token types with their union members %token <id> ID %token <num> INTEGER FLOAT %token ADDITION SUBTRACTION MULTIPLICATION DIVISION %token LPAREN RPAREN ASSIGN SEMI NEWLINE // Define types for non-terminal symbols %type <num> expr term %% input: | input line ; line: NEWLINE | expr NEWLINE { printf("\tResult: %.10g\n", $1); } | assignment SEMI NEWLINE { /* Assignment doesn't output unless referenced */ } ; // Rule for variable assignment assignment: ID ASSIGN expr { Symbol *sym = lookup($1); sym->value = $3; printf("Assigned %s = %.10g\n", $1, $3); free($1); // Free the strdup'd string to avoid memory leaks } ; expr: expr ADDITION term { $$ = $1 + $3; } | expr SUBTRACTION term { $$ = $1 - $3; } | expr MULTIPLICATION term { $$ = $1 * $3; } | expr DIVISION term { if ($3 == 0.0) { yyerror("Division by zero"); $$ = 0.0; } else { $$ = $1 / $3; } } | term ; term: LPAREN expr RPAREN { $$ = $2; } | INTEGER { $$ = $1; } | FLOAT { $$ = $1; } | ID { Symbol *sym = lookup($1); $$ = sym->value; free($1); // Free the strdup'd string } ; %% int yyerror(char *s) { fprintf(stderr, "Error: %s\n", s); return 0; } int main() { printf("Simple calculator with variable assignment. Try:\n"); printf("x = 10;\n"); printf("x add 5;\n"); printf("y = x mul 2;\n"); yyparse(); free_symbol_table(); return 0; }
Key Changes Explained
Lex Improvements:
- Added an
idrule to match valid variable names (starts with a letter, followed by letters/numbers—adjust the regex if you want to allow numeric starts). - Fixed float matching to cover edge cases like
.45and123., and placed it before the integer rule to avoid partial matches. - Added handling for unknown characters to catch invalid input.
- Added an
Yacc Improvements:
- Replaced the hardcoded
YYSTYPEwith a%unionto support both strings and numbers. - Added an
assignmentrule to handleID = expr;statements, using a linked-list symbol table to store variable values. - Added support for referencing variables in expressions via the
IDcase in thetermrule. - Added memory cleanup for the symbol table and strdup'd strings to avoid leaks.
- Added division-by-zero error handling.
- Replaced the hardcoded
How to Compile & Test
Run these commands in your terminal:
yacc -d calc.y lex calc.l gcc lex.yy.c y.tab.c -o calc ./calc
Test with inputs like:
x = 5; x add 3; y = x mul 2; y;
You'll see output confirming assignments and calculation results.
内容的提问来源于stack exchange,提问作者Eman Abbas
相关产品推荐
相关产品推荐

