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