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