Ciao Matthew,
L'esempio che segue effettua il parsing di un'espressione matematica che può essere formata da parentesi tonde, costanti numeriche, variabili e gli operatori +, -(unario e binario), *, /.
Per gestire la precedenza utilizzo le regole della grammatica e per l'associatività le regole di bison( p. es. %left '-' '+' significa che gli operatori + e - associano da sinistra). Dunque, le parentesi quadre e graffe non servono(puoi comunque gestirle con una piccola modifica alla grammatica nel file expression.y).
Ho utilizzato Flex e Bison al posto di Lex e Yacc(ma i file .y e .l sono compatibili con entrambe le versioni).
Questo è il file "Expression.y", per Bison(Yacc) :
Bison crea i file "Expression.tab.c" e "Ecpression.tab.h"codice:%{ #include "symtab.h" #include <string.h> #include <stdio.h> #include "errors.h" void yyerror(char *s) { Error(Current_Line,"%s\n",s); } %} %union { double dval; struct symtab *symp; } %token <symp> NAME %token <dval> NUMBER %left '-' '+' %left '*' '/' %nonassoc UMINUS %type <dval> expression %% statement_list: statement '\n' | statement_list statement '\n' ; statement: NAME '=' expression { $1->Value = $3; } | expression { printf("= %g\n", $1); } ; expression: expression '+' expression { $$ = $1 + $3; } | expression '-' expression { $$ = $1 - $3; } | expression '*' expression { $$ = $1 * $3; } | expression '/' expression { if($3 == 0.0) yyerror("divisione per zero"); else $$ = $1 / $3; } | '-' expression %prec UMINUS { $$ = -$2; } | '(' expression ')' { $$ = $2; } | NUMBER | NAME { $$ = $1->Value; } ; %% struct symtab * symlook(char *s) { char *p; struct symtab *sp; for(sp = symtab; sp < &symtab[MAX_SYMBOLS]; sp++) { if(sp->VariableName && !strcmp(sp->VariableName, s)) return sp; if(!sp->VariableName) { sp->VariableName = strdup(s); return sp; } } yyerror("Troppi simboli"); exit(1); }
Questo è il file "Expression.l", per Flex(Lex):
Flex crea il file "lex.yy.c".codice:%{ #include "Expression.tab.h" #include "symtab.h" #include <math.h> %} %% ([0-9]+|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?) { yylval.dval = atof(yytext); return NUMBER; } [ \t] ; /* ignoriamo gli spazi bianchi */ [A-Za-z][A-Za-z0-9]* { /* ritorna un puntatore al simbolo */ yylval.symp = symlook(yytext); return NAME; } "$" { return 0; } \n | . return yytext[0];
I seguenti file vanno creati a mano:
"symtab.h" contiene le definizioni per la tabella dei simboli:
i file "errors.h" e "errors.c" servono per la gestione degli errori:codice:#define MAX_SYMBOLS 21 /* Numero massimo di simboli */ struct symtab { char *VariableName; double Value; } symtab[MAX_SYMBOLS]; struct symtab *symlook();
errors.h:
errors.c:codice:extern int Number_Of_Errors; extern int Current_Line; void Error(int position, char * message,...); int numErrors(); int anyErrors();
infine, il file che contiene la funzione main:codice:#include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include "errors.h" int Number_Of_Errors = 0; int Current_Line = 1; void Error(int position, char *message, ...) { va_list ap; Number_Of_Errors++; fprintf(stderr, "Errore linea n. %d: ", position); va_start(ap, message); vfprintf(stderr, message, ap); va_end(ap); fprintf(stderr, "\n"); } int numErrors() { return Number_Of_Errors; } int anyErrors() { return Number_Of_Errors; }
expr.c
la funzione main si aspetta, come argomento dalla linea di comando, un file di testo. Un esempio di programma valido:codice:#include <stdio.h> extern int yyparse(void); extern FILE *yyin; #ifdef yywrap # undef yywrap #endif int yywrap(void) { return 0; } void parse(char *filename) { yyin = fopen(filename, "r"); if (yyin == NULL) { fprintf(stderr,"Impossibile aprire il file: %s\n", filename); } else { if (yyparse() == 0) fprintf(stderr,"Parsing eseguito con successo!\n"); else fprintf(stderr,"Parsing fallito.\n"); } } int main(int argc, char **argv) { if (argc!=2) { fprintf(stderr,"uso: %s filename\n", argv[0]); return -1; } parse(argv[1]); return 0; }
VarA = 21.34
VarB = 8
((5 + VarA) / (VarB + 5.8)) * 34.55
$
L'output è il seguente:
= 65.9454
Parsing eseguito con successo!
Se hai qualche dubbio, chiedi pure.
Ciao![]()

Rispondi quotando