4.4BSD snapshot (revision 8.1)
[unix-history] / usr / src / contrib / sc / lex.c
CommitLineData
4978d704
KB
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"
40typedef 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;
48extern YYSTYPE yylval;
49extern int VMS_read_raw; /*sigh*/
50#else /* VMS */
51#include "y.tab.h"
52#endif /* VMS */
53
54char *strtof();
55
56jmp_buf wakeup;
57jmp_buf fpe_buf;
58
59struct key {
60 char *key;
61 int val;
62};
63
64struct key experres[] = {
65#include "experres.h"
66 0, 0};
67
68struct key statres[] = {
69#include "statres.h"
70 0, 0};
71
72yylex ()
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
197int
198atocol (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
215initkbd()
216{}
217
218kbd_again()
219{}
220
221resetkbd()
222{}
223
224#ifndef VMS
225
226nmgetch()
227{
228 return (toascii(getchar()));
229}
230
231#else /* VMS */
232
233nmgetch()
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
268VMS_MSG (status)
269int 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
301struct key_map {
302 char *k_str;
303 char k_val;
304 char k_index;
305};
306
307struct key_map km[N_KEY];
308
309char keyarea[N_KEY*30];
310
311char *tgetstr();
312char *getenv();
313char *ks;
314char ks_buf[20];
315char *ke;
316char ke_buf[20];
317
318#ifdef TIOCSLTC
319struct ltchars old_chars, new_chars;
320#endif
321
322char 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
329charout(c)
330int c;
331{
332 (void)putchar(c);
333}
334
335initkbd()
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
387void
388kbd_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
398void
399resetkbd()
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
409nmgetch()
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
483initkbd()
484{
485 keypad(stdscr, TRUE);
486}
487
488void
489kbd_again()
490{
491 keypad(stdscr, TRUE);
492}
493
494void
495resetkbd()
496{
497 keypad(stdscr, FALSE);
498}
499
500nmgetch()
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
537void
538#endif
539time_out(signo)
540int 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
549void
550#endif
551fpe_trap(signo)
552int 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
564char *
565strtof(p, res)
566register char *p;
567double *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}