/* ************************************************************************** * * --- File: read_token.c * * --- Purpose: provides a function for reading tokens from stdin. * * --- Copyright (C) Guido Gonzato, guido@ibogeo.df.unibo.it * * --- Last updated: 3 May 2001 * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * ************************************************************************ */ #include #include #include #include #include #include "gexpr.h" #define ZERO 1E-10F #define N_ELEMS(vet) (sizeof(vet) / sizeof(vet[0])) /* ----- */ static int get_char (void); static void unget_char (int); static int binsearch (const char *[], int, const char *); static void find_out (TOKEN *); static double fact (double); /* these functions are handy */ static double my_rand (double); static double rnd (double); static double sqr (double); static int cur_pos = 0; /* current position */ static BOOL input_over = FALSE; static int last_function; static const char *s_constants[] = {"M_1_PI", "M_2_PI", "M_2_SQRTPI", "M_E", "M_LN10", "M_LN2", "M_LOG10E", "M_LOG2E", "M_PI", "M_PI_2", "M_PI_4", "M_SQRT1_2", "M_SQRT2", "PI", "PI2" }; static double v_constants[] = {0.31830988618379067154, 0.63661977236758134308, 1.12837916709551257390, 2.7182818284590452354, 2.30258509299404568402, 0.69314718055994530942, 0.43429448190325182765, 1.4426950408889634074, 3.14159265358979323846, 1.57079632679489661923, 0.78539816339744830962, 0.70710678118654752440, 1.41421356237309504880, 3.14159265358979323846, 1.57079632679489661923 }; static const int n_constants = 15; enum func {ACOS, ASIN, ATAN, ATAN2, CEIL, COS, COSH, EXP, FABS, FACT, FLOOR, FMOD, LDEXP, LOG, LOG10, POW, RAND, RND, SIN, SINH, SQR, SQRT, TAN, TANH }; static const char *s_functions[] = {"acos", "asin", "atan", "atan2", "ceil", "cos", "cosh", "exp", "fabs", "fact", "floor", "fmod", "ldexp", "log", "log10", "pow", "rand", "rnd", "sin", "sinh", "sqr", "sqrt", "tan", "tanh" }; static const int n_functions = 24; static short f_arguments[] = {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1 }; static const char *s_commands[] = {"base", "dec", "decimals", "help", "quit" }; static const int n_commands = 5; typedef double (*FDPTR)(); static FDPTR v_functions[] = {acos, asin, atan, atan2, ceil, cos, cosh, exp, fabs, fact, floor, fmod, ldexp, log, log10, pow, my_rand, rnd, sin, sinh, sqr, sqrt, tan, tanh }; /* ----- */ int get_char(void) { int ch; if (input_over) ch = EOF; else { if (input_source == STDIN) ch = getchar(); else { ch = expression_source[cur_pos]; cur_pos++; if (ch == '\0') { ch = '\n'; input_over = TRUE; } } } return ch; } /* get_char() */ /* ----- */ void unget_char(int ch) { if (input_source == STDIN) ungetc(ch, stdin); else { cur_pos--; if (input_over) input_over = FALSE; } } /* unget_char() */ /* ----- */ void check_token(TOKEN_TYPE t1, TOKEN_TYPE t2) { char s[100]; char *whats_missing[] = {"(none)", "(none)", "end", "end of input", "end of line", "`,'", "number", "identifier", "constant", "function", "command", "(none)", "`+'", "`-'", "`*'", "`/'", "`('", "`)'" }; if (t1 != t2) { sprintf(s, "%s expected", whats_missing[(int) t2]); error(s, t1); } } /* check_token() */ /* ----- */ /* read_token() reads the next available token from stdin */ TOKEN read_token(void) { int ch, i = 0; static TOKEN tok; /* token read */ if (token_read == TRUE) { token_read = FALSE; return(tok); } /* otherwise, read a new token */ tok.type = T_NONE; /* skip spaces - don't use isspace(), because it skips '\n' */ while ((ch = get_char()) == ' ') ; switch (ch) { /* check T_END */ case 'q': case EOF: tok.type = T_END; break; /* check T_COMMA */ case ',': tok.type = T_COMMA; break; /* check EOL */ case '\n': tok.type = T_EOL; break; /* check if digit */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': tok.type = T_DIGIT; tok.string[i++] = ch; tok.string[i] = '\0'; while (isdigit(ch = get_char()) || ch == '.' || ch == 'E' || ch == '-' /* e.g. 1.2E5 */ || ch == 'b' || ch == 'x' /* e.g. 0x23, 0b1101 */ || (ch >= 'a' && ch <= 'f')) /* e.g. 0xffa3 */ if (i < MAX_TOKEN_LENGTH) { tok.string[i++] = ch; tok.string[i] = '\0'; } else { tok.type = T_ERROR; /* digit too long */ break; } unget_char(ch); break; /* check if symbol */ case '+': case '-': case '*': case '/': case '(': case ')': tok.type = T_MISC; tok.string[i++] = ch; tok.string[i] = '\0'; break; case '=': case '<': case '>': tok.type = T_REL; tok.string[i++] = ch; ch = get_char(); /* must be (for =) or can be (for < >) '=' */ if (tok.string[0] == '=') { if (ch != '=') { tok.type = T_ERROR; error("'\"' expected.", tok.type); } else { tok.string[i++] = ch; tok.string[i] = '\0'; } } /* if (tok.string[0] == '=') */ else { if (ch == '=') { tok.string[i++] = ch; tok.string[i] = '\0'; } else { unget_char(ch); tok.string[i++] = ch; tok.string[i] = '\0'; } } /* else */ break; default: /* check if alphanumeric */ if (isalpha(ch)) { tok.type = T_IDENT; tok.string[i++] = ch; tok.string[i] = '\0'; while (isalpha(ch = get_char()) || isdigit(ch) || ch == '_') if (i < MAX_TOKEN_LENGTH) { tok.string[i++] = ch; tok.string[i] = '\0'; } else { tok.type = T_ERROR; /* identifier too long */ break; } unget_char(ch); } /* if (isalpha(ch) ... */ /* the rest is just plain wrong! */ else { tok.type = T_ERROR; error("unknown token.", tok.type); } } /* switch */ /* now check if the token is ok */ find_out(&tok); return tok; } /* read_token() */ /* ----- */ /* find_out() checks the tokens and assigns values */ static void find_out(TOKEN *tok) { char *err, *substr; char misc[] = "+-*/()"; char rel[] = "==<=< >=> "; int position; /* if it's a digit, check it */ if (tok->type == T_DIGIT) { tok->value = my_strtod(tok->string, &err); if (*err != '\0') tok->type = T_ERROR; } /* if it's an identifier, check what it is */ if (tok->type == T_IDENT) { /* is this a constant? */ position = binsearch(s_constants, N_ELEMS(s_constants), tok->string); if (position != -1) { tok->type = T_CONST; tok->value = v_constants[position]; } else { /* is this a command? */ position = binsearch(s_commands, N_ELEMS(s_commands), tok->string); if (position != -1) tok->type = T_COMMAND; else { /* is this a function? */ position = binsearch(s_functions, N_ELEMS(s_functions), tok->string); if (position != -1) { last_function = position; tok->type = T_FUNCTION; tok->args = f_arguments[position]; } else tok->type = T_ERROR; } /* else - function */ } /* else - command */ } /* if (tok->type == T_IDENT) */ /* if it belongs to the "misc", check it's ok */ if (tok->type == T_MISC) { substr = (char *) strchr(misc, tok->string[0]); if (substr == NULL) /* token not existent - !!! redundant */ tok->type = T_ERROR; else tok->type = T_MISC + 1 + (substr - misc); } /* if it belongs to the relationals, check it's ok */ if (tok->type == T_REL) { substr = (char *) strstr(rel, tok->string); if (substr == NULL) /* token not existent - !!! redundant */ tok->type = T_ERROR; else tok->type = T_REL + 1 + (substr - rel) / 2; } } /* find_out() */ /* ----- */ /* binsearch() is similar to bsearch(), but returns the position of the * target (or -1 if not found) */ static int binsearch(const char *a[], int size, const char *target) { int i, j, m; i = 0; j = size - 1; m = (i + j) / 2; do { m = (i + j) / 2; if (strcmp(target, a[m]) > 0) i = m + 1; else j = m - 1; } while ( (strcmp(target, a[m]) != 0) && (i <= j)); if (strcmp(target, a[m]) == 0) return m; else return -1; } /* binsearch() */ /* ----- */ double function_value(short args, double a1, double a2) { double s; if (args == 1) { switch (last_function) { case ACOS: case ASIN: if ( !(-1 <= a1 && a1 <= 1)) error("argument must be >= -1 and <= 1", T_NONE); break; case LOG: case LOG10: if (a1 <= ZERO) error("argument must be > 0", T_NONE); break; case SQRT: if (a1 < 0.0) error("argument must be >= 0", T_NONE); break; case TAN: if (fabs(cos(a1)) < 0.0000000001) /* !!! */ error("function not defined at this value", T_NONE); } /* switch */ s = v_functions[last_function](a1); } /* if (args == 1) */ else { switch (last_function) { case ATAN2: case FMOD: if (a2 == ZERO) /* !!! */ error("second argument must be != 0", T_NONE); break; case POW: if (a1 < ZERO && (a2 - floor(a2) > ZERO)) /* !!! */ error("if arg1 < 0, arg2 must be an integer", T_NONE); } if (last_function == LDEXP) s = v_functions[last_function](a1, (int) a2); else s = v_functions[last_function](a1, a2); } /* else */ return s; } /* function_value() */ /* ----- */ static double fact(double n) { int i; double f = 1; for (i = 1; i <= n; i++) f *= i; return f; } /* fact() */ /* ----- */ static double my_rand(double d) { srand ((unsigned int) d); return (double) rand(); } /* my_rand() */ static double rnd(double d) { return (double) rand() / (double) RAND_MAX * d; } /* rnd() */ /* ----- */ static double sqr(double d) { return d*d; } /* sqr() */ /* ----- */ /* --- End of file read_token.c --- */