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