codice:
Analizzatore di espressioni v0.6
#ifndef _EXPR_EVAL_
#define _EXPR_EVAL_
// I N C L U D E S
#include <string.h>
#include <math.h>
#include <setjmp.h>
// - - - - - - - -
// C O S T A N T I M A N I F E S T E E V A R I A B I L I G L O B A L I
#define SYNTAXERROR -1
#define INVALP 10 // "Parametro non valido"
typedef struct {
char *funct;
short int flag;
} errorType;
static errorType err;
// P R O T O T I P I
static double eval_expr(char *str, int p);
static void error(char *funct, short int type);
static jmp_buf jmp;
// C O S T A N T I M A T E M A T I C H E
struct c {
char *ident;
double valore;
};
struct c costanti[] = {
{"PI", M_PI},
{"E", M_E},
{NULL, 0}
};
// - - - - - - - - - - - - - - - - - - -
// F U N Z I O N I - O P E R A T O R I
double addizione(double a, double b) {
return (a+b);
}
double sottrazione(double a, double b) {
return (a-b);
}
double divisione(double a, double b) {
return (a/b);
}
double moltiplicazione(double a, double b) {
return (a*b);
}
// Definizione del template della struttura di gestione degli operatori
struct op {
char segno;
unsigned short precedenza;
double (*calcola)(double, double);
};
// Dichiarazione e inizializzazione della struttura di gestione degli operatori
struct op operatori[] = {
{'+', 1, addizione},
{'-', 1, sottrazione},
{'/', 2, divisione},
{'*', 2, moltiplicazione},
{'%', 2, fmod},
{'^', 3, pow},
{0, 0, 0}
};
// - - - - - - - - - - - - - - - - - -
// F U N Z I O N I A N A L I T I C H E T R A S C E N D E N T I
// DEFINIZIONE DEL TEMPLATE DI STRUTTURA PER
// LA GESTIONE DELLE FUNZIONI AD UN ARGOMENTO
struct f {
char *ident;
double (*esegui)(double);
};
// ------------------------------------------
// F U N Z I O N I G O N I O M E T R I C H E I N R A D I A N T I
double acosh(register double x) {
return(log(x+sqrt(x*x-1.0)));
}
double asinh(register double x) {
return(log(x+sqrt(x*x+1.0)));
}
double atanh(register double x) {
return(0.5*log((1.0+x)/(1.0-x)));
}
double sec(double n) {
return (1/cos(n));
}
double cosec(double n) {
return (1/sin(n));
}
double cot(double n) {
return (cos(n)/sin(n));
}
double acot(double n) {
return (atan(1/n));
}
double asec(double n) {
return (acos(1/n));
}
double acosec(double n) {
return (asin(1/n));
}
double coth(double n) {
return (1/tanh(n));
}
double sech(double n) {
return (1/cosh(n));
}
double cosech(double n) {
return (1/sinh(n));
}
double acoth(double n) {
return (0.5*log((n+1)/(n-1)));
}
double asech(double n) {
return (log(1/n+sqrt(1/(n*n)+1)));
}
double acosech(double n) {
return (log(1/n+sqrt(1/(n*n)-1)));
}
// Dichiarazione e inizializzazione della struttura per la
// gestione delle funzioni goniometriche che accettano come
// argomento l'ampiezza di un angolo espressa in radianti
struct f funzioniTrigR[] = {
{"asinh", asinh},
{"acosh", acosh},
{"atanh", atanh},
{"asech", sec},
{"acoth", cot},
{"acosech", cosec},
{"asin", asin},
{"acos", acos},
{"atan", atan},
{"asec", asec},
{"acot", acot},
{"acosec", acosec},
{"sinh", sinh},
{"cosh", cosh},
{"tanh", tanh},
{"sech", sec},
{"coth", cot},
{"cosech", cosec},
{"sin", sin},
{"cos", cos},
{"tan", tan},
{"sec", sec},
{"cot", cot},
{"cosec", cosec},
{NULL, 0}
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// A L T R E F U N Z I O N I (accettano un argomento)
double fact(register double n) {
register double i;
if (n < 0)
error("fact", DOMAIN);
if (fmod(n, 1.0))
error("fact", INVALP);
for(i=n, n=1.0; i>1; i--)
n *= i;
return(n);
}
double fibo(register double n) {
register double F[3] = {1.0, 1.0, 0.0};
register long long i;
if (n < 0)
error("fibo", DOMAIN);
if (fmod(n, 1.0))
error("fibo", INVALP);
if (n <= 2)
return(1);
for (i=2; i<(long long)n; i++)
F[i%3] = F[(i+4)%3] + F[(i+5)%3];
return(F[(i-1)%3]);
}
double cbrt(double n) {
return (pow(n, 1.0/3.0));
}
double degr2rad(double n) {
return(n * 0.0174532925199432957692);
}
struct f altreFunzioni1[] = {
{"sqrt", sqrt},
{"cbrt", cbrt},
{"ln", log},
{"log", log10},
{"exp", exp},
{"fibo", fibo},
{"fact", fact},
{"d2r", degr2rad},
{NULL, 0}
};
// - - - - - - - - - - - - -
// A L T R E F U N Z I O N I (accettano due parametri)
double logbN(register double b, register double N) {
return(log10(N)/log10(b));
}
double rootnX(register double n, register double X) {
return(pow(X, 1/n));
}
// Definizione del template della struttura per la gestione
// delle funzioni che accettano due argomenti
struct f_2 {
char *ident;
double (*esegui)(double, double);
};
// Dichiarazione e inizializzazione della struttura di gestione
// delle funzioni che accettano due argomenti
struct f_2 altreFunzioni2[] = {
{"root", rootnX},
{"log", logbN},
{"pow", pow},
{NULL, 0}
};
// - - - - - - - - - - - - -
// D E F I N I Z I O N E F U N Z I O N I
// Legge da expr un blocco di espressioni racchiuso tra parentesi
// (che costituisce l'argomento di una funzione)
// e lo copia in blocco. Esempio:
// espressione = "fact(12*45+5)-41)"
// expr = "12*45+5)-41";
// blocco = "12*45+5"
static int scanBlocco(char *expr, char *blocco) {
int par = 0, count = 0;
for ( ; *expr; blocco++, expr++, count++) {
*blocco = '\0';
if (*expr == '(') par++;
if (*expr == ')') par--;
if(par == -1)
return(count);
*blocco = *expr;
}
*blocco = '\0';
return((par==0) ? count : 0);
}
// Legge da expr un blocco racchiuso tra parentesi formato dai due
// argomenti di una funzione separati da una virgola.
// Copia i due argomenti in blocco1 e blocco2. Esempio:
// espressione = "log(10, 25)*fact(5)"
// expr = "10, 25)*fact(5)";
// blocco = "10, 25"
// blocco1 = "10"
// blocco2 = "25"
// Ritorna il numero di caratteri che compongono la stringa "arg1,arg2"
static int scanArgs(char *expr, char *blocco1, char *blocco2) {
int count, par=0;
char *ptrVirgola;
register int i;
count = scanBlocco(expr, blocco1); // prendo tutto il blocco "param1,param2"
for (ptrVirgola=blocco1; *ptrVirgola; ptrVirgola++) {
if (*ptrVirgola == '(')
par++;
if (*ptrVirgola == ')')
par--;
if (par != 0)
continue;
if (*ptrVirgola == ',') {
break;
}
}
if(*ptrVirgola != ',') // Se di mezzo non c'Š una virgola
return(0); // Š una funzione ad 1 argomento
*ptrVirgola = '\0'; // delimito blocco1
strcpy(blocco2, ptrVirgola+1);
return(count);
}
static int isOperat(char s) {
register int i;
for (i=0; operatori[i].segno; i++)
if (operatori[i].segno == s)
return(i);
return(-1);
}
static int isFunctGR(char *s) {
register int i;
for (i=0; funzioniTrigR[i].ident; i++) {
if (strncmp(funzioniTrigR[i].ident, s, strlen(funzioniTrigR[i].ident)) == 0)
return(i);
}
return(-1);
}
static int isFunct1(char *s) {
register int i;
for (i=0; altreFunzioni1[i].ident; i++) {
if (strncmp(altreFunzioni1[i].ident, s, strlen(altreFunzioni1[i].ident)) == 0)
return(i);
}
return(-1);
}
static int isFunct2(char *s) {
register int i;
for (i=0; altreFunzioni2[i].ident; i++) {
if (strncmp(altreFunzioni2[i].ident, s, strlen(altreFunzioni2[i].ident)) == 0)
return(i);
}
return(-1);
}
static void risolviFunzioni(char *s) {
int i;
char save[81];
int len;
for (; *s; s++) {
if ((i = isFunct2(s)) >= 0) {
char blocco1[162];
char blocco2[81];
double a, b;
len = scanArgs(s+strlen(altreFunzioni2[i].ident)+1, blocco1, blocco2);
if (len) {
a = eval_expr(blocco1, 0);
b = eval_expr(blocco2, 0);
strcpy(save, s + strlen(altreFunzioni2[i].ident) + len + 2);
sprintf(s, "%lf", altreFunzioni2[i].esegui(a, b));
s += strlen(s);
strcpy(s, save);
continue;
}
}
if ((i = isFunct1(s)) >= 0) {
char blocco[81];
len = scanBlocco(s+strlen(altreFunzioni1[i].ident)+1, blocco);
strcpy(save, s + strlen(altreFunzioni1[i].ident) + len + 2);
sprintf(s, "%lf", altreFunzioni1[i].esegui(eval_expr(blocco, 0)));
s += strlen(s);
strcpy(s, save);
continue;
}
if ((i = isFunctGR(s)) >= 0) {
char blocco[81];
len = scanBlocco(s+strlen(funzioniTrigR[i].ident)+1, blocco);
strcpy(save, s + strlen(funzioniTrigR[i].ident) + len + 2);
sprintf(s, "%lf", funzioniTrigR[i].esegui(eval_expr(blocco, 0)));
s += strlen(s);
strcpy(s, save);
}
}
}
#define NOP -2
#define NOPREC -1
static int find_last_op(char *expr, int p) {
register int pos;
int i;
int par = 0;
int ret = NOP;
for (pos=strlen(expr)-1; pos>=0; pos--) {
if (expr[pos] == ')')
par++;
if (expr[pos] == '(')
par--;
if (par != 0)
continue;
i = isOperat(expr[pos]);
if (i >= 0) {
if (operatori[i].precedenza == p)
return (pos);
ret = NOPREC;
}
}
return (ret);
}
static double is_nb(char *str) {
// Motivo 1: elimina le parentesi e analizza l'espressione
if (*str=='(') {
str[strlen(str)-1] = 0;
return (eval_expr(str+1, 1));
}
// Motivo 2: converte la stringa in valore double e la ritorna
return (atof(str));
}
static double eval_expr(char *expr, int p) {
register int i;
int pos;
char save;
// per prima cosa mi risolvo le funzioni e sostituisco ad esse
// nella stringa il corrispondente valore numerico
risolviFunzioni(expr);
// Vanno eseguite le operazioni a partire da quelle con precedenza
// maggiore. Questo problema comunque è a carico del processo di
// ricorsione: comincio a scandagliare quelle a precedenza minore
// e che non siano all'interno di blocchi di parentesi.
// Questa ricerca l'addebito alla find_last_op().
// Cerco quindi l'operazione con priorità == p.
switch (pos = find_last_op(expr, p)) {
case NOPREC:
// non ci sono operatori a precedenza p:
// aumento la precedenza e rieseguo la ricerca
return (eval_expr(expr, p+1));
case NOP:
// non ci sono più operazioni pronte da eseguire.
// Motivo numero 1 - Abbiamo ridotto l'espressione
// ad un blocco di parentesi: "(...)"
// Motivo numero 2 - Siamo arrivati al punto in cui
// expr è un semplice numero
// In ambedue i casi risolve la is_nb().
return (is_nb(expr));
}
// la find_last_op() ha trovato l'operatore a precedenza richiesta
// e ne ha restituito l'indice
save = expr[pos];
expr[pos] = 0; // sullo stesso operatore divido l'espressione in due parti...
i = isOperat(save); // ... e, individuato l'operatore,...
// ...eseguo il calcolo
return (operatori[i].calcola(eval_expr(expr, p), eval_expr(expr+pos+1, p+1)));
}
static void sostituisciCostanti(register char *s) {
char save[300];
register int i;
while(*s) {
if (*s == '#') {
for (i=0; costanti[i].ident; i++) {
if (strncmp(s+1, costanti[i].ident, strlen(costanti[i].ident))==0) {
strcpy(save, s + strlen(costanti[i].ident) + 1);
sprintf(s, "%lf", costanti[i].valore);
s += strlen(s);
strcpy(s, save);
break;
}
}
if (costanti[i].ident == NULL)
s++;
continue;
}
s++;
}
}
static void strnospc(register char *s) {
register char *tmp;
while(*s) {
if ((*s == ' ') || (*s == '\t'))
for(tmp=s; *tmp; tmp++)
*tmp = *(tmp+1);
else s++;
}
}
static int checkPar(register char *s) {
int par = 0;
while(*s) {
if (*s == '(') par++;
if (*s == ')') par--;
if (par < 0) return(-1);
s++;
}
return(par);
}
static int analizzaSintassi(char *s) {
if (*s == '\0')
return(-1);
// faccio un controllo superficiale sulle parentesi
if (checkPar(s))
return(-1);
// Faccio dei controlli (il minimo...)
// ...
// Sarebbe necessario fare un po' di analisi sintattica...
// ...
return(0);
}
static void error(char *funct, short int type) {
err.funct = funct;
err.flag = type;
longjmp(jmp, 1);
}
errorType * EvaluateExpression(char *expr, double *result) {
err.flag = 0;
err.funct = 0;
strnospc(expr); // elimino gli spazi per evitare problemi
if (analizzaSintassi(expr)) {
err.flag = SYNTAXERROR;
return (&err);
}
if (setjmp(jmp) == 0) {
sostituisciCostanti(expr);
*result = eval_expr(expr, 1);
}
return(&err);
}
#endif