1 /*  **************************************************************************
2  *
3  * --- File: read_token.c
4  *
5  * --- Purpose: provides a function for reading tokens from stdin.
6  *
7  * --- Copyright (C) Guido Gonzato, guido@ibogeo.df.unibo.it
8  *
9  * --- Last updated: 3 May 2001
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  *
25  * ************************************************************************ */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <math.h>
32 #include "gexpr.h"
33 
34 #define	ZERO	1E-10F
35 #define N_ELEMS(vet) (sizeof(vet) / sizeof(vet[0]))
36 
37 /* ----- */
38 
39 static int get_char (void);
40 static void unget_char (int);
41 static int binsearch (const char *[], int, const char *);
42 static void find_out (TOKEN *);
43 static double fact (double);	/* these functions are handy */
44 static double my_rand (double);
45 static double rnd (double);
46 static double sqr (double);
47 
48 static int cur_pos = 0;		/* current position */
49 static BOOL input_over = FALSE;
50 static int last_function;
51 
52 static const char *s_constants[] =
53 {"M_1_PI", "M_2_PI", "M_2_SQRTPI", "M_E", "M_LN10", "M_LN2", "M_LOG10E",
54  "M_LOG2E", "M_PI", "M_PI_2", "M_PI_4", "M_SQRT1_2", "M_SQRT2", "PI", "PI2"
55 };
56 
57 static double v_constants[] =
58 {0.31830988618379067154, 0.63661977236758134308,
59  1.12837916709551257390, 2.7182818284590452354,
60  2.30258509299404568402, 0.69314718055994530942,
61  0.43429448190325182765, 1.4426950408889634074,
62  3.14159265358979323846, 1.57079632679489661923,
63  0.78539816339744830962, 0.70710678118654752440,
64  1.41421356237309504880, 3.14159265358979323846,
65  1.57079632679489661923
66 };
67 static const int n_constants = 15;
68 
69 enum func
70 {ACOS, ASIN, ATAN, ATAN2, CEIL, COS, COSH, EXP, FABS,
71  FACT, FLOOR, FMOD, LDEXP, LOG, LOG10, POW, RAND, RND,
72  SIN, SINH, SQR, SQRT, TAN, TANH
73 };
74 
75 static const char *s_functions[] =
76 {"acos", "asin", "atan", "atan2", "ceil", "cos", "cosh", "exp", "fabs",
77  "fact", "floor", "fmod", "ldexp", "log", "log10", "pow", "rand", "rnd",
78  "sin", "sinh", "sqr", "sqrt", "tan", "tanh"
79 };
80 static const int n_functions = 24;
81 
82 static short f_arguments[] =
83 {1, 1, 1, 2, 1, 1, 1, 1, 1,
84  1, 1, 2, 2, 1, 1, 2, 1, 1,
85  1, 1, 1, 1, 1, 1
86 };
87 
88 static const char *s_commands[] = {"base", "dec", "decimals", "help", "quit"
89 };
90 static const int n_commands = 5;
91 
92 typedef double (*FDPTR)();
93 
94 static FDPTR v_functions[] =
95 {acos, asin, atan, atan2, ceil, cos, cosh, exp, fabs,
96  fact, floor, fmod, ldexp, log, log10, pow, my_rand, rnd,
97  sin, sinh, sqr, sqrt, tan, tanh
98 };
99 
100 /* ----- */
101 
get_char(void)102 int get_char(void)
103 {
104   int ch;
105 
106   if (input_over)
107     ch = EOF;
108   else {
109     if (input_source == STDIN)
110       ch = getchar();
111     else {
112       ch = expression_source[cur_pos];
113       cur_pos++;
114       if (ch == '\0') {
115 	ch = '\n';
116 	input_over = TRUE;
117       }
118     }
119   }
120 
121   return ch;
122 
123 } /* get_char() */
124 
125 /* ----- */
126 
unget_char(int ch)127 void unget_char(int ch)
128 {
129 
130   if (input_source == STDIN)
131     ungetc(ch, stdin);
132   else {
133     cur_pos--;
134     if (input_over)
135       input_over = FALSE;
136   }
137 
138 } /* unget_char() */
139 
140 /* ----- */
141 
check_token(TOKEN_TYPE t1,TOKEN_TYPE t2)142 void check_token(TOKEN_TYPE t1, TOKEN_TYPE t2)
143 {
144 
145   char s[100];
146   char *whats_missing[] = {"(none)", "(none)", "end", "end of input",
147       "end of line", "`,'", "number", "identifier", "constant",
148       "function", "command", "(none)", "`+'", "`-'", "`*'", "`/'",
149       "`('", "`)'"
150   };
151 
152   if (t1 != t2) {
153     sprintf(s, "%s expected", whats_missing[(int) t2]);
154     error(s, t1);
155   }
156 
157 } /* check_token() */
158 
159 /* ----- */
160 
161 /* read_token() reads the next available token from stdin */
162 
read_token(void)163 TOKEN read_token(void)
164 {
165   int ch, i = 0;
166   static TOKEN tok;	/* token read */
167 
168   if (token_read == TRUE) {
169     token_read = FALSE;
170     return(tok);
171   }
172 
173   /* otherwise, read a new token */
174 
175   tok.type = T_NONE;
176 
177   /* skip spaces - don't use isspace(), because it skips '\n' */
178 
179   while ((ch = get_char()) == ' ')
180     ;
181 
182   switch (ch) {
183 
184     /* check T_END */
185 
186    case 'q':
187    case EOF:
188     tok.type = T_END;
189     break;
190 
191     /* check T_COMMA */
192 
193    case ',':
194     tok.type = T_COMMA;
195     break;
196 
197     /* check EOL */
198 
199    case '\n':
200     tok.type = T_EOL;
201     break;
202 
203     /* check if digit */
204 
205    case '0':
206    case '1':
207    case '2':
208    case '3':
209    case '4':
210    case '5':
211    case '6':
212    case '7':
213    case '8':
214    case '9':
215    case '.':
216     tok.type = T_DIGIT;
217     tok.string[i++] = ch;
218     tok.string[i] = '\0';
219     while (isdigit(ch = get_char())
220 	   || ch == '.' || ch == 'E'
221 	   || ch == '-'                  /* e.g. 1.2E5 */
222 	   || ch == 'b' || ch == 'x'     /* e.g. 0x23, 0b1101 */
223 	   || (ch >= 'a' && ch <= 'f'))  /* e.g. 0xffa3 */
224       if (i < MAX_TOKEN_LENGTH) {
225   	tok.string[i++] = ch;
226 	tok.string[i] = '\0';
227       }
228       else {
229 	tok.type = T_ERROR; 	/* digit too long */
230 	break;
231       }
232     unget_char(ch);
233     break;
234 
235     /* check if symbol */
236 
237    case '+':
238    case '-':
239    case '*':
240    case '/':
241    case '(':
242    case ')':
243     tok.type = T_MISC;
244     tok.string[i++] = ch;
245     tok.string[i] = '\0';
246     break;
247 
248    case '=':
249    case '<':
250    case '>':
251     tok.type = T_REL;
252     tok.string[i++] = ch;
253     ch = get_char();	/* must be (for =) or can be (for < >) '=' */
254     if (tok.string[0] == '=') {
255       if (ch != '=') {
256         tok.type = T_ERROR;
257         error("'\"' expected.", tok.type);
258       }
259       else {
260 	tok.string[i++] = ch;
261         tok.string[i] = '\0';
262       }
263     } /* if (tok.string[0] == '=') */
264     else {
265       if (ch == '=') {
266         tok.string[i++] = ch;
267         tok.string[i] = '\0';
268       }
269       else {
270 	unget_char(ch);
271 	tok.string[i++] = ch;
272         tok.string[i] = '\0';
273       }
274     } /* else */
275     break;
276 
277    default:
278     /* check if alphanumeric */
279     if (isalpha(ch)) {
280       tok.type = T_IDENT;
281       tok.string[i++] = ch;
282       tok.string[i] = '\0';
283       while (isalpha(ch = get_char()) || isdigit(ch) || ch == '_')
284         if (i < MAX_TOKEN_LENGTH) {
285 	  tok.string[i++] = ch;
286 	  tok.string[i] = '\0';
287         }
288         else {
289 	  tok.type = T_ERROR; 	/* identifier too long */
290 	  break;
291         }
292       unget_char(ch);
293     } /* if (isalpha(ch) ... */
294     /* the rest is just plain wrong! */
295     else {
296       tok.type = T_ERROR;
297       error("unknown token.", tok.type);
298     }
299 
300   } /* switch */
301 
302   /* now check if the token is ok  */
303 
304   find_out(&tok);
305   return tok;
306 
307 } /* read_token() */
308 
309 /* ----- */
310 
311 /* find_out() checks the tokens and assigns values */
312 
find_out(TOKEN * tok)313 static void find_out(TOKEN *tok)
314 {
315   char *err, *substr;
316   char misc[] = "+-*/()";
317   char rel[] = "==<=< >=> ";
318   int position;
319 
320   /* if it's a digit, check it */
321 
322   if (tok->type == T_DIGIT) {
323     tok->value = my_strtod(tok->string, &err);
324     if (*err != '\0')
325       tok->type = T_ERROR;
326   }
327 
328   /* if it's an identifier, check what it is */
329 
330   if (tok->type == T_IDENT) { /* is this a constant?  */
331     position = binsearch(s_constants, N_ELEMS(s_constants), tok->string);
332     if (position != -1) {
333       tok->type = T_CONST;
334       tok->value = v_constants[position];
335     }
336     else { /* is this a command? */
337       position = binsearch(s_commands, N_ELEMS(s_commands), tok->string);
338       if (position != -1)
339 	tok->type = T_COMMAND;
340       else { /* is this a function? */
341 	position = binsearch(s_functions, N_ELEMS(s_functions), tok->string);
342 	if (position != -1) {
343 	  last_function = position;
344 	  tok->type = T_FUNCTION;
345 	  tok->args = f_arguments[position];
346 	}
347 	else
348 	  tok->type = T_ERROR;
349       } /* else  - function */
350     } /* else - command */
351   } /* if (tok->type == T_IDENT) */
352 
353   /* if it belongs to the "misc", check it's ok */
354 
355   if (tok->type == T_MISC) {
356     substr = (char *) strchr(misc, tok->string[0]);
357     if (substr == NULL)  /* token not existent  - !!! redundant */
358       tok->type = T_ERROR;
359     else
360       tok->type = T_MISC + 1 + (substr - misc);
361   }
362 
363   /* if it belongs to the relationals, check it's ok */
364 
365   if (tok->type == T_REL) {
366     substr = (char *) strstr(rel, tok->string);
367     if (substr == NULL)  /* token not existent  - !!! redundant */
368       tok->type = T_ERROR;
369     else
370       tok->type = T_REL + 1 + (substr - rel) / 2;
371   }
372 
373 } /* find_out() */
374 
375 /* ----- */
376 
377 /* binsearch() is similar to bsearch(), but returns the position of the
378  * target (or -1 if not found)
379  */
380 
binsearch(const char * a[],int size,const char * target)381 static int binsearch(const char *a[], int size, const char *target)
382 {
383 
384   int i, j, m;
385 
386   i = 0;
387   j = size - 1;
388   m = (i + j) / 2;
389 
390   do {
391 
392     m = (i + j) / 2;
393 
394     if (strcmp(target, a[m]) > 0)
395       i = m + 1;
396     else
397       j = m - 1;
398 
399   } while ( (strcmp(target, a[m]) != 0) && (i <= j));
400 
401   if (strcmp(target, a[m]) == 0)
402     return m;
403   else
404     return -1;
405 
406 } /* binsearch() */
407 
408 /* ----- */
409 
function_value(short args,double a1,double a2)410 double function_value(short args, double a1, double a2)
411 {
412   double s;
413 
414   if (args == 1) {
415     switch (last_function) {
416      case ACOS:
417      case ASIN: if ( !(-1 <= a1 && a1 <= 1))
418 	error("argument must be >= -1 and <= 1", T_NONE);
419       break;
420      case LOG:
421      case LOG10: if (a1 <= ZERO)
422 	error("argument must be > 0", T_NONE);
423       break;
424      case SQRT: if (a1 < 0.0)
425 	error("argument must be >= 0", T_NONE);
426       break;
427      case TAN: if (fabs(cos(a1)) < 0.0000000001) /* !!! */
428 	error("function not defined at this value", T_NONE);
429     } /* switch */
430     s = v_functions[last_function](a1);
431   } /* if (args == 1) */
432   else {
433     switch (last_function) {
434      case ATAN2:
435      case FMOD: if (a2 == ZERO) /* !!! */
436 	error("second argument must be != 0", T_NONE);
437       break;
438      case POW: if (a1 < ZERO && (a2 - floor(a2) > ZERO)) /* !!! */
439 	error("if arg1 < 0, arg2 must be an integer", T_NONE);
440     }
441     if (last_function == LDEXP)
442       s = v_functions[last_function](a1, (int) a2);
443     else
444       s = v_functions[last_function](a1, a2);
445   } /* else */
446 
447   return s;
448 
449 } /* function_value() */
450 
451 /* ----- */
452 
fact(double n)453 static double fact(double n)
454 {
455   int i;
456   double f = 1;
457 
458   for (i = 1; i <= n; i++)
459     f *= i;
460 
461   return f;
462 
463 } /* fact() */
464 
465 /* ----- */
466 
my_rand(double d)467 static double my_rand(double d)
468 {
469 
470   srand ((unsigned int) d);
471   return (double) rand();
472 
473 } /* my_rand() */
474 
475 
rnd(double d)476 static double rnd(double d)
477 {
478 
479   return (double) rand() / (double) RAND_MAX * d;
480 
481 } /* rnd() */
482 
483 /* ----- */
484 
sqr(double d)485 static double sqr(double d)
486 {
487 
488   return d*d;
489 
490 } /* sqr() */
491 
492 /* ----- */
493 
494 /* --- End of file read_token.c --- */
495