lvmsoglia

Math expression parser c/c++

Aug 25th, 2013
122
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 17.95 KB | None | 0 0
  1. //math equation parser - analyzes a mathematical expression or equation contained in a string variable, and solves the expression or equation.
  2. //by: Luiz Vinicius Soglia - email: [email protected]
  3.  
  4. //SEE THE FUNCTION main() FOR UNDERSTAND HOW USE THIS MATH PARSER FOR EVALUATE AND SOLVE EQUATIONS AND MATH EXPRESSIONS
  5.  
  6. //Comments in brazilian portuguese
  7.  
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11. #include <math.h>
  12. #include <errno.h>
  13.  
  14. //==========================================================================================================
  15.  
  16. //funcao math_parser - tradutor matematico de expressoes e equacoes.
  17. //utiliza chamadas recursivas para interpretar e transformar a string em um resultado numerico.
  18. //OBS: antes de uma string ser traduzida por essa funcao, a string deve passar pela funcao verifica_equacao(), para retirar possiveis erros.
  19.  
  20. //fx e a string que contem a equacao. n e o tamanho da string fx.
  21. //i inicia com zero e representa a atual posicao do indice do vetor na string fx.
  22. //x e o valor numerico da variavel.
  23. //prioridade indica a prioridade entre as operacoes: 0 = + -, 1 = *, 2 = /, 3 = ^, na ordem crescente de prioridade.
  24. //prioridade assume -1 para indicar que durante o calculo de uma funcao (ex: sin(), cos(), log()) somente o que esta
  25. //dentro dos parenteses deve ser lido e retornado para o calculo da funcao.
  26. //error se torna diferente de zero se houve erro de execucao, como extrapolacao de dominio de uma funcao.
  27.  
  28. //constantes, operacoes e funcoes suportadas:
  29. //+, -, *, /, ^, e, pi, sin(), cos(), tan(), asin(), acos(), atan(), log(), ln(), abs(), sqrt(), variavel x
  30.  
  31. float math_parser (const char *fx, const int n, int *i, const float x, int prioridade, int *error)
  32. {
  33.     float result=0.0, aux;
  34.     int k, e=0;
  35.     char str[64];
  36.    
  37.     errno=0; //variavel global que guarda os codigos de erros numericos (EDOM e ERANGE)
  38.     //EDOM = erro de dominio e ERANGE = erro de extrapolacao de limitacao de uma variavel
  39.    
  40.     while (*i<n && *error==0)
  41.     {        
  42.         if (fx[*i] > 47 && fx[*i] < 58) //procura somente numeros - na tabela ASCII os numeros 0-9 vao de 48 a 57
  43.         {
  44.              k=0;
  45.              do
  46.              {
  47.                  str[k] = fx[*i];
  48.                  k++;
  49.                  *i = *i + 1;      
  50.              }
  51.              while ( ( (fx[*i] > 47 && fx[*i] < 58) || fx[*i] == '.') && *i<n ); //enquanto for numeros e ponto
  52.              str[k] = '\0';
  53.              e = sscanf (str,"%f",&result); //transforma string em float
  54.         }      
  55.        
  56.         //nova verificao de fim da string, por seguranca, e verifica se a funcao sscanf converteu string em float corretamente
  57.         if (*i>=n || e==EOF)
  58.         {return result;}
  59.        
  60.         switch (fx[*i]) //caractere atual da string que contem a equacao
  61.         {  
  62.             case '(': //abre parentese
  63.             if (prioridade >= 0) {*i = *i + 1; result = math_parser (fx, n, i, x, 0, error); *i = *i + 1;}
  64.             //no caso de funcoes - prioridade -1 - retornar somente o que esta dentro dos parenteses
  65.             else {*i = *i + 1; result = math_parser (fx, n, i, x, 0, error); *i = *i + 1; return result;}
  66.             break;
  67.            
  68.             case ')': //fecha parentese - finalizar funcao
  69.             return result;
  70.            
  71.             case '-': //subtracao - prioridade 0
  72.             if (prioridade <= 0) {*i = *i + 1; result = result - math_parser (fx, n, i, x, 1, error);}
  73.             else {return result;} break;
  74.            
  75.             case '+': //soma - prioridade 0
  76.             if (prioridade <= 0) {*i = *i + 1; result = result + math_parser (fx, n, i, x, 1, error);}
  77.             else {return result;} break;
  78.            
  79.             case '*': //multiplicacao - prioridade 1
  80.             if (prioridade <= 1)
  81.             {
  82.                 *i = *i + 1;
  83.                 if (fx[*i] == '-') //se existir um sinal de menos apos a multiplicacao, ex: 4*-2 = -8
  84.                 {*i = *i + 1; result = result * (-math_parser (fx, n, i, x, 2, error));}
  85.                 else
  86.                 {result = result * math_parser (fx, n, i, x, 2, error);}
  87.             }
  88.             else {return result;} break;
  89.            
  90.             case '/': //divisao - prioridade 2
  91.             if (prioridade <= 2)
  92.             {  
  93.                 *i = *i + 1;
  94.                 if (fx[*i] == '-')
  95.                 {*i = *i + 1; aux = -math_parser (fx, n, i, x, 3, error);} //se existir um sinal de menos apos a divisao, ex: 8/-2 = -4
  96.                 else
  97.                 {aux = math_parser (fx, n, i, x, 3, error);}
  98.                 if (aux == 0.0) //divisao por zero, notifica erro
  99.                 {*error=1; return 1;}
  100.                 else
  101.                 {result = result / aux;}
  102.             }
  103.             else {return result;} break;
  104.            
  105.             case '^': //exponenciacao - prioridade 3
  106.             *i = *i + 1;
  107.             if (fx[*i] == '-') //se existir um sinal de menos apos a potenciacao, ex: 4^-2 = 1/16 = 0.0625
  108.             {*i = *i + 1; result = pow (result, -math_parser (fx, n, i, x, 3, error));}
  109.             else
  110.             {result = pow (result, math_parser (fx, n, i, x, 3, error));}
  111.             if (errno == EDOM || errno == ERANGE) {*error=1; errno=0; return 1;} //se houve erro de dominio ou range
  112.             break;
  113.            
  114.             case 'x': //substitui a variavel x por seu valor numerico
  115.             *i = *i + 1; result = x; break;
  116.        
  117.             case 'e': //substitui a constante e (numero de Euler) por 2.71828182846
  118.             *i = *i + 1; result = 2.71828182846; break;
  119.        
  120.             case 'p': //substitui a constante pi por 3.14159265359
  121.             *i = *i + 2;  result = 3.14159265359; break;
  122.            
  123.             //funcoes trigonometricas, sqrt() e abs() - prioridade -1
  124.            
  125.             case 'c': //funcao cosseno = cos()
  126.             *i = *i + 3; result = cos (math_parser (fx, n, i, x, -1, error)); break;
  127.            
  128.             case 's': //funcao seno = sin()
  129.             if (fx[*i + 1] == 'i')
  130.             {*i = *i + 3; result = sin (math_parser (fx, n, i, x, -1, error));} else{
  131.            
  132.             if (fx[*i + 1] == 'q') //funcao raiz quadrada = sqrt()
  133.             {
  134.                 *i = *i + 4; result = sqrt (math_parser (fx, n, i, x, -1, error));
  135.                 if (errno == EDOM) //se houve erro de dominio
  136.                 {*error=1; errno=0; return 1;}
  137.             }}
  138.             break;
  139.            
  140.             case 't': //funcao tangente = tan()
  141.             *i = *i + 3; result = tan (math_parser (fx, n, i, x, -1, error)); break;
  142.            
  143.             case 'a':
  144.             if (fx[*i + 1] == 'c') //funcao arco cosseno = acos()
  145.             {*i = *i + 4; result = acos (math_parser (fx, n, i, x, -1, error));} else{
  146.            
  147.             if (fx[*i + 1] == 's') //funcao arco seno = asin()
  148.             {*i = *i + 4; result = asin (math_parser (fx, n, i, x, -1, error));} else{
  149.            
  150.             if (fx[*i + 1] == 't') //funcao arco tangente = atan()
  151.             {*i = *i + 4; result = atan (math_parser (fx, n, i, x, -1, error));} else{
  152.            
  153.             if (fx[*i + 1] == 'b') //funcao modulo, valor absoluto = abs()
  154.             {*i = *i + 3; result = fabs (math_parser (fx, n, i, x, -1, error));}
  155.             }}}
  156.             if (errno == EDOM) {*error=1; errno=0; return 1;} //se houve erro de dominio
  157.             break;
  158.            
  159.             //funcoes logaritmicas - prioridade -1
  160.            
  161.             case 'l':
  162.             if (fx[*i + 1] == 'o') //funcao logaritmo na base 10 = log()
  163.             {*i = *i + 3; result = log10 (math_parser (fx, n, i, x, -1, error));} else{
  164.            
  165.             if (fx[*i + 1] == 'n') //funcao logaritmo natural, base e, = ln()
  166.             {*i = *i + 2; result = log (math_parser (fx, n, i, x, -1, error));}
  167.             }
  168.             if (errno == EDOM || errno == ERANGE) {*error=1; errno=0; return 1;} //se houve erro de dominio ou range
  169.             break;
  170.            
  171.             //ignora espacos vazios
  172.             case ' ':
  173.             *i = *i + 1; break;
  174.            
  175.             //caso nao haja nenhuma opcao valida
  176.             default: *error=1; return result; //nenhuma opcao valida, houve erro de digitacao na equacao
  177.         }
  178.     }
  179.    
  180.     return result;
  181. }
  182.  
  183. //==========================================================================================================
  184.  
  185. //verifica se a equacao foi digitada corretamente e tenta corrigir alguns problemas de digitacao
  186. //retorna zero caso nao encontre erros
  187. //retorna 1 caso haja erro de digitacao de funcoes matematicas e operadores
  188. //retorna 2 caso nao haja o mesmo numero de parenteses abertos e fechados
  189.  
  190. int verifica_equacao (char *fx)
  191. {
  192.     int n, i, abre_parentese=0, fecha_parentese=0;
  193.     char str[256];
  194.    
  195.     n = strlen(fx); //tamanho da string
  196.    
  197.     //retira espacos em branco e troca virgula por ponto (o separador decimal deve ser um ponto)
  198.     i=0;
  199.     while (i<n)
  200.     {
  201.         if (fx[i] == ' ')
  202.         {
  203.             //sequencia que retira um espaco ' ' da posicao i
  204.             strncpy(str,fx,i);
  205.             str[i] = '\0';
  206.             strcat(str,fx+(i+1));
  207.             strcpy(fx,str);
  208.             i--;
  209.             n--;
  210.         }
  211.         else
  212.         {if (fx[i] == ',') {fx[i] = '.';}} //troca virgula por ponto
  213.         i++;
  214.     }
  215.    
  216.     //procura caracteres e funcoes desconhecidas
  217.     i=0;
  218.     while (i<n)
  219.     {
  220.         if (fx[i] < 48 || fx[i] > 57) //verifica se nao sao numeros - na tabela ASCII os numeros 0-9 vao de 48 a 57
  221.         {
  222.         switch(fx[i])
  223.         {
  224.             case 'x': case 'X': fx[i]='x'; break;
  225.             case '(': abre_parentese++; break;
  226.             case ')': fecha_parentese++; break;
  227.            
  228.             case '-':
  229.             case '+':
  230.             case '*':
  231.             case '/':
  232.             case '^':
  233.             //testa se existe operador repetido ou expressao invalida do tipo: 2-/2, 4+^2, 3**2, 3/*4, 4^*1, 4*), 5+)
  234.             if (fx[i+1] == '*' || fx[i+1] == '/' || fx[i+1] == '^' || fx[i+1] == ')')
  235.             {return 1;}
  236.             if (fx[i+1] == '+') //retira sinal positivo desnecessario, como 2*+3, 6/+4, 5^+3
  237.             {
  238.                 //sequencia que retira '+' desnecessario da posicao i+1
  239.                 strncpy(str,fx,i+1);
  240.                 str[i+1] = '\0';
  241.                 strcat(str,fx+(i+2));
  242.                 strcpy(fx,str);
  243.                 i--;
  244.                 n--;
  245.             }
  246.             break;
  247.            
  248.             case '.': break;
  249.             case 'e': case 'E': fx[i]='e'; break; //numero de Euler
  250.            
  251.             case 'p': case 'P': //pi
  252.             if (toupper(fx[i + 1]) == 'I')
  253.             {fx[i]='p'; fx[i+1]='i'; i=i+1;} else {return 1;} break;
  254.            
  255.             case 'c': case 'C': //cos()
  256.             if (toupper(fx[i + 1]) == 'O' && toupper(fx[i + 2]) == 'S')
  257.             {fx[i]='c'; fx[i+1]='o'; fx[i+2]='s'; i=i+2;} else {return 1;} break;
  258.            
  259.             case 's': case 'S':
  260.             if ((toupper(fx[i + 1]) == 'I' || toupper(fx[i + 1]) == 'E') && toupper(fx[i + 2]) == 'N') //sin() ou sen()
  261.             {fx[i]='s'; fx[i+1]='i'; fx[i+2]='n'; i=i+2; break;}
  262.            
  263.             if (toupper(fx[i + 1]) == 'Q' && toupper(fx[i + 2]) == 'R' && toupper(fx[i + 3]) == 'T') //sqrt()
  264.             {fx[i]='s'; fx[i+1]='q'; fx[i+2]='r'; fx[i+3]='t'; i=i+3; break;}
  265.             return 1;
  266.            
  267.             case 't': case 'T': //tan()
  268.             if (toupper(fx[i + 1]) == 'A' && toupper(fx[i + 2]) == 'N')
  269.             {fx[i]='t'; fx[i+1]='a'; fx[i+2]='n'; i=i+2;} else {return 1;} break;
  270.            
  271.             case 'a': case 'A':
  272.             if (toupper(fx[i + 1]) == 'C' && toupper(fx[i + 2]) == 'O' && toupper(fx[i + 3]) == 'S') //acos()
  273.             {fx[i]='a'; fx[i+1]='c'; fx[i+2]='o'; fx[i+3]='s'; i=i+3; break;}
  274.            
  275.             //asin() ou asen()
  276.             if (toupper(fx[i + 1]) == 'S' && (toupper(fx[i + 2]) == 'I' || toupper(fx[i + 2]) == 'E') && toupper(fx[i + 3]) == 'N')
  277.             {fx[i]='a'; fx[i+1]='s'; fx[i+2]='i'; fx[i+3]='n'; i=i+3; break;}
  278.            
  279.             if (toupper(fx[i + 1]) == 'T' && toupper(fx[i + 2]) == 'A' && toupper(fx[i + 3]) == 'N') //atan()
  280.             {fx[i]='a'; fx[i+1]='t'; fx[i+2]='a'; fx[i+3]='n'; i=i+3; break;}
  281.            
  282.             if (toupper(fx[i + 1]) == 'B' && toupper(fx[i + 2]) == 'S') //abs()
  283.             {fx[i]='a'; fx[i+1]='b'; fx[i+2]='s'; i=i+2; break;}
  284.             return 1;
  285.        
  286.             case 'l': case 'L':
  287.             if (toupper(fx[i + 1]) == 'O' && toupper(fx[i + 2]) == 'G') //log()
  288.             {fx[i]='l'; fx[i+1]='o'; fx[i+2]='g'; i=i+2; break;}
  289.             if (toupper(fx[i + 1]) == 'N') //ln()
  290.             {fx[i]='l'; fx[i+1]='n'; i=i+1; break;}
  291.             return 1;
  292.            
  293.             //caso nao haja nenhuma opcao valida
  294.             default: return 1; //nenhuma opcao valida, houve erro de digitacao na equacao
  295.         }
  296.         }
  297.         i++;    
  298.     }
  299.    
  300.     //se o numero de parenteses abertos nao for igual aos fechados retorna erro
  301.     if (abre_parentese != fecha_parentese)
  302.     {return 2;}
  303.    
  304.     //verifica se depois do numero ou ')' nao ha uma operacao, se nao ha interpreta como uma multiplicacao. exemplo: 23x se torna 23*x
  305.     i=0;
  306.     while (i<n)
  307.     {  
  308.         //procura somente numeros (na tabela ASCII os numeros 0-9 vao de 48 a 57), x, e, pi, ')'
  309.         if ((fx[i] > 47 && fx[i] < 58) || fx[i] == 'x' || fx[i] == 'e' || fx[i] == 'p' || fx[i] == ')')
  310.         {
  311.              if (fx[i] > 47 && fx[i] < 58)
  312.              {
  313.                  do {i++;} while (((fx[i] > 47 && fx[i] < 58) || fx[i] == '.') && i<n);
  314.              }
  315.              else
  316.              {
  317.                  if (fx[i] == 'p') {i++;} //para pular 'i' de 'pi'
  318.                  i++;
  319.              }
  320.              
  321.              if (i<n && (fx[i]!='-' && fx[i]!='+' && fx[i]!='*' && fx[i]!='/' && fx[i]!='^' && fx[i]!=')' && fx[i]!=' ' && fx[i]!='\0'))
  322.              {
  323.                  //sequencia que insere um '*' na posicao i
  324.                  strncpy(str,fx,i);
  325.                  str[i] = '\0';
  326.                  strcat(str,"*");
  327.                  strcat(str,fx+i);
  328.                  strcpy(fx,str);
  329.                  n++;
  330.              }
  331.         }
  332.         if (fx[i]!=')') //se for ')' deve continuar na mesma posicao, para avaliar o que se tem apos o ')'
  333.         {i++;}
  334.     }
  335.     return 0; //retorna zero caso nao encontre erros
  336. }
  337.  
  338. //==========================================================================================================
  339.  
  340. //interpreta_equacao - funcao que transforma uma string em um resultado numerico utilizando a funcao math_parser().
  341. //ela facilita o uso do tradutor matematico math_parser(), diminuindo o numero de argumentos necessarios.
  342.  
  343. //fx eh a string contendo a funcao ou expressao que se deseja resolver.
  344. //x eh o valor numerico da variavel (se for uma expressao, como por exemplo 5+2*3, sem utilizar x, essa variavel nao influencia no calculo.
  345. //result guarda o resultado numerico da expressao ou equacao.
  346.  
  347. //A funcao retorna 0 caso haja sucesso, e um inteiro diferente de zero caso ocorra erro
  348.  
  349. //constantes, operacoes e funcoes suportadas:
  350. //+, -, *, /, ^, e, pi, sin(), cos(), tan(), asin(), acos(), atan(), log(), ln(), abs(), sqrt(), variavel x
  351.  
  352. int interpreta_equacao (const char *fx, const float x, float *result)
  353. {
  354.     int i=0, n, error=0;
  355.     n = strlen(fx);
  356.     *result = math_parser (fx, n, &i, x, 0, &error);
  357.     return error;
  358. }
  359.  
  360. //==========================================================================================================
  361.  
  362. int main()
  363. {
  364.     float x, result;
  365.     char string_equation [256];
  366.     int error;
  367.  
  368.   printf ("Type a math expression or equation, example: 4*e^2-log(5)+4*sin(x+pi)+1");
  369.   printf ("\nType [exit] for exit.\nType [list] for see the list of the math operations,\nconstants and functions supported.\n\n");
  370.          
  371.   while (1) //infinite loop
  372.   {
  373.     do
  374.     {
  375.         printf ("type a expression or equation = ");
  376.         scanf("%255[^\n]", string_equation); fflush (stdin); //get a string containing a math expression or equation
  377.        
  378.         if (!strcmp(string_equation,"exit")) //exit
  379.         {return 0;}
  380.        
  381.         if (!strcmp(string_equation,"list")) //list of the math operations, constants and functions supported
  382.         {
  383.             printf ("\noperators and constants: +, -, *, /, ^, e, pi, variable x\n");
  384.             printf("functions: sin(),cos(),tan(),asin(),acos(),atan(),log(),ln(),abs(),sqrt()\n\n");
  385.             error=1; //only for don't exit of the loop
  386.         }
  387.        
  388.         else
  389.         {
  390.         //Function verifica_equacao() - check for typos in the expression or equation, mathematical errors, syntax. This function attempts to correct the errors.
  391.         //NOTE: mandatory use function verifica_equacao() to check for errors in the string before using the function interpreta_equacao() to resolve it.
  392.             error = verifica_equacao (string_equation); //find and fix typos, return 0 on success
  393.            
  394.             if (error == 1)
  395.             {printf ("\nerror! typos found!\n\n");} //typos found
  396.             if (error == 2)
  397.             {printf ("\nunbalanced parenthesis!\n\n");} //unbalanced parenthesis
  398.         }
  399.     }
  400.     while (error != 0);
  401.    
  402.     printf ("\nx = "); //get the value of the variable x,
  403.     scanf ("%f", &x);  //if not there is a variable x in the equation, the value entered here does not influence anything
  404.    
  405.     //function interpreta_equacao - interprets the equation contained in the string,
  406.     //apply the variable x, and stores the value in the variable result, returns zero on success.
  407.     error = interpreta_equacao (string_equation, x, &result);
  408.    
  409.     if (error == 0) //on success
  410.     {printf ("\nresult = %f\n\n", result);}
  411.     else
  412.     {printf ("\nthere was error domain of the function f (x) or runtime error\n\n");}
  413.     fflush (stdin);
  414.   }
  415.   return 0;
  416. }
Advertisement
Add Comment
Please, Sign In to add comment