/* **************************************************************************
*
* --- 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 <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#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 --- */
syntax highlighted by Code2HTML, v. 0.9.1