/*
* lexical FSM encoding
* when in state state, and one of the characters
* in ch arrives, enter nextstate.
* States >= S_SELF are either final, or at least require special action.
* In 'fsm' there is a line for each state X charset X nextstate.
* List chars that overwrite previous entries later (e.g. C_ALPH
* can be overridden by '_' by a later entry; and C_XX is the
* the universal set, and should always be first.
* States above S_SELF are represented in the big table as negative values.
* S_SELF and S_SELFB encode the resulting token type in the upper bits.
* These actions differ in that S_SELF doesn't have a lookahead char,
* S_SELFB does.
*
* The encoding is blown out into a big table for time-efficiency.
* Entries have
* nextstate: 6 bits; ?\ marker: 1 bit; tokentype: 9 bits.
*/
int tottok;
int tokkind[256];
struct fsm {
int state; /* if in this state */
uchar ch[4]; /* and see one of these characters */
int nextstate; /* enter this state if +ve */
};
/* first index is char, second is state */
/* increase #states to power of 2 to encourage use of shift */
short bigfsm[256][MAXSTATE];
void
expandlex(void)
{
/*const*/ struct fsm *fp;
int i, j, nstate;
for (fp = fsm; fp->state>=0; fp++) {
for (i=0; fp->ch[i]; i++) {
nstate = fp->nextstate;
if (nstate >= S_SELF)
nstate = ~nstate;
switch (fp->ch[i]) {
case C_XX: /* random characters */
for (j=0; j<256; j++)
bigfsm[j][fp->state] = nstate;
continue;
case C_ALPH:
for (j=0; j<=256; j++)
if ('a'<=j&&j<='z' || 'A'<=j&&j<='Z'
|| UTF2(j) || UTF3(j) || UTF4(j) || j=='_')
bigfsm[j][fp->state] = nstate;
continue;
case C_NUM:
for (j='0'; j<='9'; j++)
bigfsm[j][fp->state] = nstate;
continue;
default:
bigfsm[fp->ch[i]][fp->state] = nstate;
}
}
}
/* install special cases for ? (trigraphs), \ (splicing), runes */
for (i=0; i<MAXSTATE; i++) {
for (j=0; j<0xFF; j++)
if (j=='?' || j=='\\' || UTF2(j) || UTF3(j) || UTF4(j)) {
if (bigfsm[j][i]>0)
bigfsm[j][i] = ~bigfsm[j][i];
bigfsm[j][i] &= ~QBSBIT;
}
if (bigfsm[EOFC][i]>=0)
bigfsm[EOFC][i] = ~S_EOF;
}
}
/*
* fill in a row of tokens from input, terminated by NL or END
* First token is put at trp->lp.
* Reset is non-zero when the input buffer can be "rewound."
* The value is a flag indicating that possible macros have
* been seen in the row.
*/
int
gettokens(Tokenrow *trp, int reset)
{
register int c, state, oldstate;
register uchar *ip;
register Token *tp, *maxp;
int runelen;
Source *s = cursource;
int nmac = 0;
tp = trp->lp;
ip = s->inp;
if (reset)
s->lineinc = 0;
maxp = &trp->bp[trp->max];
runelen = 1;
for (;;) {
continue2:
if (tp>=maxp) {
trp->lp = tp;
tp = growtokenrow(trp);
maxp = &trp->bp[trp->max];
}
tp->type = UNCLASS;
tp->hideset = 0;
tp->t = ip;
tp->wslen = 0;
tp->flag = 0;
state = START;
for (;;) {
oldstate = state;
c = *ip;
if ((state = bigfsm[c][state]) >= 0) {
ip += runelen;
runelen = 1;
continue;
}
state = ~state;
reswitch:
switch (state&0177) {
case S_SELF:
ip += runelen;
runelen = 1;
case S_SELFB:
tp->type = GETACT(state);
tp->len = ip - tp->t;
tp++;
goto continue2;
case S_NAME: /* like S_SELFB but with nmac check */
tp->type = NAME;
tp->len = ip - tp->t;
nmac |= quicklook(tp->t[0], tp->len>1?tp->t[1]:0);
tp++;
goto continue2;
case S_WS:
tp->wslen = ip - tp->t;
tp->t = ip;
state = START;
continue;
default:
if ((state&QBSBIT)==0) {
ip += runelen;
runelen = 1;
continue;
}
state &= ~QBSBIT;
s->inp = ip;
if (c=='?') { /* check trigraph */
if (trigraph(s)) {
state = oldstate;
continue;
}
goto reswitch;
}
if (c=='\\') { /* line-folding */
if (foldline(s)) {
s->lineinc++;
state = oldstate;
continue;
}
goto reswitch;
}
if (UTF2(c)) {
runelen = 2;
goto reswitch;
}
if (UTF3(c)) {
runelen = 3;
goto reswitch;
}
if (UTF4(c)) {
runelen = 4;
goto reswitch;
}
error(WARNING, "Lexical botch in cpp");
ip += runelen;
runelen = 1;
continue;
case S_EOF:
tp->type = END;
tp->len = 0;
s->inp = ip;
if (tp!=trp->bp && (tp-1)->type!=NL && cursource->fd!=-1)
error(WARNING,"No newline at end of file");
trp->lp = tp+1;
return nmac;
case S_EOFSTR:
error(FATAL, "EOF in string or char constant");
break;
case S_COMNL:
s->lineinc++;
state = COM2;
ip += runelen;
runelen = 1;
continue;
case S_EOFCOM:
error(WARNING, "EOF inside comment");
--ip;
case S_COMMENT:
++ip;
tp->t = ip;
tp->t[-1] = ' ';
tp->wslen = 1;
state = START;
continue;
}
break;
}
ip += runelen;
runelen = 1;
tp->len = ip - tp->t;
tp++;
}
}
/* have seen ?; handle the trigraph it starts (if any) else 0 */
int
trigraph(Source *s)
{
int c;
if (s->inp[1]!='?')
return 0;
c = 0;
switch(s->inp[2]) {
case '=':
c = '#'; break;
case '(':
c = '['; break;
case '/':
c = '\\'; break;
case ')':
c = ']'; break;
case '\'':
c = '^'; break;
case '<':
c = '{'; break;
case '!':
c = '|'; break;
case '>':
c = '}'; break;
case '-':
c = '~'; break;
}
if (c) {
*s->inp = c;
memmove(s->inp+1, s->inp+3, s->inl-s->inp+2);
s->inl -= 2;
}
return c;
}
/*
* Push down to new source of characters.
* If fd>0 and str==NULL, then from a file `name';
* if fd==-1 and str, then from the string.
*/
Source *
setsource(char *name, int fd, char *str)
{
Source *s = new(Source);
int n, len;
s->line = 1;
s->lineinc = 0;
s->fd = fd;
s->filename = name;
s->next = cursource;
s->ifdepth = 0;
cursource = s;
/* slop at right for EOFC */
if (str) {
len = strlen(str);
s->inb = domalloc(len+4);
strncpy((char *)s->inb, str, len);
} else {
len = 0;
s->inb = nil;
for(;;){
s->inb = dorealloc(s->inb, len + INS);
if (s->fd<0 || (n=read(s->fd, (char *)s->inb + len, INS)) <= 0)
break;
len += n;
}
s->inb = dorealloc(s->inb, len + 4);
}
s->inp = s->inb;
s->inl = s->inp+len;
s->inl[0] = s->inl[1] = s->inl[2] = s->inl[3] = EOFC;
return s;
}