You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
538 lines
18 KiB
538 lines
18 KiB
/***********************************************************************
|
|
* Microsoft (R) Windows (R) Resource Compiler
|
|
*
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
*
|
|
* File Comments:
|
|
*
|
|
*
|
|
***********************************************************************/
|
|
|
|
#include "rc.h"
|
|
|
|
/************************************************************************
|
|
** MAP_TOKEN : a token has two representations and additional information.
|
|
** (ex : const, has basic token of L_CONST,
|
|
** mapped token of [L_TYPE | L_MODIFIER]
|
|
** and info based on what the map token is)
|
|
** MAP_AND_FILL : has two representations, but none of the extra info.
|
|
** (ex : '<', has basic of L_LT, and map of L_RELOP)
|
|
** NOMAP_TOKEN : has 1 representation and additional info.
|
|
** (ex: a string, basic and 'map' type L_STRING and ptrs to the actual str)
|
|
** NOMAP_AND_FILL : has 1 representation and no additional info.
|
|
** (ex : 'while', has basic and 'map' of L_WHILE)
|
|
** the FILL versions fill the token with the basic token type.
|
|
************************************************************************/
|
|
#define MAP_TOKEN(otok)\
|
|
(Basic_token = (otok), TS_VALUE(Basic_token))
|
|
#define MAP_AND_FILL(otok)\
|
|
(yylval.yy_token = Basic_token = (otok), TS_VALUE(Basic_token))
|
|
#define NOMAP_TOKEN(otok)\
|
|
(Basic_token = (otok))
|
|
#define NOMAP_AND_FILL(otok)\
|
|
(yylval.yy_token = Basic_token = (otok))
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* yylex - main tokenization routine */
|
|
/************************************************************************/
|
|
|
|
token_t
|
|
yylex(
|
|
void
|
|
)
|
|
{
|
|
REG WCHAR last_mapped;
|
|
WCHAR mapped_c;
|
|
WCHAR buf[5];
|
|
REG token_t lex_token;
|
|
|
|
for(;;) {
|
|
last_mapped = mapped_c = CHARMAP(GETCH());
|
|
first_switch:
|
|
switch(mapped_c) {
|
|
case LX_EACH:
|
|
case LX_ASCII:
|
|
if (fAFXSymbols && PREVCH() == SYMUSESTART || PREVCH() == SYMDEFSTART
|
|
|| PREVCH() == SYMDELIMIT) {
|
|
myfwrite(&(PREVCH()), sizeof(WCHAR), 1, OUTPUTFILE);
|
|
continue;
|
|
}
|
|
error(2018, PREVCH());
|
|
continue;
|
|
|
|
case LX_OBRACE:
|
|
return(NOMAP_AND_FILL(L_LCURLY));
|
|
|
|
case LX_CBRACE:
|
|
return(NOMAP_AND_FILL(L_RCURLY));
|
|
|
|
case LX_OBRACK:
|
|
return(NOMAP_AND_FILL(L_LBRACK));
|
|
|
|
case LX_CBRACK:
|
|
return(NOMAP_AND_FILL(L_RBRACK));
|
|
|
|
case LX_OPAREN:
|
|
return(NOMAP_AND_FILL(L_LPAREN));
|
|
|
|
case LX_CPAREN:
|
|
return(NOMAP_AND_FILL(L_RPAREN));
|
|
|
|
case LX_COMMA:
|
|
return(NOMAP_AND_FILL(L_COMMA));
|
|
|
|
case LX_QUEST:
|
|
return(NOMAP_AND_FILL(L_QUEST));
|
|
|
|
case LX_SEMI:
|
|
return(NOMAP_AND_FILL(L_SEMI));
|
|
|
|
case LX_TILDE:
|
|
return(NOMAP_AND_FILL(L_TILDE));
|
|
|
|
case LX_NUMBER:
|
|
return(MAP_TOKEN(getnum(PREVCH())));
|
|
|
|
case LX_MINUS:
|
|
switch(last_mapped = CHARMAP(GETCH())) {
|
|
case LX_EQ:
|
|
return(MAP_AND_FILL(L_MINUSEQ));
|
|
|
|
case LX_GT:
|
|
return(MAP_AND_FILL(L_POINTSTO));
|
|
|
|
case LX_MINUS:
|
|
return(MAP_AND_FILL(L_DECR));
|
|
|
|
default:
|
|
lex_token = L_MINUS;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LX_PLUS:
|
|
switch(last_mapped = CHARMAP(GETCH())) {
|
|
case LX_EQ:
|
|
return(MAP_AND_FILL(L_PLUSEQ));
|
|
|
|
case LX_PLUS:
|
|
return(MAP_AND_FILL(L_INCR));
|
|
|
|
default:
|
|
lex_token = L_PLUS;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LX_AND:
|
|
switch(last_mapped = CHARMAP(GETCH())) {
|
|
case LX_EQ:
|
|
return(MAP_AND_FILL(L_ANDEQ));
|
|
|
|
case LX_AND:
|
|
return(MAP_AND_FILL(L_ANDAND));
|
|
|
|
default:
|
|
lex_token = L_AND;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LX_OR:
|
|
switch(last_mapped = CHARMAP(GETCH())) {
|
|
case LX_EQ:
|
|
return(MAP_AND_FILL(L_OREQ));
|
|
|
|
case LX_OR:
|
|
return(MAP_AND_FILL(L_OROR));
|
|
|
|
default:
|
|
lex_token = L_OR;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LX_COLON:
|
|
return(NOMAP_AND_FILL(L_COLON));
|
|
|
|
case LX_HAT:
|
|
if((last_mapped = CHARMAP(GETCH())) == LX_EQ) {
|
|
return(MAP_AND_FILL(L_XOREQ));
|
|
}
|
|
lex_token = L_XOR;
|
|
break;
|
|
|
|
case LX_PERCENT:
|
|
if((last_mapped = CHARMAP(GETCH())) == LX_EQ) {
|
|
return(MAP_AND_FILL(L_MODEQ));
|
|
}
|
|
lex_token = L_MOD;
|
|
break;
|
|
|
|
case LX_EQ:
|
|
if((last_mapped = CHARMAP(GETCH())) == LX_EQ) {
|
|
return(MAP_AND_FILL(L_EQUALS));
|
|
}
|
|
lex_token = L_ASSIGN;
|
|
break;
|
|
|
|
case LX_BANG:
|
|
if((last_mapped = CHARMAP(GETCH())) == LX_EQ) {
|
|
return(MAP_AND_FILL(L_NOTEQ));
|
|
}
|
|
lex_token = L_EXCLAIM;
|
|
break;
|
|
|
|
case LX_SLASH:
|
|
switch(last_mapped = CHARMAP(GETCH())) {
|
|
case LX_STAR:
|
|
dump_comment();
|
|
continue;
|
|
|
|
case LX_SLASH:
|
|
DumpSlashComment();
|
|
continue;
|
|
|
|
case LX_EQ:
|
|
return(MAP_AND_FILL(L_DIVEQ));
|
|
|
|
default:
|
|
lex_token = L_DIV;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LX_STAR:
|
|
switch(last_mapped = CHARMAP(GETCH())) {
|
|
case LX_SLASH:
|
|
if( ! Prep ) {
|
|
error(2138); /* (nested comments) */
|
|
} else {
|
|
myfwrite(L"*/", 2 * sizeof(WCHAR), 1, OUTPUTFILE);
|
|
}
|
|
continue;
|
|
|
|
case LX_EQ:
|
|
return(MAP_AND_FILL(L_MULTEQ));
|
|
|
|
default:
|
|
lex_token = L_MULT;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LX_LT:
|
|
switch(last_mapped = CHARMAP(GETCH())) {
|
|
case LX_LT:
|
|
if((last_mapped = CHARMAP(GETCH())) == LX_EQ) {
|
|
return(MAP_AND_FILL(L_LSHFTEQ));
|
|
}
|
|
mapped_c = LX_LSHIFT;
|
|
lex_token = L_LSHIFT;
|
|
break;
|
|
|
|
case LX_EQ:
|
|
return(MAP_AND_FILL(L_LTEQ));
|
|
|
|
default:
|
|
lex_token = L_LT;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LX_LSHIFT:
|
|
/*
|
|
** if the next char is not an =, then we unget and return,
|
|
** since the only way in here is if we broke on the char
|
|
** following '<<'. since we'll have already worked the handle_eos()
|
|
** code prior to getting here, we'll not see another eos,
|
|
** UNLESS i/o buffering is char by char. ???
|
|
** see also, LX_RSHIFT
|
|
*/
|
|
if((last_mapped = CHARMAP(GETCH())) == LX_EQ) {
|
|
return(MAP_AND_FILL(L_LSHFTEQ));
|
|
}
|
|
UNGETCH();
|
|
return(MAP_AND_FILL(L_LSHIFT));
|
|
|
|
case LX_GT:
|
|
switch(last_mapped = CHARMAP(GETCH())) {
|
|
case LX_EQ:
|
|
return(MAP_AND_FILL(L_GTEQ));
|
|
|
|
case LX_GT:
|
|
if((last_mapped = CHARMAP(GETCH())) == LX_EQ) {
|
|
return(MAP_AND_FILL(L_RSHFTEQ));
|
|
}
|
|
mapped_c = LX_RSHIFT;
|
|
lex_token = L_RSHIFT;
|
|
break;
|
|
|
|
default:
|
|
lex_token = L_GT;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LX_RSHIFT:
|
|
if((last_mapped = CHARMAP(GETCH())) == LX_EQ) {
|
|
return(MAP_AND_FILL(L_RSHFTEQ));
|
|
}
|
|
UNGETCH();
|
|
return(MAP_AND_FILL(L_RSHIFT));
|
|
|
|
case LX_POUND:
|
|
if( ! Prep ) {
|
|
error(2014);/* # sign must be first non-whitespace */
|
|
UNGETCH(); /* replace it */
|
|
Linenumber--; /* do_newline counts a newline */
|
|
do_newline(); /* may be a 'real' prepro line */
|
|
} else {
|
|
myfwrite(L"#", sizeof(WCHAR), 1, OUTPUTFILE);
|
|
}
|
|
continue;
|
|
|
|
case LX_EOS:
|
|
if(PREVCH() == L'\\') {
|
|
if( ! Prep ) {
|
|
if( ! checknl()) { /* ignore the new line */
|
|
error(2017);/* illegal escape sequence */
|
|
}
|
|
} else {
|
|
myfwrite(L"\\", sizeof(WCHAR), 1, OUTPUTFILE);
|
|
*buf = get_non_eof();
|
|
myfwrite(buf, sizeof(WCHAR), 1, OUTPUTFILE);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if(Macro_depth == 0) {
|
|
if( ! io_eob()) { /* not the end of the buffer */
|
|
continue;
|
|
}
|
|
if(fpop()) { /* have more files to read */
|
|
continue;
|
|
}
|
|
return(MAP_AND_FILL(L_EOF)); /* all gone . . . */
|
|
}
|
|
handle_eos(); /* found end of macro */
|
|
continue;
|
|
|
|
case LX_DQUOTE:
|
|
if( ! Prep ) {
|
|
str_const();
|
|
return(NOMAP_TOKEN(L_STRING));
|
|
}
|
|
prep_string(L'\"');
|
|
continue;
|
|
|
|
case LX_SQUOTE:
|
|
if( ! Prep ) {
|
|
return(MAP_TOKEN(char_const()));
|
|
}
|
|
prep_string(L'\'');
|
|
continue;
|
|
|
|
case LX_CR: /* ??? check for nl next */
|
|
continue;
|
|
|
|
case LX_NL:
|
|
if(On_pound_line) {
|
|
UNGETCH();
|
|
return(NOMAP_TOKEN(L_NOTOKEN));
|
|
}
|
|
if(Prep) {
|
|
// must manually write '\r' with '\n' when writing 16-bit strings
|
|
myfwrite(L"\r\n", 2 * sizeof(WCHAR), 1, OUTPUTFILE);
|
|
}
|
|
do_newline();
|
|
continue;
|
|
|
|
case LX_WHITE: /* skip all white space */
|
|
if( ! Prep ) { /* check only once */
|
|
do {
|
|
;
|
|
} while(LXC_IS_WHITE(GETCH()));
|
|
}
|
|
else {
|
|
WCHAR c;
|
|
|
|
c = PREVCH();
|
|
do {
|
|
myfwrite(&c, sizeof(WCHAR), 1, OUTPUTFILE);
|
|
} while(LXC_IS_WHITE(c = GETCH()));
|
|
}
|
|
UNGETCH();
|
|
continue;
|
|
|
|
case LX_ILL:
|
|
if( ! Prep ) {
|
|
error(2018, PREVCH());/* unknown character */
|
|
} else {
|
|
myfwrite(&(PREVCH()), sizeof(WCHAR), 1, OUTPUTFILE);
|
|
}
|
|
continue;
|
|
|
|
case LX_BACKSLASH:
|
|
if( ! Prep ) {
|
|
if( ! checknl()) { /* ignore the new line */
|
|
error(2017);/* illegal escape sequence */
|
|
}
|
|
}
|
|
else {
|
|
myfwrite(L"\\", sizeof(WCHAR), 1, OUTPUTFILE);
|
|
*buf = get_non_eof();
|
|
myfwrite(buf, sizeof(WCHAR), 1, OUTPUTFILE);
|
|
}
|
|
continue;
|
|
|
|
case LX_DOT:
|
|
dot_switch:
|
|
switch(last_mapped = CHARMAP(GETCH())) {
|
|
case LX_BACKSLASH:
|
|
if(checknl()) {
|
|
goto dot_switch;
|
|
}
|
|
UNGETCH();
|
|
break;
|
|
|
|
case LX_EOS:
|
|
if(handle_eos() == BACKSLASH_EOS) {
|
|
break;
|
|
}
|
|
goto dot_switch;
|
|
|
|
case LX_DOT:
|
|
if( ! checkop(L'.') ) {
|
|
error(2142);/* ellipsis requires three '.'s */
|
|
}
|
|
return(NOMAP_AND_FILL(L_ELLIPSIS));
|
|
|
|
case LX_NUMBER:
|
|
/*
|
|
** don't worry about getting correct hash value.
|
|
** The text equivalent of a real number is never
|
|
** hashed
|
|
*/
|
|
Reuse_W[0] = L'.';
|
|
Reuse_W[1] = PREVCH();
|
|
return(MAP_TOKEN(get_real(&Reuse_W[2])));
|
|
}
|
|
UNGETCH();
|
|
return(MAP_AND_FILL(L_PERIOD));
|
|
|
|
case LX_NOEXPAND:
|
|
SKIPCH(); /* just skip length */
|
|
continue;
|
|
|
|
case LX_ID:
|
|
{
|
|
pdefn_t pdef;
|
|
|
|
if(Macro_depth > 0) {
|
|
if( ! lex_getid(PREVCH())) {
|
|
goto avoid_expand;
|
|
}
|
|
}
|
|
else {
|
|
getid(PREVCH());
|
|
}
|
|
|
|
if( ((pdef = get_defined()) != 0)
|
|
&&
|
|
( ! DEFN_EXPANDING(pdef))
|
|
&&
|
|
( can_expand(pdef))
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
avoid_expand:
|
|
if( ! Prep ) {
|
|
/* M00BUG get near copy of identifier???? */
|
|
HLN_NAME(yylval.yy_ident) = Reuse_W;
|
|
HLN_HASH(yylval.yy_ident) = Reuse_W_hash;
|
|
HLN_LENGTH(yylval.yy_ident) = (UINT)Reuse_W_length;
|
|
return(L_IDENT);
|
|
} else {
|
|
myfwrite(Reuse_W, (Reuse_W_length - 1) * sizeof(WCHAR), 1, OUTPUTFILE);
|
|
return(NOMAP_TOKEN(L_NOTOKEN));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
/*
|
|
** all the multichar ( -> -- -= etc ) operands
|
|
** must come through here. we've gotten the next char,
|
|
** and not matched one of the possiblities, but we have to check
|
|
** for the end of the buffer character and act accordingly
|
|
** if it is the eob, then we handle it and go back for another try.
|
|
** otherwise, we unget the char we got, and return the base token.
|
|
*/
|
|
if(last_mapped == LX_EOS) {
|
|
if(handle_eos() != BACKSLASH_EOS) {
|
|
goto first_switch;
|
|
}
|
|
}
|
|
UNGETCH(); /* cause we got an extra one to check */
|
|
return(MAP_AND_FILL(lex_token));
|
|
}
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
**
|
|
** lex_getid: reads an identifier for the main lexer. The
|
|
** identifier is read into Reuse_W. This function should not handle
|
|
** an end of string if it is rescanning a macro expansion, because
|
|
** this could switch the context with regards to whether the macro
|
|
** is expandable or not. Similarly, the noexpand marker must only be
|
|
** allowed if a macro is being rescanned, otherwise let this character
|
|
** be caught as an illegal character in text
|
|
************************************************************************/
|
|
int
|
|
lex_getid(
|
|
WCHAR c
|
|
)
|
|
{
|
|
REG WCHAR *p;
|
|
int length = 0;
|
|
|
|
p = Reuse_W;
|
|
*p++ = c;
|
|
c &= HASH_MASK;
|
|
for(;;) {
|
|
while(LXC_IS_IDENT(*p = GETCH())) { /* collect character */
|
|
c += (*p & HASH_MASK); /* hash it */
|
|
p++;
|
|
}
|
|
|
|
if(CHARMAP(*p) == LX_NOEXPAND ) {
|
|
length = (int)GETCH();
|
|
continue;
|
|
}
|
|
|
|
UNGETCH();
|
|
break; /* out of for loop - only way out */
|
|
}
|
|
|
|
if(p >= LIMIT(Reuse_W)) { /* is this error # correct? */
|
|
fatal(1067);
|
|
}
|
|
|
|
if(((p - Reuse_W) > LIMIT_ID_LENGTH) && ( ! Prep )) {
|
|
p = Reuse_W + LIMIT_ID_LENGTH;
|
|
*p = L'\0';
|
|
c = local_c_hash(Reuse_W);
|
|
warning(4011, Reuse_W); /* id truncated */
|
|
} else {
|
|
*p = L'\0'; /* terminates identifier for expandable check */
|
|
}
|
|
|
|
Reuse_W_hash = (hash_t)c;
|
|
Reuse_W_length = (UINT)((p - Reuse_W) + 1);
|
|
|
|
return(length != (p - Reuse_W));
|
|
}
|