quantum-espresso/UtilXlib/eval_infix.c

710 lines
19 KiB
C

/* Copyright (C) 2008 by www.guidealgoritmi.it
Author: Vincenzo Lo Cicero.
e-mail: vincenzolocicero@guidealgoritmi.it
http://www.guidealgoritmi.it
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
This version of EvalInfix includes a wrapper to allow calls from
fortran code (written by Lorenzo Paulatto, 2008).
An example F90 program follows:
PROGRAM use_ex
implicit none
character(len=256) :: expr
integer :: ierr
real(8) :: result
real(8),external :: eval_infix
expr = "3 * 3"
result = eval_infix(ierr, expr)
if (ierr == 0) then
write(*,*) result, expr
else
stop
endif
END PROGRAM
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
/* #pragma warning( disable : 4996 ) */
#define MAXOP 100 /* dimensione massima di un operando o operatore */
#define MAXSTACK 100 /* dimensione massima dello stack */
typedef int BOOL;
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
typedef enum tagTokenType
{
EOL, UNKNOWN, VALUE, OPAREN, CPAREN, EXP, UPLUS, UMINUS, MULT, DIV, PLUS, MINUS
}TokenTypeEnum;
typedef struct tagToken
{
TokenTypeEnum Type;
char str[54];
double Value;
}Token;
struct Precedence
{
int inputSymbol;
int topOfStack;
} PREC_TABLE [ ] =
{
{ 0, -1 }, {-1, -1}, { 0, 0 }, /* EOL, UNKNOWN, VALUE */
{ 100, 0 }, { 0, 99 }, /* OPAREN, CPAREN */
{ 6, 5 }, {6, 5}, {6, 5}, /* EXP, UPLUS, UMINUS */
{ 3, 4 }, { 3, 4 }, /* MULT, DIV */
{ 1, 2 }, { 1, 2 } /* PLUS, MINUS */
};
int nNextPos = 0;
TokenTypeEnum PreviousTokenType = EOL;
int sp_op = 0;
Token stack_op[MAXSTACK]; /* stack degli operatori */
/* Operazioni sullo stack degli operatori */
void push_op(Token, char *);
Token pop_op(char *);
Token top_op(char *);
BOOL is_empty_op();
int sp_val = 0;
double stack_val[MAXSTACK]; /* stack degli operandi */
/* Operazioni sullo stack degli operandi */
void push_val(double, char *);
double pop_val(char *);
double top_val(char *);
BOOL is_empty_val();
TokenTypeEnum GetNextToken(const char *str, Token *token, BOOL bIsInfix);
double BinaryOperation(double left, double right, char op, char *strError);
/*BOOL InfixToPostfix(const char *strInfix, char *strPostfix, char *strError);
double EvalPostfix(const char *strExpression, char *strError); */
double EvalInfix(const char *strExpression, char *strError);
/* inserisce un elemento nello stack degli operatori */
/* In caso di errore viene riportato un messaggio nel parametro strError */
/* In assenza di errori, il parametro strError è impostato ala stringa vuota = "" */
void push_op(Token Tok, char *strError)
{
strcpy(strError, "");
if (sp_op < MAXSTACK)
stack_op[sp_op++] = Tok;
else
sprintf(strError, "Error: operators stack is full, cannot add more elements %c\n", Tok.str[0]);
}
/* Estrae e ritorna un elemento dallo stack degli operatori */
/* In caso di errore viene riportato un messaggio nel parametro strError */
/* In assenza di errori, il parametro strError è impostato ala stringa vuota = "" */
Token pop_op(char *strError)
{
Token tok_temp;
strcpy(strError, "");
if (sp_op > 0)
return stack_op[--sp_op];
else
{
sprintf(strError, "Error: missing operator\n");
strcpy(tok_temp.str, "");
tok_temp.Type = UNKNOWN;
return tok_temp;
}
}
/* Ritorna il valore in cima allo stack degli operatori senza estrarlo */
/* In caso di errore viene riportato un messaggio nel parametro strError */
/* In assenza di errori, il parametro strError è impostato ala stringa vuota = "" */
Token top_op(char *strError)
{
Token tok_temp;
strcpy(strError, "");
if (sp_op >= 0)
return stack_op[sp_op - 1];
else
{
sprintf(strError, "Error: missing operator\n");
strcpy(tok_temp.str, "");
tok_temp.Type = UNKNOWN;
return tok_temp;
}
}
/* Ritorna un valore diverso da zero se lo stack degli operatori è vuoto */
BOOL is_empty_op()
{
if ( sp_op > 0 )
return FALSE;
else
return TRUE;
}
/* Inserisce un elemento nello stack degli operandi */
/* In caso di errore viene riportato un messaggio nel parametro strError */
/* In assenza di errori, il parametro strError è impostato ala stringa vuota = "" */
void push_val(double c, char *strError)
{
strcpy(strError, "");
if (sp_val < MAXSTACK)
stack_val[sp_val++] = c;
else
sprintf(strError, "Error: values stack is full: cannot add more elements %g\n", c);
}
/* Estrae e ritorna un elemento dallo stack degli operandi */
/* In caso di errore viene riportato un messaggio nel parametro strError */
/* In assenza di errori, il parametro strError è impostato ala stringa vuota = "" */
double pop_val(char *strError)
{
strcpy(strError, "");
if (sp_val > 0)
return stack_val[--sp_val];
else
{
sprintf(strError, "Error: missing operand\n");
return 0;
}
}
/* ritorna il valore in cima allo stack degli operandi senza estrarlo */
/* In caso di errore viene riportato un messaggio nel parametro strError */
/* In assenza di errori, il parametro strError è impostato ala stringa vuota = "" */
double top_val(char *strError)
{
strcpy(strError, "");
if (sp_val > 0)
return stack_val[sp_val - 1];
else
{
sprintf(strError, "Error top: values stack is empty\n");
return 0;
}
}
/* ritorna un valore diverso da zero se lo stack degli operandi è vuoto */
BOOL is_empty_val()
{
if ( sp_val > 0 )
return FALSE;
else
return TRUE;
}
/* ritorna un valore diverso da zero per "e", "E", "d o "D", o se il carattere prima lo era */
BOOL is_scientific(char strChar)
{
static BOOL was_scientific = FALSE;
if (was_scientific)
{
was_scientific = FALSE;
return TRUE;
}
else if (strChar == 'e' || strChar == 'E'||
strChar == 'd' || strChar == 'D')
{
was_scientific = TRUE;
return TRUE;
}
else if ( isdigit(strChar) ){
was_scientific = FALSE;
return TRUE;
}
else
{
was_scientific = FALSE;
return FALSE;
}
}
/* Analizzatore lessicale */
TokenTypeEnum GetNextToken(const char *str, Token *token, BOOL bIsInfix)
{
int i;
char strToken[MAXOP];
while ( 1 )
{
while ( str[nNextPos++] == ' ' )
;
--nNextPos;
if ( str[nNextPos] == '\0' )
{
token->Type = EOL;
strcpy(token->str, "\n");
nNextPos = 0;
PreviousTokenType = EOL;
return EOL;
}
else if ( is_scientific(str[nNextPos]) )
{
i = 0;
while ( is_scientific(strToken[i++] = str[nNextPos++]) )
if (strToken[i-1] == 'd' || strToken[i-1] == 'D') strToken[i-1] = 'e';
if ( str[nNextPos - 1] == '.' )
{
while ( is_scientific(strToken[i++] = str[nNextPos++]) )
if (strToken[i-1] == 'd' || strToken[i-1] == 'D') strToken[i-1] = 'e';
strToken[i - 1] = '\0';
--nNextPos;
token->Type = VALUE;
strcpy(token->str, strToken);
token->Value = atof(strToken);
return VALUE;
}
else
{
strToken[i - 1] = '\0';
--nNextPos;
token->Type = VALUE;
strcpy(token->str, strToken);
token->Value = atof(strToken);
return VALUE;
}
}
else if ( str[nNextPos] == '.' )
{
i = 0;
strToken[i++] = str[nNextPos++];
while ( is_scientific(strToken[i++] = str[nNextPos++]) )
if (strToken[i-1] == 'd' || strToken[i-1] == 'D') strToken[i-1] = 'e';
strToken[i - 1] = '\0';
--nNextPos;
token->Type = VALUE;
strcpy(token->str, strToken);
token->Value = atof(strToken);
return VALUE;
}
else if ( str[nNextPos] == '(' )
{
token->Type = OPAREN;
strcpy(token->str, "(");
++nNextPos;
return OPAREN;
}
else if ( str[nNextPos] == ')' )
{
token->Type = CPAREN;
strcpy(token->str, ")");
++nNextPos;
return CPAREN;
}
else if ( str[nNextPos] == '+' )
{
strcpy(token->str, "+");
++nNextPos;
if ( !bIsInfix )
{
token->Type = PLUS;
return PLUS;
}
else
{
if ( PreviousTokenType == CPAREN || PreviousTokenType == VALUE )
{
token->Type = PLUS;
return PLUS;
}
else
{
token->Type = UPLUS;
return UPLUS;
}
}
}
else if ( str[nNextPos] == '-' )
{
strcpy(token->str, "-");
++nNextPos;
if ( !bIsInfix )
{
token->Type = MINUS;
return MINUS;
}
else
{
if ( PreviousTokenType == CPAREN || PreviousTokenType == VALUE )
{
token->Type = MINUS;
return MINUS;
}
else
{
token->Type = UMINUS;
return UMINUS;
}
}
}
else if ( str[nNextPos] == '~' )
{
strcpy(token->str, "~");
++nNextPos;
if ( !bIsInfix )
{
token->Type = UMINUS;
return UMINUS;
}
else
{
token->Type = UNKNOWN;
return UNKNOWN;
}
}
else if ( str[nNextPos] == '*' )
{
token->Type = MULT;
strcpy(token->str, "*");
++nNextPos;
return MULT;
}
else if ( str[nNextPos] == '/' )
{
token->Type = DIV;
strcpy(token->str, "/");
++nNextPos;
return DIV;
}
else if ( str[nNextPos] == '^' )
{
token->Type = EXP;
strcpy(token->str, "^");
++nNextPos;
return EXP;
}
else
{
token->Type = UNKNOWN;
token->str[0] = str[nNextPos];
token->str[1] = '\0';
++nNextPos;
return UNKNOWN;
}
}
return EOL;
}
/* Ritorna il risultato di un'operazione binaria */
/* In caso di errore viene riportato un messaggio nel parametro strError */
/* In assenza di errori, il parametro strError è impostato ala stringa vuota = "" */
double BinaryOperation(double left, double right, char op, char* strError)
{
strcpy(strError, "");
switch ( op )
{
case '-':
return left - right;
case '+':
return left + right;
case '*':
return left * right;
case '/':
if ( right == 0 )
{
sprintf(strError, "Error: division by zero!\n");
return 0.0;
}
else
return left / right;
case '^':
return pow(left, right);
default:
if ( op == '(' )
sprintf(strError, "Error: unbalanced brackets.\n");
else
sprintf(strError, "Error: unknown operator: %c\n", op);
return 0.0;
}
}
/* Calcola e restituisce il risultato di un'espressione in forma infissa */
double EvalInfix(const char *strExpression, char * strError)
{
Token tok;
Token tok_temp;
double left, right;
double dblRet;
strcpy(strError, "");
tok_temp.Type = EOL;
tok_temp.str[0] = '@';
tok_temp.str[1] = '\0';
push_op(tok_temp, strError);
if ( strError[0] != '\0' )
return 0.0;
left = right = 0.0;
while ( (PreviousTokenType = GetNextToken(strExpression, &tok, TRUE)) != EOL )
{
if ( tok.Type == UNKNOWN )
{
sprintf(strError, "Error: invalid token: %s\n", tok.str);
return 0.0;
}
else if ( tok.Type == VALUE )
{
push_val(tok.Value, strError);
if ( strError[0] != '\0' )
return 0.0;
}
else if ( tok.Type == OPAREN || tok.Type == UMINUS || tok.Type == UPLUS )
{
push_op(tok, strError);
if ( strError[0] != '\0' )
return 0.0;
}
else if ( tok.Type == CPAREN )
{
while ( top_op(strError).Type != OPAREN )
{
if ( strError[0] != '\0' )
return 0.0;
tok_temp = pop_op(strError);
if ( strError[0] != '\0' )
return 0.0;
if ( (tok_temp.Type == EOL) || (is_empty_op()) )
{
sprintf(strError, "Error: unbalanced brackets.\n");
return 0.0;
}
right = pop_val(strError);
if ( strError[0] != '\0' )
return 0.0;
if ( tok_temp.Type != UMINUS )
{
left = pop_val(strError);
if ( strError[0] != '\0' )
return 0.0;
dblRet = BinaryOperation(left, right, tok_temp.str[0], strError);
if ( strError[0] != '\0' )
return 0.0;
push_val(dblRet, strError);
if ( strError[0] != '\0' )
return 0.0;
}
else
{
push_val( -1 * right, strError );
if ( strError[0] != '\0' )
return 0.0;
}
}
pop_op(strError);
if ( strError[0] != '\0' )
return 0.0;
}
else
{
while ( PREC_TABLE[ top_op(strError).Type ].topOfStack >= PREC_TABLE[ tok.Type ].inputSymbol )
{
if ( strError[0] != '\0' )
return 0.0;
if ( top_op(strError).Type != UMINUS && top_op(strError).Type != UPLUS )
{
if ( strError[0] != '\0' )
return 0.0;
right = pop_val(strError);
if ( strError[0] != '\0' )
return 0.0;
left = pop_val(strError);
if ( strError[0] != '\0' )
return 0.0;
tok_temp = pop_op(strError);
if ( strError[0] != '\0' )
return 0.0;
dblRet = BinaryOperation(left, right, tok_temp.str[0], strError);
if ( strError[0] != '\0' )
return 0.0;
push_val(dblRet, strError);
if ( strError[0] != '\0' )
return 0.0;
}
else
{
if ( top_op(strError).Type == UMINUS )
{
if ( strError[0] != '\0' )
return 0.0;
right = pop_val(strError);
if ( strError[0] != '\0' )
return 0.0;
pop_op(strError);
if ( strError[0] != '\0' )
return 0.0;
push_val(-1 * right, strError);
if ( strError[0] != '\0' )
return 0.0;
}
else
{
pop_op(strError);
if ( strError[0] != '\0' )
return 0.0;
}
}
}
if ( tok.Type != EOL )
{
push_op(tok, strError);
if ( strError[0] != '\0' )
return 0.0;
}
}
}
while ( 1 )
{
tok_temp = pop_op(strError);
if ( strError[0] != '\0' )
return 0.0;
if ( tok_temp.Type == EOL )
break;
if ( tok_temp.Type != UPLUS )
{
right = pop_val(strError);
if ( strError[0] != '\0' )
return 0.0;
}
if ( tok_temp.Type != UMINUS && tok_temp.Type != UPLUS )
{
left = pop_val(strError);
if ( strError[0] != '\0' )
return 0.0;
dblRet = BinaryOperation(left, right, tok_temp.str[0], strError);
if ( strError[0] != '\0' )
return 0.0;
push_val(dblRet, strError);
if ( strError[0] != '\0' )
return 0.0;
}
else
{
push_val( -1 * right, strError );
if ( strError[0] != '\0' )
return 0.0;
}
}
dblRet = pop_val(strError);
if ( strError[0] != '\0' )
return 0.0;
if ( is_empty_val() )
{
return dblRet;
}
else
{
sprintf(strError, "Error: malformed expression.\n");
return 0.0;
}
}
double eval_infix( int *ierr, const char *strExpression, int len )
{
double result = 0.0;
char strHelper[257];
char strError[257];
int i;
/* maximum length of strExpression is 256 chars */
if (len>256) {
printf("[eval_infix.c] expression longer than 256 characters\n");
ierr[0] = 1;
return result;
}
/* it's safer to reformat strings for C, with null terminator '\0' */
for(i=0;i<len;i++) strHelper[i]=' ';
strHelper[len] = '\0';
for(i=0;i<len;i++) strHelper[i] = strExpression[i];
for(i=0;i<len;i++) strError[i] = ' ';
strError[len] = '\0';
result = EvalInfix(strHelper, strError);
if ( strError[0] != '\0' ) {
printf("[eval_infix.c] A parsing error occurred\n");
/*printf("input string: \n----\n%s\n----\n", strExpression);*/
printf("helper string:\n%s\n", strHelper);
printf("error code: \n%s\n", strError);
ierr[0] = 1;
}
else ierr[0] = 0;
return result;
}