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) :
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);
}
Bison crea i file "Expression.tab.c" e "Ecpression.tab.h"
Questo è il file "Expression.l", per Flex(Lex):
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];
Flex crea il file "lex.yy.c".
I seguenti file vanno creati a mano:
"symtab.h" contiene le definizioni per la tabella dei simboli:
codice:
#define MAX_SYMBOLS 21 /* Numero massimo di simboli */
struct symtab
{
char *VariableName;
double Value;
} symtab[MAX_SYMBOLS];
struct symtab *symlook();
i file "errors.h" e "errors.c" servono per la gestione degli errori:
errors.h:
codice:
extern int Number_Of_Errors;
extern int Current_Line;
void Error(int position, char * message,...);
int numErrors();
int anyErrors();
errors.c:
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;
}
infine, il file che contiene la funzione main:
expr.c
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;
}
la funzione main si aspetta, come argomento dalla linea di comando, un file di testo. Un esempio di programma valido:
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