]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cpp/lex.c
kbdfs: simplfy
[plan9front.git] / sys / src / cmd / cpp / lex.c
1 #include <u.h>
2 #include <libc.h>
3 #include <stdio.h>
4 #include "cpp.h"
5
6 /*
7  * lexical FSM encoding
8  *   when in state state, and one of the characters
9  *   in ch arrives, enter nextstate.
10  *   States >= S_SELF are either final, or at least require special action.
11  *   In 'fsm' there is a line for each state X charset X nextstate.
12  *   List chars that overwrite previous entries later (e.g. C_ALPH
13  *   can be overridden by '_' by a later entry; and C_XX is the
14  *   the universal set, and should always be first.
15  *   States above S_SELF are represented in the big table as negative values.
16  *   S_SELF and S_SELFB encode the resulting token type in the upper bits.
17  *   These actions differ in that S_SELF doesn't have a lookahead char,
18  *   S_SELFB does.
19  *
20  *   The encoding is blown out into a big table for time-efficiency.
21  *   Entries have
22  *      nextstate: 6 bits; ?\ marker: 1 bit; tokentype: 9 bits.
23  */
24
25 #define MAXSTATE 32
26 #define ACT(tok,act)    ((tok<<7)+act)
27 #define QBSBIT  0100
28 #define GETACT(st)      (st>>7)&0x1ff
29
30 #define UTF2(c)         ((c)>=0xA0 && (c)<0xE0)         /* 2-char UTF seq */
31 #define UTF3(c)         ((c)>=0xE0 && (c)<0xF0)         /* 3-char UTF seq */
32
33 /* character classes */
34 #define C_WS    1
35 #define C_ALPH  2
36 #define C_NUM   3
37 #define C_EOF   4
38 #define C_XX    5
39
40 enum state {
41         START=0, NUM1, NUM2, NUM3, ID1, ST1, ST2, ST3, COM1, COM2, COM3, COM4,
42         CC1, CC2, WS1, PLUS1, MINUS1, STAR1, SLASH1, PCT1, SHARP1,
43         CIRC1, GT1, GT2, LT1, LT2, OR1, AND1, ASG1, NOT1, DOTS1,
44         S_SELF=MAXSTATE, S_SELFB, S_EOF, S_NL, S_EOFSTR,
45         S_STNL, S_COMNL, S_EOFCOM, S_COMMENT, S_EOB, S_WS, S_NAME
46 };
47
48 int     tottok;
49 int     tokkind[256];
50 struct  fsm {
51         int     state;          /* if in this state */
52         uchar   ch[4];          /* and see one of these characters */
53         int     nextstate;      /* enter this state if +ve */
54 };
55
56 /*const*/ struct fsm fsm[] = {
57         /* start state */
58         START,  { C_XX },       ACT(UNCLASS,S_SELF),
59         START,  { ' ', '\t', '\v', '\r' },      WS1,
60         START,  { C_NUM },      NUM1,
61         START,  { '.' },        NUM3,
62         START,  { C_ALPH },     ID1,
63         START,  { 'L' },        ST1,
64         START,  { '"' },        ST2,
65         START,  { '\'' },       CC1,
66         START,  { '/' },        COM1,
67         START,  { EOFC },       S_EOF,
68         START,  { '\n' },       S_NL,
69         START,  { '-' },        MINUS1,
70         START,  { '+' },        PLUS1,
71         START,  { '<' },        LT1,
72         START,  { '>' },        GT1,
73         START,  { '=' },        ASG1,
74         START,  { '!' },        NOT1,
75         START,  { '&' },        AND1,
76         START,  { '|' },        OR1,
77         START,  { '#' },        SHARP1,
78         START,  { '%' },        PCT1,
79         START,  { '[' },        ACT(SBRA,S_SELF),
80         START,  { ']' },        ACT(SKET,S_SELF),
81         START,  { '(' },        ACT(LP,S_SELF),
82         START,  { ')' },        ACT(RP,S_SELF),
83         START,  { '*' },        STAR1,
84         START,  { ',' },        ACT(COMMA,S_SELF),
85         START,  { '?' },        ACT(QUEST,S_SELF),
86         START,  { ':' },        ACT(COLON,S_SELF),
87         START,  { ';' },        ACT(SEMIC,S_SELF),
88         START,  { '{' },        ACT(CBRA,S_SELF),
89         START,  { '}' },        ACT(CKET,S_SELF),
90         START,  { '~' },        ACT(TILDE,S_SELF),
91         START,  { '^' },        CIRC1,
92
93         /* saw a digit */
94         NUM1,   { C_XX },       ACT(NUMBER,S_SELFB),
95         NUM1,   { C_NUM, C_ALPH, '.' }, NUM1,
96         NUM1,   { 'E', 'e' },   NUM2,
97         NUM1,   { '_' },        ACT(NUMBER,S_SELFB),
98
99         /* saw possible start of exponent, digits-e */
100         NUM2,   { C_XX },       ACT(NUMBER,S_SELFB),
101         NUM2,   { '+', '-' },   NUM1,
102         NUM2,   { C_NUM, C_ALPH },      NUM1,
103         NUM2,   { '_' },        ACT(NUMBER,S_SELFB),
104
105         /* saw a '.', which could be a number or an operator */
106         NUM3,   { C_XX },       ACT(DOT,S_SELFB),
107         NUM3,   { '.' },        DOTS1,
108         NUM3,   { C_NUM },      NUM1,
109
110         DOTS1,  { C_XX },       ACT(UNCLASS, S_SELFB),
111         DOTS1,  { C_NUM },      NUM1,
112         DOTS1,  { '.' },        ACT(ELLIPS, S_SELF),
113
114         /* saw a letter or _ */
115         ID1,    { C_XX },       ACT(NAME,S_NAME),
116         ID1,    { C_ALPH, C_NUM },      ID1,
117
118         /* saw L (start of wide string?) */
119         ST1,    { C_XX },       ACT(NAME,S_NAME),
120         ST1,    { C_ALPH, C_NUM },      ID1,
121         ST1,    { '"' },        ST2,
122         ST1,    { '\'' },       CC1,
123
124         /* saw " beginning string */
125         ST2,    { C_XX },       ST2,
126         ST2,    { '"' },        ACT(STRING, S_SELF),
127         ST2,    { '\\' },       ST3,
128         ST2,    { '\n' },       S_STNL,
129         ST2,    { EOFC },       S_EOFSTR,
130
131         /* saw \ in string */
132         ST3,    { C_XX },       ST2,
133         ST3,    { '\n' },       S_STNL,
134         ST3,    { EOFC },       S_EOFSTR,
135
136         /* saw ' beginning character const */
137         CC1,    { C_XX },       CC1,
138         CC1,    { '\'' },       ACT(CCON, S_SELF),
139         CC1,    { '\\' },       CC2,
140         CC1,    { '\n' },       S_STNL,
141         CC1,    { EOFC },       S_EOFSTR,
142
143         /* saw \ in ccon */
144         CC2,    { C_XX },       CC1,
145         CC2,    { '\n' },       S_STNL,
146         CC2,    { EOFC },       S_EOFSTR,
147
148         /* saw /, perhaps start of comment */
149         COM1,   { C_XX },       ACT(SLASH, S_SELFB),
150         COM1,   { '=' },        ACT(ASSLASH, S_SELF),
151         COM1,   { '*' },        COM2,
152         COM1,   { '/' },        COM4,
153
154         /* saw "/*", start of comment */
155         COM2,   { C_XX },       COM2,
156         COM2,   { '\n' },       S_COMNL,
157         COM2,   { '*' },        COM3,
158         COM2,   { EOFC },       S_EOFCOM,
159
160         /* saw the * possibly ending a comment */
161         COM3,   { C_XX },       COM2,
162         COM3,   { '\n' },       S_COMNL,
163         COM3,   { '*' },        COM3,
164         COM3,   { '/' },        S_COMMENT,
165         COM3,   { EOFC },       S_EOFCOM,
166
167         /* // comment */
168         COM4,   { C_XX },       COM4,
169         COM4,   { '\n' },       S_NL,
170         COM4,   { EOFC },       S_EOFCOM,
171
172         /* saw white space, eat it up */
173         WS1,    { C_XX },       S_WS,
174         WS1,    { ' ', '\t', '\v', '\r'},       WS1,
175
176         /* saw -, check --, -=, -> */
177         MINUS1, { C_XX },       ACT(MINUS, S_SELFB),
178         MINUS1, { '-' },        ACT(MMINUS, S_SELF),
179         MINUS1, { '=' },        ACT(ASMINUS,S_SELF),
180         MINUS1, { '>' },        ACT(ARROW,S_SELF),
181
182         /* saw +, check ++, += */
183         PLUS1,  { C_XX },       ACT(PLUS, S_SELFB),
184         PLUS1,  { '+' },        ACT(PPLUS, S_SELF),
185         PLUS1,  { '=' },        ACT(ASPLUS, S_SELF),
186
187         /* saw <, check <<, <<=, <= */
188         LT1,    { C_XX },       ACT(LT, S_SELFB),
189         LT1,    { '<' },        LT2,
190         LT1,    { '=' },        ACT(LEQ, S_SELF),
191         LT2,    { C_XX },       ACT(LSH, S_SELFB),
192         LT2,    { '=' },        ACT(ASLSH, S_SELF),
193
194         /* saw >, check >>, >>=, >= */
195         GT1,    { C_XX },       ACT(GT, S_SELFB),
196         GT1,    { '>' },        GT2,
197         GT1,    { '=' },        ACT(GEQ, S_SELF),
198         GT2,    { C_XX },       ACT(RSH, S_SELFB),
199         GT2,    { '=' },        ACT(ASRSH, S_SELF),
200
201         /* = */
202         ASG1,   { C_XX },       ACT(ASGN, S_SELFB),
203         ASG1,   { '=' },        ACT(EQ, S_SELF),
204
205         /* ! */
206         NOT1,   { C_XX },       ACT(NOT, S_SELFB),
207         NOT1,   { '=' },        ACT(NEQ, S_SELF),
208
209         /* & */
210         AND1,   { C_XX },       ACT(AND, S_SELFB),
211         AND1,   { '&' },        ACT(LAND, S_SELF),
212         AND1,   { '=' },        ACT(ASAND, S_SELF),
213
214         /* | */
215         OR1,    { C_XX },       ACT(OR, S_SELFB),
216         OR1,    { '|' },        ACT(LOR, S_SELF),
217         OR1,    { '=' },        ACT(ASOR, S_SELF),
218
219         /* # */
220         SHARP1, { C_XX },       ACT(SHARP, S_SELFB),
221         SHARP1, { '#' },        ACT(DSHARP, S_SELF),
222
223         /* % */
224         PCT1,   { C_XX },       ACT(PCT, S_SELFB),
225         PCT1,   { '=' },        ACT(ASPCT, S_SELF),
226
227         /* * */
228         STAR1,  { C_XX },       ACT(STAR, S_SELFB),
229         STAR1,  { '=' },        ACT(ASSTAR, S_SELF),
230
231         /* ^ */
232         CIRC1,  { C_XX },       ACT(CIRC, S_SELFB),
233         CIRC1,  { '=' },        ACT(ASCIRC, S_SELF),
234
235         -1
236 };
237
238 /* first index is char, second is state */
239 /* increase #states to power of 2 to encourage use of shift */
240 short   bigfsm[256][MAXSTATE];
241
242 void
243 expandlex(void)
244 {
245         /*const*/ struct fsm *fp;
246         int i, j, nstate;
247
248         for (fp = fsm; fp->state>=0; fp++) {
249                 for (i=0; fp->ch[i]; i++) {
250                         nstate = fp->nextstate;
251                         if (nstate >= S_SELF)
252                                 nstate = ~nstate;
253                         switch (fp->ch[i]) {
254
255                         case C_XX:              /* random characters */
256                                 for (j=0; j<256; j++)
257                                         bigfsm[j][fp->state] = nstate;
258                                 continue;
259                         case C_ALPH:
260                                 for (j=0; j<=256; j++)
261                                         if ('a'<=j&&j<='z' || 'A'<=j&&j<='Z'
262                                           || UTF2(j) || UTF3(j) || j=='_')
263                                                 bigfsm[j][fp->state] = nstate;
264                                 continue;
265                         case C_NUM:
266                                 for (j='0'; j<='9'; j++)
267                                         bigfsm[j][fp->state] = nstate;
268                                 continue;
269                         default:
270                                 bigfsm[fp->ch[i]][fp->state] = nstate;
271                         }
272                 }
273         }
274         /* install special cases for ? (trigraphs),  \ (splicing), runes, and EOB */
275         for (i=0; i<MAXSTATE; i++) {
276                 for (j=0; j<0xFF; j++)
277                         if (j=='?' || j=='\\' || UTF2(j) || UTF3(j)) {
278                                 if (bigfsm[j][i]>0)
279                                         bigfsm[j][i] = ~bigfsm[j][i];
280                                 bigfsm[j][i] &= ~QBSBIT;
281                         }
282                 bigfsm[EOB][i] = ~S_EOB;
283                 if (bigfsm[EOFC][i]>=0)
284                         bigfsm[EOFC][i] = ~S_EOF;
285         }
286 }
287
288 void
289 fixlex(void)
290 {
291         /* do C++ comments? */
292         if (Cplusplus==0)
293                 bigfsm['/'][COM1] = bigfsm['x'][COM1];
294 }
295
296 /*
297  * fill in a row of tokens from input, terminated by NL or END
298  * First token is put at trp->lp.
299  * Reset is non-zero when the input buffer can be "rewound."
300  * The value is a flag indicating that possible macros have
301  * been seen in the row.
302  */
303 int
304 gettokens(Tokenrow *trp, int reset)
305 {
306         register int c, state, oldstate;
307         register uchar *ip;
308         register Token *tp, *maxp;
309         int runelen;
310         Source *s = cursource;
311         int nmac = 0;
312         extern char outbuf[];
313
314         tp = trp->lp;
315         ip = s->inp;
316         if (reset) {
317                 s->lineinc = 0;
318                 if (ip>=s->inl) {               /* nothing in buffer */
319                         s->inl = s->inb;
320                         fillbuf(s);
321                         ip = s->inp = s->inb;
322                 } else if (ip >= s->inb+(3*s->ins/4)) {
323                         memmove(s->inb, ip, 4+s->inl-ip);
324                         s->inl = s->inb+(s->inl-ip);
325                         ip = s->inp = s->inb;
326                 }
327         }
328         maxp = &trp->bp[trp->max];
329         runelen = 1;
330         for (;;) {
331            continue2:
332                 if (tp>=maxp) {
333                         trp->lp = tp;
334                         tp = growtokenrow(trp);
335                         maxp = &trp->bp[trp->max];
336                 }
337                 tp->type = UNCLASS;
338                 tp->hideset = 0;
339                 tp->t = ip;
340                 tp->wslen = 0;
341                 tp->flag = 0;
342                 state = START;
343                 for (;;) {
344                         oldstate = state;
345                         c = *ip;
346                         if ((state = bigfsm[c][state]) >= 0) {
347                                 ip += runelen;
348                                 runelen = 1;
349                                 continue;
350                         }
351                         state = ~state;
352                 reswitch:
353                         switch (state&0177) {
354                         case S_SELF:
355                                 ip += runelen;
356                                 runelen = 1;
357                         case S_SELFB:
358                                 tp->type = GETACT(state);
359                                 tp->len = ip - tp->t;
360                                 tp++;
361                                 goto continue2;
362
363                         case S_NAME:    /* like S_SELFB but with nmac check */
364                                 tp->type = NAME;
365                                 tp->len = ip - tp->t;
366                                 nmac |= quicklook(tp->t[0], tp->len>1?tp->t[1]:0);
367                                 tp++;
368                                 goto continue2;
369
370                         case S_WS:
371                                 tp->wslen = ip - tp->t;
372                                 tp->t = ip;
373                                 state = START;
374                                 continue;
375
376                         default:
377                                 if ((state&QBSBIT)==0) {
378                                         ip += runelen;
379                                         runelen = 1;
380                                         continue;
381                                 }
382                                 state &= ~QBSBIT;
383                                 s->inp = ip;
384                                 if (c=='?') {   /* check trigraph */
385                                         if (trigraph(s)) {
386                                                 state = oldstate;
387                                                 continue;
388                                         }
389                                         goto reswitch;
390                                 }
391                                 if (c=='\\') { /* line-folding */
392                                         if (foldline(s)) {
393                                                 s->lineinc++;
394                                                 state = oldstate;
395                                                 continue;
396                                         }
397                                         goto reswitch;
398                                 }
399                                 if (UTF2(c)) {
400                                         runelen = 2;
401                                         goto reswitch;
402                                 }
403                                 if (UTF3(c)) {
404                                         runelen = 3;
405                                         goto reswitch;
406                                 }
407                                 error(WARNING, "Lexical botch in cpp");
408                                 ip += runelen;
409                                 runelen = 1;
410                                 continue;
411
412                         case S_EOB:
413                                 s->inp = ip;
414                                 fillbuf(cursource);
415                                 state = oldstate;
416                                 continue;
417
418                         case S_EOF:
419                                 tp->type = END;
420                                 tp->len = 0;
421                                 s->inp = ip;
422                                 if (tp!=trp->bp && (tp-1)->type!=NL && cursource->fd!=-1)
423                                         error(WARNING,"No newline at end of file");
424                                 trp->lp = tp+1;
425                                 return nmac;
426
427                         case S_STNL:
428                                 error(ERROR, "Unterminated string or char const");
429                         case S_NL:
430                                 tp->t = ip;
431                                 tp->type = NL;
432                                 tp->len = 1;
433                                 tp->wslen = 0;
434                                 s->lineinc++;
435                                 s->inp = ip+1;
436                                 trp->lp = tp+1;
437                                 return nmac;
438
439                         case S_EOFSTR:
440                                 error(FATAL, "EOF in string or char constant");
441                                 break;
442
443                         case S_COMNL:
444                                 s->lineinc++;
445                                 state = COM2;
446                                 ip += runelen;
447                                 runelen = 1;
448                                 if (ip >= s->inb+(7*s->ins/8)) { /* very long comment */
449                                         memmove(tp->t, ip, 4+s->inl-ip);
450                                         s->inl -= ip-tp->t;
451                                         ip = tp->t+1;
452                                 }
453                                 continue;
454
455                         case S_EOFCOM:
456                                 error(WARNING, "EOF inside comment");
457                                 --ip;
458                         case S_COMMENT:
459                                 ++ip;
460                                 tp->t = ip;
461                                 tp->t[-1] = ' ';
462                                 tp->wslen = 1;
463                                 state = START;
464                                 continue;
465                         }
466                         break;
467                 }
468                 ip += runelen;
469                 runelen = 1;
470                 tp->len = ip - tp->t;
471                 tp++;
472         }
473 }
474
475 /* have seen ?; handle the trigraph it starts (if any) else 0 */
476 int
477 trigraph(Source *s)
478 {
479         int c;
480
481         while (s->inp+2 >= s->inl && fillbuf(s)!=EOF)
482                 ;
483         if (s->inp[1]!='?')
484                 return 0;
485         c = 0;
486         switch(s->inp[2]) {
487         case '=':
488                 c = '#'; break;
489         case '(':
490                 c = '['; break;
491         case '/':
492                 c = '\\'; break;
493         case ')':
494                 c = ']'; break;
495         case '\'':
496                 c = '^'; break;
497         case '<':
498                 c = '{'; break;
499         case '!':
500                 c = '|'; break;
501         case '>':
502                 c = '}'; break;
503         case '-':
504                 c = '~'; break;
505         }
506         if (c) {
507                 *s->inp = c;
508                 memmove(s->inp+1, s->inp+3, s->inl-s->inp+2);
509                 s->inl -= 2;
510         }
511         return c;
512 }
513
514 int
515 foldline(Source *s)
516 {
517         int ncr = 0;
518
519 recheck:
520         while (s->inp+1 >= s->inl && fillbuf(s)!=EOF)
521                 ;
522         if (s->inp[ncr+1] == '\r') {    /* nonstandardly, ignore CR before line-folding */
523                 ncr++;
524                 goto recheck;
525         }
526         if (s->inp[ncr+1] == '\n') {
527                 memmove(s->inp, s->inp+2+ncr, s->inl-s->inp+3-ncr);
528                 s->inl -= 2+ncr;
529                 return 1;
530         }
531         return 0;
532 }
533
534 int
535 fillbuf(Source *s)
536 {
537         int n;
538
539         while((char *)s->inl+s->ins/8 > (char *)s->inb+s->ins) {
540                 int l = s->inl - s->inb;
541                 int p = s->inp - s->inb;
542                 if(l < 0) 
543                         error(FATAL, "negative end of input!?");
544                 if(p < 0)
545                         error(FATAL, "negative input pointer!?");
546                 /* double the buffer size and try again */
547                 s->ins *= 2;
548                 s->inb = dorealloc(s->inb, s->ins);
549                 s->inl = s->inb + l;
550                 s->inp = s->inb + p;
551         }
552         if (s->fd<0 || (n=read(s->fd, (char *)s->inl, s->ins/8)) <= 0)
553                 n = 0;
554         if ((*s->inp&0xff) == EOB) /* sentinel character appears in input */
555                 *s->inp = EOFC;
556         s->inl += n;
557         s->inl[0] = s->inl[1]= s->inl[2]= s->inl[3] = EOB;
558         if (n==0) {
559                 s->inl[0] = s->inl[1]= s->inl[2]= s->inl[3] = EOFC;
560                 return EOF;
561         }
562         return 0;
563 }
564
565 /*
566  * Push down to new source of characters.
567  * If fd>0 and str==NULL, then from a file `name';
568  * if fd==-1 and str, then from the string.
569  */
570 Source *
571 setsource(char *name, int fd, char *str)
572 {
573         Source *s = new(Source);
574         int len;
575
576         s->line = 1;
577         s->lineinc = 0;
578         s->fd = fd;
579         s->filename = name;
580         s->next = cursource;
581         s->ifdepth = 0;
582         cursource = s;
583         /* slop at right for EOB */
584         if (str) {
585                 len = strlen(str);
586                 s->inb = domalloc(len+4);
587                 s->inp = s->inb;
588                 strncpy((char *)s->inp, str, len);
589         } else {
590                 Dir *d;
591                 int junk;
592                 ulong length = 0;
593                 d = dirfstat(fd);
594                 if (d != nil) {
595                         length = d->length;
596                         free(d);
597                 }
598                 junk = length;
599                 if (junk<INS)
600                         junk = INS;
601                 s->inb = domalloc((junk)+4);
602                 s->inp = s->inb;
603                 len = 0;
604         }
605
606         s->ins = INS;   
607         s->inl = s->inp+len;
608         s->inl[0] = s->inl[1] = EOB;
609         return s;
610 }
611
612 void
613 unsetsource(void)
614 {
615         Source *s = cursource;
616
617         if (s->fd>=0) {
618                 close(s->fd);
619                 dofree(s->inb);
620         }
621         cursource = s->next;
622         dofree(s);
623 }