mirror of
https://github.com/ioquake/ioq3.git
synced 2024-11-10 15:21:35 +00:00
580 lines
13 KiB
C
580 lines
13 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "cpp.h"
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
#define MAXSTATE 32
|
|
#define ACT(tok,act) ((tok<<7)+act)
|
|
#define QBSBIT 0100
|
|
#define GETACT(st) (st>>7)&0x1ff
|
|
|
|
/* character classes */
|
|
#define C_WS 1
|
|
#define C_ALPH 2
|
|
#define C_NUM 3
|
|
#define C_EOF 4
|
|
#define C_XX 5
|
|
|
|
enum state {
|
|
START=0, NUM1, NUM2, NUM3, ID1, ST1, ST2, ST3, COM1, COM2, COM3, COM4,
|
|
CC1, CC2, WS1, PLUS1, MINUS1, STAR1, SLASH1, PCT1, SHARP1,
|
|
CIRC1, GT1, GT2, LT1, LT2, OR1, AND1, ASG1, NOT1, DOTS1,
|
|
S_SELF=MAXSTATE, S_SELFB, S_EOF, S_NL, S_EOFSTR,
|
|
S_STNL, S_COMNL, S_EOFCOM, S_COMMENT, S_EOB, S_WS, S_NAME
|
|
};
|
|
|
|
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 */
|
|
};
|
|
|
|
/*const*/ struct fsm fsm[] = {
|
|
/* start state */
|
|
{START, { C_XX }, ACT(UNCLASS,S_SELF)},
|
|
{START, { ' ', '\t', '\v' }, WS1},
|
|
{START, { C_NUM }, NUM1},
|
|
{START, { '.' }, NUM3},
|
|
{START, { C_ALPH }, ID1},
|
|
{START, { 'L' }, ST1},
|
|
{START, { '"' }, ST2},
|
|
{START, { '\'' }, CC1},
|
|
{START, { '/' }, COM1},
|
|
{START, { EOFC }, S_EOF},
|
|
{START, { '\n' }, S_NL},
|
|
{START, { '-' }, MINUS1},
|
|
{START, { '+' }, PLUS1},
|
|
{START, { '<' }, LT1},
|
|
{START, { '>' }, GT1},
|
|
{START, { '=' }, ASG1},
|
|
{START, { '!' }, NOT1},
|
|
{START, { '&' }, AND1},
|
|
{START, { '|' }, OR1},
|
|
{START, { '#' }, SHARP1},
|
|
{START, { '%' }, PCT1},
|
|
{START, { '[' }, ACT(SBRA,S_SELF)},
|
|
{START, { ']' }, ACT(SKET,S_SELF)},
|
|
{START, { '(' }, ACT(LP,S_SELF)},
|
|
{START, { ')' }, ACT(RP,S_SELF)},
|
|
{START, { '*' }, STAR1},
|
|
{START, { ',' }, ACT(COMMA,S_SELF)},
|
|
{START, { '?' }, ACT(QUEST,S_SELF)},
|
|
{START, { ':' }, ACT(COLON,S_SELF)},
|
|
{START, { ';' }, ACT(SEMIC,S_SELF)},
|
|
{START, { '{' }, ACT(CBRA,S_SELF)},
|
|
{START, { '}' }, ACT(CKET,S_SELF)},
|
|
{START, { '~' }, ACT(TILDE,S_SELF)},
|
|
{START, { '^' }, CIRC1},
|
|
|
|
/* saw a digit */
|
|
{NUM1, { C_XX }, ACT(NUMBER,S_SELFB)},
|
|
{NUM1, { C_NUM, C_ALPH, '.' }, NUM1},
|
|
{NUM1, { 'E', 'e' }, NUM2},
|
|
{NUM1, { '_' }, ACT(NUMBER,S_SELFB)},
|
|
|
|
/* saw possible start of exponent, digits-e */
|
|
{NUM2, { C_XX }, ACT(NUMBER,S_SELFB)},
|
|
{NUM2, { '+', '-' }, NUM1},
|
|
{NUM2, { C_NUM, C_ALPH }, NUM1},
|
|
{NUM2, { '_' }, ACT(NUMBER,S_SELFB)},
|
|
|
|
/* saw a '.', which could be a number or an operator */
|
|
{NUM3, { C_XX }, ACT(DOT,S_SELFB)},
|
|
{NUM3, { '.' }, DOTS1},
|
|
{NUM3, { C_NUM }, NUM1},
|
|
|
|
{DOTS1, { C_XX }, ACT(UNCLASS, S_SELFB)},
|
|
{DOTS1, { C_NUM }, NUM1},
|
|
{DOTS1, { '.' }, ACT(ELLIPS, S_SELF)},
|
|
|
|
/* saw a letter or _ */
|
|
{ID1, { C_XX }, ACT(NAME,S_NAME)},
|
|
{ID1, { C_ALPH, C_NUM }, ID1},
|
|
|
|
/* saw L (start of wide string?) */
|
|
{ST1, { C_XX }, ACT(NAME,S_NAME)},
|
|
{ST1, { C_ALPH, C_NUM }, ID1},
|
|
{ST1, { '"' }, ST2},
|
|
{ST1, { '\'' }, CC1},
|
|
|
|
/* saw " beginning string */
|
|
{ST2, { C_XX }, ST2},
|
|
{ST2, { '"' }, ACT(STRING, S_SELF)},
|
|
{ST2, { '\\' }, ST3},
|
|
{ST2, { '\n' }, S_STNL},
|
|
{ST2, { EOFC }, S_EOFSTR},
|
|
|
|
/* saw \ in string */
|
|
{ST3, { C_XX }, ST2},
|
|
{ST3, { '\n' }, S_STNL},
|
|
{ST3, { EOFC }, S_EOFSTR},
|
|
|
|
/* saw ' beginning character const */
|
|
{CC1, { C_XX }, CC1},
|
|
{CC1, { '\'' }, ACT(CCON, S_SELF)},
|
|
{CC1, { '\\' }, CC2},
|
|
{CC1, { '\n' }, S_STNL},
|
|
{CC1, { EOFC }, S_EOFSTR},
|
|
|
|
/* saw \ in ccon */
|
|
{CC2, { C_XX }, CC1},
|
|
{CC2, { '\n' }, S_STNL},
|
|
{CC2, { EOFC }, S_EOFSTR},
|
|
|
|
/* saw /, perhaps start of comment */
|
|
{COM1, { C_XX }, ACT(SLASH, S_SELFB)},
|
|
{COM1, { '=' }, ACT(ASSLASH, S_SELF)},
|
|
{COM1, { '*' }, COM2},
|
|
{COM1, { '/' }, COM4},
|
|
|
|
/* saw / then *, start of comment */
|
|
{COM2, { C_XX }, COM2},
|
|
{COM2, { '\n' }, S_COMNL},
|
|
{COM2, { '*' }, COM3},
|
|
{COM2, { EOFC }, S_EOFCOM},
|
|
|
|
/* saw the * possibly ending a comment */
|
|
{COM3, { C_XX }, COM2},
|
|
{COM3, { '\n' }, S_COMNL},
|
|
{COM3, { '*' }, COM3},
|
|
{COM3, { '/' }, S_COMMENT},
|
|
|
|
/* // comment */
|
|
{COM4, { C_XX }, COM4},
|
|
{COM4, { '\n' }, S_NL},
|
|
{COM4, { EOFC }, S_EOFCOM},
|
|
|
|
/* saw white space, eat it up */
|
|
{WS1, { C_XX }, S_WS},
|
|
{WS1, { ' ', '\t', '\v' }, WS1},
|
|
|
|
/* saw -, check --, -=, -> */
|
|
{MINUS1, { C_XX }, ACT(MINUS, S_SELFB)},
|
|
{MINUS1, { '-' }, ACT(MMINUS, S_SELF)},
|
|
{MINUS1, { '=' }, ACT(ASMINUS,S_SELF)},
|
|
{MINUS1, { '>' }, ACT(ARROW,S_SELF)},
|
|
|
|
/* saw +, check ++, += */
|
|
{PLUS1, { C_XX }, ACT(PLUS, S_SELFB)},
|
|
{PLUS1, { '+' }, ACT(PPLUS, S_SELF)},
|
|
{PLUS1, { '=' }, ACT(ASPLUS, S_SELF)},
|
|
|
|
/* saw <, check <<, <<=, <= */
|
|
{LT1, { C_XX }, ACT(LT, S_SELFB)},
|
|
{LT1, { '<' }, LT2},
|
|
{LT1, { '=' }, ACT(LEQ, S_SELF)},
|
|
{LT2, { C_XX }, ACT(LSH, S_SELFB)},
|
|
{LT2, { '=' }, ACT(ASLSH, S_SELF)},
|
|
|
|
/* saw >, check >>, >>=, >= */
|
|
{GT1, { C_XX }, ACT(GT, S_SELFB)},
|
|
{GT1, { '>' }, GT2},
|
|
{GT1, { '=' }, ACT(GEQ, S_SELF)},
|
|
{GT2, { C_XX }, ACT(RSH, S_SELFB)},
|
|
{GT2, { '=' }, ACT(ASRSH, S_SELF)},
|
|
|
|
/* = */
|
|
{ASG1, { C_XX }, ACT(ASGN, S_SELFB)},
|
|
{ASG1, { '=' }, ACT(EQ, S_SELF)},
|
|
|
|
/* ! */
|
|
{NOT1, { C_XX }, ACT(NOT, S_SELFB)},
|
|
{NOT1, { '=' }, ACT(NEQ, S_SELF)},
|
|
|
|
/* & */
|
|
{AND1, { C_XX }, ACT(AND, S_SELFB)},
|
|
{AND1, { '&' }, ACT(LAND, S_SELF)},
|
|
{AND1, { '=' }, ACT(ASAND, S_SELF)},
|
|
|
|
/* | */
|
|
{OR1, { C_XX }, ACT(OR, S_SELFB)},
|
|
{OR1, { '|' }, ACT(LOR, S_SELF)},
|
|
{OR1, { '=' }, ACT(ASOR, S_SELF)},
|
|
|
|
/* # */
|
|
{SHARP1, { C_XX }, ACT(SHARP, S_SELFB)},
|
|
{SHARP1, { '#' }, ACT(DSHARP, S_SELF)},
|
|
|
|
/* % */
|
|
{PCT1, { C_XX }, ACT(PCT, S_SELFB)},
|
|
{PCT1, { '=' }, ACT(ASPCT, S_SELF)},
|
|
|
|
/* * */
|
|
{STAR1, { C_XX }, ACT(STAR, S_SELFB)},
|
|
{STAR1, { '=' }, ACT(ASSTAR, S_SELF)},
|
|
|
|
/* ^ */
|
|
{CIRC1, { C_XX }, ACT(CIRC, S_SELFB)},
|
|
{CIRC1, { '=' }, ACT(ASCIRC, S_SELF)},
|
|
|
|
{-1}
|
|
};
|
|
|
|
/* 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')
|
|
|| 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, and EOB */
|
|
for (i=0; i<MAXSTATE; i++) {
|
|
for (j=0; j<0xFF; j++)
|
|
if (j=='?' || j=='\\') {
|
|
if (bigfsm[j][i]>0)
|
|
bigfsm[j][i] = ~bigfsm[j][i];
|
|
bigfsm[j][i] &= ~QBSBIT;
|
|
}
|
|
bigfsm[EOB][i] = ~S_EOB;
|
|
if (bigfsm[EOFC][i]>=0)
|
|
bigfsm[EOFC][i] = ~S_EOF;
|
|
}
|
|
}
|
|
|
|
void
|
|
fixlex(void)
|
|
{
|
|
/* do C++ comments? */
|
|
if (Cplusplus==0)
|
|
bigfsm['/'][COM1] = bigfsm['x'][COM1];
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
if (ip>=s->inl) { /* nothing in buffer */
|
|
s->inl = s->inb;
|
|
fillbuf(s);
|
|
ip = s->inp = s->inb;
|
|
} else if (ip >= s->inb+(3*INS/4)) {
|
|
memmove(s->inb, ip, 4+s->inl-ip);
|
|
s->inl = s->inb+(s->inl-ip);
|
|
ip = s->inp = s->inb;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
error(WARNING, "Lexical botch in cpp");
|
|
ip += runelen;
|
|
runelen = 1;
|
|
continue;
|
|
|
|
case S_EOB:
|
|
s->inp = ip;
|
|
fillbuf(cursource);
|
|
state = oldstate;
|
|
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_STNL:
|
|
error(ERROR, "Unterminated string or char const");
|
|
case S_NL:
|
|
tp->t = ip;
|
|
tp->type = NL;
|
|
tp->len = 1;
|
|
tp->wslen = 0;
|
|
s->lineinc++;
|
|
s->inp = ip+1;
|
|
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;
|
|
if (ip >= s->inb+(7*INS/8)) { /* very long comment */
|
|
memmove(tp->t, ip, 4+s->inl-ip);
|
|
s->inl -= ip-tp->t;
|
|
ip = tp->t+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;
|
|
|
|
while (s->inp+2 >= s->inl && fillbuf(s)!=EOF)
|
|
;
|
|
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;
|
|
}
|
|
|
|
int
|
|
foldline(Source *s)
|
|
{
|
|
while (s->inp+1 >= s->inl && fillbuf(s)!=EOF)
|
|
;
|
|
if (s->inp[1] == '\n') {
|
|
memmove(s->inp, s->inp+2, s->inl-s->inp+3);
|
|
s->inl -= 2;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fillbuf(Source *s)
|
|
{
|
|
int n, nr;
|
|
|
|
nr = INS/8;
|
|
if ((char *)s->inl+nr > (char *)s->inb+INS)
|
|
error(FATAL, "Input buffer overflow");
|
|
if (s->fd<0 || (n=read(s->fd, (char *)s->inl, INS/8)) <= 0)
|
|
n = 0;
|
|
if ((*s->inp&0xff) == EOB) /* sentinel character appears in input */
|
|
*s->inp = EOFC;
|
|
s->inl += n;
|
|
s->inl[0] = s->inl[1]= s->inl[2]= s->inl[3] = EOB;
|
|
if (n==0) {
|
|
s->inl[0] = s->inl[1]= s->inl[2]= s->inl[3] = EOFC;
|
|
return EOF;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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 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 EOB */
|
|
if (str) {
|
|
len = strlen(str);
|
|
s->inb = domalloc(len+4);
|
|
s->inp = s->inb;
|
|
strncpy((char *)s->inp, str, len);
|
|
} else {
|
|
s->inb = domalloc(INS+4);
|
|
s->inp = s->inb;
|
|
len = 0;
|
|
}
|
|
s->inl = s->inp+len;
|
|
s->inl[0] = s->inl[1] = EOB;
|
|
return s;
|
|
}
|
|
|
|
void
|
|
unsetsource(void)
|
|
{
|
|
Source *s = cursource;
|
|
|
|
if (s->fd>=0) {
|
|
close(s->fd);
|
|
dofree(s->inb);
|
|
}
|
|
cursource = s->next;
|
|
dofree(s);
|
|
}
|