Having not used C since that time I am now rekindling my interest with the Raspberry Pi and can successfully write small programs using GCC, although I seem to have forgotten all of my C knowledge so am re-learning it (slowly!)
I would like to make use of this math parser once again but I am struggling to get it to compile successfully under GCC, presumably due to standards changing over time.
The first issue is #include <functions.h>, which is not found so I have removed this line and also added in #include <string.h> which seems to clear up quite a few problems, but I am stuck what to do next.
I would be really grateful if anyone could take a look, and maybe try compiling it and give me some pointers as to what the issues might be. I have put the original code below.
Many thanks
Neil
Code: Select all
/********************************************************************
* Parse.c (C) copyright 1987 John M. Olsen
*
* /| | /||| /\| | John M. Olsen
* \|()|\|\_ |||. \/|/)@|\_ | 1547 Jamestown Drive
* | | Salt Lake City, UT 84121-2051
* u-jmolse@ug.utah.edu or ...!{seismo,ihnp4}!utah-cs!utah-ug!u-jmolse
*
* This program or any significant portion of it may not be used in a
* commercial product without written permission of the author. (My
* permission is easy to get).
*
* Feel free to distribute this code as part of any public domain collection,
* or hack on it to make it more user friendly, as long as you try to
* document your changes as not being my code.
*
* This is a recursive descent exprsession parser, based very loosely on one
* found in Herbert Schildt's book "Advanced C", published by McGraw-Hill.
* It parses expressions by having each layer either recognize something that
* it can do, or passing it on to the next function which does higher
* precedence operators.
*
* It has very minimal error checking, and does not check for overflow or
* underflow on calculations. If it detects an error, it will give a message
* and what it thinks the correct result was up to that time.
*
* It converts expressions to lower case so it only needs to look for math
* function names spelled one way.
*
* It uses standard I/O, so it must be used from the CLI. This makes it
* very easy to port to other machines.
*
* Aztec instructions using fast floating point:
*
* cc parse.c
* ln parse.o -lm -lc
*
* Aztec 4.30a using IEEE double precision floating point: (Big difference!)
*
* cc parse.c +fi
* ln parse.o -lmx -lc
*
* It has also been (fairly) successfully compiled on a VAX1180, but it
* complained at expressions on the argument list. The only modification
* required was to comment out the #include <functions.h> command.
*
*********************************************************************/
#include <stdio.h>
#include <ctype.h>
#include <functions.h>
#include <math.h>
#define PI ((double)3.14159265358979)
#define E ((double)2.71828182845904)
#define TOK_SIZE 20 /* Tokens are no longer than this number. */
#define EXP_SIZE 80 /* Expressions no longer than this number. */
#define TRUE 1
#define FALSE 0
extern double atof(), binary(), fn_eval();
extern double parse(), parse2(), parse3(), parse4(), parse5();
/* Here is a simple calling program to test it. */
main (argc, argv)
int argc;
char *argv[];
{
char exprs[EXP_SIZE];
double ans;
int loop;
if(argc < 2)
{
ans = 0;
printf("Type '?' for help.\n");
printf("Enter exprsression: ");
gets (exprs);
}
else
{
exprs[0] = NULL;
for(loop = 1; loop < argc; loop++)
strcat(exprs, argv[loop]);
}
for(loop = 0; loop < strlen(exprs); loop++) /* convert to lower case */
if(isalpha(exprs[loop]))
exprs[loop] = tolower(exprs[loop]);
if(exprs[0] == '?' || exprs[0] == 'h')
{ /* help menu */
printf("Supported binary operators: + - / % ^\n");
printf("Supported unary operators: + -\n");
printf("Supported unary functions: sin, cos, tan,\n");
printf(" asin, acos, atan, sinh, cosh, tahn,\n");
printf(" log, logten, abs, sqrt, int.\n");
printf("Defined constants: pi, e.\n\n");
printf("Usage: %s (expression)\n", argv[0]);
printf(" or: %s ?\n", argv[0]);
exit(0);
}
ans = parse (exprs);
if(exprs[0])
printf("Bad expression.\n");
else
printf ("%f\n", ans);
}
/*******************************************************************
The main parser function. It parses + and -.
*******************************************************************/
double parse (exprs)
char exprs[];
{
char tok[TOK_SIZE]; /* this should only be a + or - character. */
double a;
a = parse2 (exprs);
while (exprs[0])
{
get_token (tok, exprs);
if (tok[0] == '+' || tok[0] == '-')
a = binary (a, parse2 (exprs), tok);
else
return (a);
}
return (a);
}
/*******************************************************************
The secondary parser, which parses * and /.
*******************************************************************/
double parse2 (exprs)
char *exprs;
{
char tok[TOK_SIZE];
double a;
a = parse3 (exprs);
while (exprs[0])
{
get_token (tok, exprs);
if (tok[0] == '*' || tok[0] == '/' || tok[0] == 'm'
|| tok[0] == 'd')
a = binary (a, parse3 (exprs), tok);
else
{
unget_tok (tok, exprs);
return (a);
}
}
return (a);
}
/*******************************************************************
The third parser, which parses ^.
*******************************************************************/
double parse3 (exprs)
char *exprs;
{
char tok[TOK_SIZE];
double a;
a = parse4 (exprs);
while (exprs[0])
{
get_token (tok, exprs);
if (tok[0]=='^')
a = (binary (a, parse4 (exprs), tok));
else
{
unget_tok (tok, exprs);
return (a);
}
}
return (a);
}
/*******************************************************************
The fourth parser, which parses unary minus and plus.
*******************************************************************/
double parse4 (exprs)
char *exprs;
{
char tok[TOK_SIZE];
get_token (tok, exprs);
if (tok[0] == '-')
return (-(parse4 (exprs)));
else if(tok[0] == '+')
return(parse4(exprs));
unget_tok (tok, exprs);
return (parse5 (exprs));
}
/*******************************************************************
The fifth parser, which parses functions and parentheses.
*******************************************************************/
double parse5 (exprs)
char *exprs;
{
char tok[TOK_SIZE], buff[EXP_SIZE];
double a;
get_fn (tok, exprs);
if (tok[0]) /* a function call. */
return (fn_eval (tok, exprs));
get_numb (tok, exprs);
if (tok[0]) /* A number. */
return (atof (tok));
if (exprs[0] == '(') /* a parenthesized exprsression. */
{ /*strip open paren, call parse, strip close paren. */
strcpy (buff, exprs + 1);
strcpy (exprs, buff);
a = parse (exprs);
if (exprs[0] == ')')
{
strcpy (buff, exprs + 1);
strcpy (exprs, buff);
}
else
{ /* Error if here. */
printf("Bad parentheses.\n");
}
return (a);
}
printf("Bad parentheses.\n");
}
/*******************************************************************
Return a pointer to a string with the token in it. A token may be
at most TOK_SIZE characters. All after that are tossed into the
bit bucket.
*******************************************************************/
get_token (tok, exprs)
char *tok, *exprs;
{
char temp[EXP_SIZE];
tok[0] = 0;
while (iswhite (exprs[0])) /* move from exprs[1] to exprs[0] */
{
strcpy (temp, exprs + 1);
strcpy (exprs, temp);
}
sscanf (exprs, "%1[+-*/^]", tok);
strcpy (temp, exprs + strlen (tok));
strcpy (exprs, temp);
}
/*******************************************************************
Get a number from the front of the exprsression.
*******************************************************************/
get_numb (tok, exprs)
char *tok, *exprs;
{
char temp[EXP_SIZE];
tok[0] = 0;
while (iswhite (exprs[0])) /* move from exprs[1] to exprs[0] */
{
strcpy (temp, exprs + 1);
strcpy (exprs, temp);
}
sscanf (exprs, "%[0123456789.]", tok);
strcpy (temp, exprs + strlen (tok));
strcpy (exprs, temp);
}
/*******************************************************************
Get a function name from the front of the exprsression.
*******************************************************************/
get_fn (tok, exprs)
char *tok, *exprs;
{
char temp[EXP_SIZE];
tok[0] = 0;
while (iswhite (exprs[0])) /* move from exprs[1] to exprs[0] */
{
strcpy (temp, exprs + 1);
strcpy (exprs, temp);
}
sscanf (exprs, "%[abcdefghijklmnopqrstuvwxyz]", tok);
strcpy (temp, exprs + strlen (tok));
strcpy (exprs, temp);
}
/*******************************************************************
Replace a token at the front of the exprsression.
*******************************************************************/
unget_tok (tok, exprs)
char *tok, *exprs;
{
char buff[EXP_SIZE];
strcpy (buff, exprs);
strcpy (exprs, tok);
strcat (exprs, buff);
}
/*******************************************************************
This fn returns true if the key given to it is white space.
*******************************************************************/
iswhite (key)
char key;
{
if (key == ' ' || key == 9 || key == 13)
return (1);
return (0);
}
/*******************************************************************
This fn returns (a operator b), or just a if an error occurs.
*******************************************************************/
double binary (a, b, tok)
double a, b;
char *tok;
{
int loop;
double c;
switch (tok[0])
{
case '+':
return (a + b);
case '-':
return (a - b);
case '*':
return (a * b);
case '/':
if (!near_zero (b)) /* if b != 0 */
return (a / b);
else
{ /* division by zero error message. */
printf("Division by zero error.\n");
}
return (a);
case '^': /* a to the b power. */
{
c = a;
if (near_zero (b))
return (a);
return(pow(a,b));
/* non-function if you don't like the pow function */
for (loop = (int) b - 1;loop > 0; --loop)
a = a * c;
return (a);
}
default:
printf("No such token as %c\n", tok[0]);
}
}
/*******************************************************************
Evaluates the function on the front of exprs.
*******************************************************************/
double fn_eval (tok, exprs)
char *exprs, *tok;
{
if (tok[0])
{
if (!strcmp(tok, "abs"))
return(fabs (parse5(exprs)));
if (!strcmp(tok, "sqrt"))
return(sqrt (parse5(exprs)));
if (!strcmp(tok, "tan"))
return(tan (parse5(exprs)));
if (!strcmp(tok, "sin"))
return(sin (parse5(exprs)));
if (!strcmp(tok, "cos"))
return(cos (parse5(exprs)));
if (!strcmp(tok, "atan"))
return(atan (parse5(exprs)));
if (!strcmp(tok, "asin"))
return(asin (parse5(exprs)));
if (!strcmp(tok, "acos"))
return(acos (parse5(exprs)));
if (!strcmp(tok, "tanh"))
return(tanh (parse5(exprs)));
if (!strcmp(tok, "sinh"))
return(sinh (parse5(exprs)));
if (!strcmp(tok, "cosh"))
return(cosh (parse5(exprs)));
if (!strcmp(tok, "pi"))
return(PI);
if (!strcmp(tok, "e"))
return(E);
if (!strcmp(tok, "log"))
return(log(parse5(exprs)));
if (!strcmp(tok, "logten"))
return(log10(parse5(exprs)));
if (!strcmp(tok, "int"))
return((double)((long)(parse5(exprs))));
}
printf("Could not find expression %s\n",tok);
unget_tok(tok, exprs);
}
/*******************************************************************
Returns true if num is near zero. It's used in division checks.
*******************************************************************/
near_zero (num)
double num;
{
if (fabs (num) < .000000001)
return (1);
return (0);
}