xref: /original-bsd/contrib/sc/lex.c (revision eafa6506)
1 /*	SC	A Spreadsheet Calculator
2  *		Lexical analyser
3  *
4  *		original by James Gosling, September 1982
5  *		modifications by Mark Weiser and Bruce Israel,
6  *			University of Maryland
7  *
8  *              More mods Robert Bond, 12/86
9  *		More mods by Alan Silverstein, 3/88, see list of changes.
10  *		$Revision: 6.8 $
11  *
12  */
13 
14 
15 
16 #if defined(BSD42) || defined(BSD43)
17 #include <sys/ioctl.h>
18 #endif
19 
20 #ifdef IEEE_MATH
21 #include <ieeefp.h>
22 #endif /* IEEE_MATH */
23 
24 #include <curses.h>
25 #include <signal.h>
26 #include <setjmp.h>
27 #include "sc.h"
28 #include <ctype.h>
29 
30 #ifdef BSD42
31 #include <strings.h>
32 #else
33 #ifndef SYSIII
34 #include <string.h>
35 #endif
36 #endif
37 
38 #ifdef VMS
39 #include "gram_tab.h"
40 typedef union {
41     int ival;
42     double fval;
43     struct ent *ent;
44     struct enode *enode;
45     char *sval;
46     struct range_s rval;
47 } YYSTYPE;
48 extern YYSTYPE yylval;
49 extern int VMS_read_raw;   /*sigh*/
50 #else	/* VMS */
51 #include "y.tab.h"
52 #endif /* VMS */
53 
54 char *strtof();
55 
56 jmp_buf wakeup;
57 jmp_buf fpe_buf;
58 
59 struct key {
60     char *key;
61     int val;
62 };
63 
64 struct key experres[] = {
65 #include "experres.h"
66     0, 0};
67 
68 struct key statres[] = {
69 #include "statres.h"
70     0, 0};
71 
72 yylex ()
73 {
74     register char *p = line+linelim;
75     int ret;
76     while (isspace(*p)) p++;
77     if (*p == '\0') ret = -1;
78     else if (isalpha(*p)) {
79 	char *tokenst = p;
80 	register tokenl;
81 	register struct key *tblp;
82 	tokenl = 0;
83 	/*
84 	 * This picks up either 1 or 2 alpha characters (a column) or
85 	 * tokens with at least three leading alphas and '_' or digits
86 	 * (a function or token or command or a range name)
87 	*/
88 	while (isalpha(*p) || ((*p == '_') || isdigit(*p)) && (tokenl > 2)) {
89 	    p++;
90 	    tokenl++;
91 	}
92 	if (tokenl <= 2) { /* a COL is 1 or 2 char alpha
93 		(but not pi, ln, fv, pv, if -- this should be fixed!) */
94 	    if (tokenl == 2 && tokenst[0] == 'p' && tokenst[1] == 'i') {
95 		ret = K_PI;
96 	    } else if (tokenl == 2 && tokenst[0] == 'l' && tokenst[1] == 'n') {
97 		ret = K_LN;
98 	    } else if (tokenl == 2 && tokenst[0] == 'f' && tokenst[1] == 'v') {
99 		ret = K_FV;
100 	    } else if (tokenl == 2 && tokenst[0] == 'p' && tokenst[1] == 'v') {
101 		ret = K_PV;
102 	    } else if (tokenl == 2 && tokenst[0] == 'i' && tokenst[1] == 'f') {
103 		ret = K_IF;
104 
105 	    } else {
106 		ret = COL;
107 		yylval.ival = atocol (tokenst, tokenl);
108 	    }
109 	} else {
110 	    ret = WORD;
111 	    for (tblp = linelim ? experres : statres; tblp->key; tblp++)
112 		    if (((tblp->key[0]^tokenst[0])&0137)==0
113 		     && tblp->key[tokenl]==0) {
114 			register i = 1;
115 			while (i<tokenl && ((tokenst[i]^tblp->key[i])&0137)==0)
116 			    i++;
117 			if (i>=tokenl) {
118 			    ret = tblp->val;
119 			    break;
120 			}
121 		    }
122 	    if (ret==WORD) {
123 		struct range *r;
124 		if (r = find_range(tokenst, tokenl,
125 				   (struct ent *)0, (struct ent *)0)) {
126 		    yylval.rval.left = r->r_left;
127 		    yylval.rval.right = r->r_right;
128 		    if (r->r_is_range)
129 		        ret = RANGE;
130 		    else
131 			ret = VAR;
132 		} else {
133 		    linelim = p-line;
134 		    yyerror ("Unintelligible word");
135 		}
136 	    }
137 	}
138     } else if ((*p == '.') || isdigit(*p)) {
139 	double v = 0;
140 	int temp;
141 	char *nstart = p;
142 	if (*p != '.') {
143 	    do v = v*10 + (double)(*p-'0');
144 	    while (isdigit(*++p));
145 	}
146 	if (*p=='.' || *p == 'e' || *p == 'E') {
147 	    ret = FNUMBER;
148 	    p = strtof(nstart, &yylval.fval);
149 	} else {
150 	    /* A NUMBER must hold at least MAXROW and MAXCOL */
151 	    /* This is consistent with a short row and col in struct ent */
152 	    if (v > (double)32767 || v < (double)-32768) {
153 		ret = FNUMBER;
154 		yylval.fval = v;
155 	    } else {
156 		temp = (int)v;
157 		if((double)temp != v) {
158 		    ret = FNUMBER;
159 		    yylval.fval = v;
160 		} else {
161 		    ret = NUMBER;
162 		    yylval.ival = temp;
163 		}
164 	    }
165 	}
166     } else if (*p=='"') {
167 	char *ptr;
168         ptr = p+1;
169         while(*ptr && *ptr++ != '"');
170         ptr = xmalloc((unsigned)(ptr-p));
171 	yylval.sval = ptr;
172 	p += 1;
173 	while (*p && *p!='"') *ptr++ = *p++;
174 	*ptr = 0;
175 	if (*p) p += 1;
176 	ret = STRING;
177     } else if (*p=='[') {
178 	while (*p && *p!=']') p++;
179 	if (*p) p++;
180 	linelim = p-line;
181 	return yylex();
182     } else ret = *p++;
183     linelim = p-line;
184     return ret;
185 }
186 
187 
188 /*
189  * Given a token string starting with a symbolic column name and its valid
190  * length, convert column name ("A"-"Z" or "AA"-"ZZ") to a column number (0-N).
191  * Never mind if the column number is illegal (too high).  The procedure's name
192  * and function are the inverse of coltoa().
193  *
194  * Case-insensitivity is done crudely, by ignoring the 040 bit.
195  */
196 
197 int
198 atocol (string, len)
199 	char	*string;
200 	int	len;
201 {
202 	register int col;
203 
204 	col = (string [0] & 0137) - 'A';
205 
206 	if (len == 2)		/* has second char */
207 	    col = ((col + 1) * 26) + ((string [1] & 0137) - 'A');
208 
209 	return (col);
210 }
211 
212 
213 #ifdef SIMPLE
214 
215 initkbd()
216 {}
217 
218 kbd_again()
219 {}
220 
221 resetkbd()
222 {}
223 
224 #ifndef VMS
225 
226 nmgetch()
227 {
228     return (toascii(getchar()));
229 }
230 
231 #else /* VMS */
232 
233 nmgetch()
234 /*
235    This is not perfect, it doesn't move the cursor when goraw changes
236    over to deraw, but it works well enough since the whole sc package
237    is incredibly stable (loop constantly positions cursor).
238 
239    Question, why didn't the VMS people just implement cbreak?
240 
241    NOTE: During testing it was discovered that the DEBUGGER and curses
242    and this method of reading would collide (the screen was not updated
243    when continuing from screen mode in the debugger).
244 */
245 {
246     short c;
247     static int key_id=0;
248     int status;
249 #define VMScheck(a) {if (~(status = (a)) & 1) VMS_MSG (status);}
250 
251     if (VMS_read_raw) {
252       VMScheck(smg$read_keystroke (&stdkb->_id, &c, 0, 0, 0));
253     }
254     else
255        c = getchar();
256 
257     switch (c) {
258     case SMG$K_TRM_LEFT:  c = ctl('b'); break;
259     case SMG$K_TRM_RIGHT: c = ctl('f'); break;
260     case SMG$K_TRM_UP:    c = ctl('p'); break;
261     case SMG$K_TRM_DOWN:  c = ctl('n'); break;
262     default:   c = c & 0x7f;
263     }
264     return (c);
265 }
266 
267 
268 VMS_MSG (status)
269 int status;
270 /*
271    Routine to put out the VMS operating system error (if one occurs).
272 */
273 {
274 #include <descrip.h>
275    char errstr[81], buf[120];
276    $DESCRIPTOR(errdesc, errstr);
277    short int length;
278 #define err_out(msg) fprintf (stderr,msg)
279 
280 /* Check for no error or standard error */
281 
282    if (~status & 1) {
283       status = status & 0x8000 ? status & 0xFFFFFFF : status & 0xFFFF;
284       if (SYS$GETMSG(status, &length, &errdesc, 1, 0) == SS$_NORMAL) {
285          errstr[length] = '\0';
286          sprintf (buf, "<0x%x> %s", status, errdesc.dsc$a_pointer);
287          err_out (buf);
288       }
289       else
290          err_out ("System error");
291    }
292 }
293 #endif /* VMS */
294 
295 #else /*SIMPLE*/
296 
297 #if defined(BSD42) || defined (SYSIII) || defined(BSD43)
298 
299 #define N_KEY 4
300 
301 struct key_map {
302     char *k_str;
303     char k_val;
304     char k_index;
305 };
306 
307 struct key_map km[N_KEY];
308 
309 char keyarea[N_KEY*30];
310 
311 char *tgetstr();
312 char *getenv();
313 char *ks;
314 char ks_buf[20];
315 char *ke;
316 char ke_buf[20];
317 
318 #ifdef TIOCSLTC
319 struct ltchars old_chars, new_chars;
320 #endif
321 
322 char dont_use[] = {
323 	ctl('['), ctl('a'), ctl('b'), ctl('c'), ctl('e'), ctl('f'), ctl('g'), ctl('h'),
324 	ctl('i'), ctl('j'),  ctl('l'), ctl('m'), ctl('n'), ctl('p'), ctl('q'),
325 	ctl('r'), ctl('s'), ctl('t'), ctl('u'), ctl('v'),  ctl('w'), ctl('x'),
326 	ctl('z'), 0
327 };
328 
329 charout(c)
330 int c;
331 {
332 	(void)putchar(c);
333 }
334 
335 initkbd()
336 {
337     register struct key_map *kp;
338     register i,j;
339     char *p = keyarea;
340     char *ktmp;
341     static char buf[1024]; /* Why do I have to do this again? */
342 
343     if (tgetent(buf, getenv("TERM")) <= 0)
344 	return;
345 
346     km[0].k_str = tgetstr("kl", &p); km[0].k_val = ctl('b');
347     km[1].k_str = tgetstr("kr", &p); km[1].k_val = ctl('f');
348     km[2].k_str = tgetstr("ku", &p); km[2].k_val = ctl('p');
349     km[3].k_str = tgetstr("kd", &p); km[3].k_val = ctl('n');
350     ktmp = tgetstr("ks",&p);
351     if (ktmp)  {
352 	(void) strcpy(ks_buf, ktmp);
353 	ks = ks_buf;
354 	tputs(ks, 1, charout);
355     }
356     ktmp = tgetstr("ke",&p);
357     if (ktmp)  {
358 	(void) strcpy(ke_buf, ktmp);
359 	ke = ke_buf;
360     }
361 
362     /* Unmap arrow keys which conflict with our ctl keys   */
363     /* Ignore unset, longer than length 1, and 1-1 mapped keys */
364 
365     for (i = 0; i < N_KEY; i++) {
366 	kp = &km[i];
367 	if (kp->k_str && (kp->k_str[1] == 0) && (kp->k_str[0] != kp->k_val))
368 	    for (j = 0; dont_use[j] != 0; j++)
369 	        if (kp->k_str[0] == dont_use[j]) {
370 		     kp->k_str = (char *)0;
371 		     break;
372 		}
373     }
374 
375 
376 #ifdef TIOCSLTC
377     (void)ioctl(fileno(stdin), TIOCGLTC, (char *)&old_chars);
378     new_chars = old_chars;
379     if (old_chars.t_lnextc == ctl('v'))
380 	new_chars.t_lnextc = -1;
381     if (old_chars.t_rprntc == ctl('r'))
382 	new_chars.t_rprntc = -1;
383     (void)ioctl(fileno(stdin), TIOCSLTC, (char *)&new_chars);
384 #endif
385 }
386 
387 void
388 kbd_again()
389 {
390     if (ks)
391 	tputs(ks, 1, charout);
392 
393 #ifdef TIOCSLTC
394     (void)ioctl(fileno(stdin), TIOCSLTC, (char *)&new_chars);
395 #endif
396 }
397 
398 void
399 resetkbd()
400 {
401     if (ke)
402 	tputs(ke, 1, charout);
403 
404 #ifdef TIOCSLTC
405     (void)ioctl(fileno(stdin), TIOCSLTC, (char *)&old_chars);
406 #endif
407 }
408 
409 nmgetch()
410 {
411     register int c;
412     register struct key_map *kp;
413     register struct key_map *biggest;
414     register int i;
415     int almost;
416     int maybe;
417 
418     static char dumpbuf[10];
419     static char *dumpindex;
420 
421 #ifdef SIGVOID
422     void time_out();
423 #else
424     int time_out();
425 #endif
426 
427     if (dumpindex && *dumpindex)
428 	    return (*dumpindex++);
429 
430     c = toascii(getchar());
431     biggest = 0;
432     almost = 0;
433 
434     for (kp = &km[0]; kp < &km[N_KEY]; kp++) {
435 	if (!kp->k_str)
436 	    continue;
437 	if (c == kp->k_str[kp->k_index]) {
438 	    almost = 1;
439 	    kp->k_index++;
440 	    if (kp->k_str[kp->k_index] == 0) {
441 		c = kp->k_val;
442 	        for (kp = &km[0]; kp < &km[N_KEY]; kp++)
443 	            kp->k_index = 0;
444 	        return(c);
445 	    }
446 	}
447 	if (!biggest && kp->k_index)
448 	    biggest = kp;
449         else if (kp->k_index && biggest->k_index < kp->k_index)
450 	    biggest = kp;
451     }
452 
453     if (almost) {
454         (void) signal(SIGALRM, time_out);
455         (void) alarm(1);
456 
457 	if (setjmp(wakeup) == 0) {
458 	    maybe = nmgetch();
459 	    (void) alarm(0);
460 	    return(maybe);
461 	}
462     }
463 
464     if (biggest) {
465 	for (i = 0; i<biggest->k_index; i++)
466 	    dumpbuf[i] = biggest->k_str[i];
467 	if (!almost)
468 	    dumpbuf[i++] = c;
469 	dumpbuf[i] = '\0';
470 	dumpindex = &dumpbuf[1];
471 	for (kp = &km[0]; kp < &km[N_KEY]; kp++)
472 	    kp->k_index = 0;
473 	return (dumpbuf[0]);
474     }
475 
476     return(c);
477 }
478 
479 #endif
480 
481 #if defined(SYSV2) || defined(SYSV3)
482 
483 initkbd()
484 {
485     keypad(stdscr, TRUE);
486 }
487 
488 void
489 kbd_again()
490 {
491     keypad(stdscr, TRUE);
492 }
493 
494 void
495 resetkbd()
496 {
497     keypad(stdscr, FALSE);
498 }
499 
500 nmgetch()
501 {
502     register int c;
503 
504     c = getch();
505     switch (c) {
506     case KEY_LEFT:  c = ctl('b'); break;
507     case KEY_RIGHT: c = ctl('f'); break;
508     case KEY_UP:    c = ctl('p'); break;
509     case KEY_DOWN:  c = ctl('n'); break;
510 #ifdef KEY_C1
511 /* This stuff works for a wyse wy75 in ANSI mode under 5.3.  Good luck. */
512 /* It is supposed to map the curses keypad back to the numeric equiv. */
513     case KEY_C1:    c = '0'; break;
514     case KEY_A1:    c = '1'; break;
515     case KEY_B2:    c = '2'; break;
516     case KEY_A3:    c = '3'; break;
517     case KEY_F(5):  c = '4'; break;
518     case KEY_F(6):  c = '5'; break;
519     case KEY_F(7):  c = '6'; break;
520     case KEY_F(9):  c = '7'; break;
521     case KEY_F(10): c = '8'; break;
522     case KEY_F0:    c = '9'; break;
523     case KEY_C3:    c = '.'; break;
524     case KEY_ENTER: c = ctl('m'); break;
525 #endif
526     default:   c = toascii(c);
527     break;
528     }
529     return (c);
530 }
531 
532 #endif /* SYSV2 || SYSV3 */
533 
534 #endif /* SIMPLE */
535 
536 #ifdef SIGVOID
537 void
538 #endif
539 time_out(signo)
540 int signo;
541 {
542 #ifdef IEEE_MATH
543 	(void)fpsetsticky((fp_except)0); 		/* Clear exception */
544 #endif /* IEEE_MATH */
545     longjmp(wakeup, -1);
546 }
547 
548 #ifdef SIGVOID
549 void
550 #endif
551 fpe_trap(signo)
552 int signo;
553 {
554     longjmp(fpe_buf, 1);
555 }
556 
557 /*
558  * This converts a floating point number of the form
559  * [s]ddd[.d*][esd*]  where s can be a + or - and e is E or e.
560  * to floating point.
561  * p is advanced.
562  */
563 
564 char *
565 strtof(p, res)
566 register char *p;
567 double *res;
568 {
569     double acc;
570     int sign;
571     double fpos;
572     int exp;
573     int exps;
574 #ifdef SIGVOID
575     void (*sig_save)();
576 #else
577     int (*sig_save)();
578 #endif
579 
580     sig_save = signal(SIGFPE, fpe_trap);
581     if (setjmp(fpe_buf)) {
582 	error("Floating point exception\n");
583 	*res = 0.0;
584         (void) signal(SIGFPE, sig_save);
585 	return(p);
586     }
587     acc = 0.0;
588     sign = 1;
589     exp = 0;
590     exps = 1;
591     if (*p == '+')
592         p++;
593     else if (*p == '-') {
594         p++;
595         sign = -1;
596     }
597     while (isdigit(*p)) {
598         acc = acc * 10.0 + (double)(*p - '0');
599         p++;
600     }
601     if (*p == 'e' || *p == 'E') {
602 	    p++;
603         if (*p == '+')
604 	    p++;
605         else if (*p == '-') {
606 	    p++;
607 	    exps = -1;
608         }
609         while(isdigit(*p)) {
610 	    exp = exp * 10 + (*p - '0');
611 	    p++;
612         }
613     }
614     if (*p == '.') {
615 	fpos = 1.0/10.0;
616 	p++;
617 	while(isdigit(*p)) {
618 	    acc += (*p - '0') * fpos;
619 	    fpos *= 1.0/10.0;
620 	    p++;
621 	}
622     }
623     if (*p == 'e' || *p == 'E') {
624 	exp = 0;
625 	exps = 1;
626         p++;
627 	if (*p == '+')
628 	    p++;
629 	else if (*p == '-') {
630 	    p++;
631 	    exps = -1;
632 	}
633 	while(isdigit(*p)) {
634 	    exp = exp * 10 + (*p - '0');
635 	    p++;
636 	}
637     }
638     if (exp) {
639 	if (exps > 0)
640 	    while (exp--)
641 		acc *= 10.0;
642 	else
643 	    while (exp--)
644 		acc *= 1.0/10.0;
645     }
646     if (sign > 0)
647         *res = acc;
648     else
649 	*res = -acc;
650 
651     (void) signal(SIGFPE, sig_save);
652     return(p);
653 }
654