2818 lines
62 KiB
C
2818 lines
62 KiB
C
//=======================================================================
|
|
// Copyright XashXT Group 2007 ©
|
|
// pr_lex.c - qcclib lexemes set
|
|
//=======================================================================
|
|
|
|
#include <stdio.h>
|
|
//#include <time.h>
|
|
#include "vprogs.h"
|
|
#include "mathlib.h"
|
|
|
|
char *compilingfile;
|
|
int pr_source_line;
|
|
char *pr_file_p;
|
|
char *pr_line_start; // start of current source line
|
|
int pr_bracelevel;
|
|
char pr_token[8192];
|
|
token_type_t pr_token_type;
|
|
type_t *pr_immediate_type;
|
|
eval_t pr_immediate;
|
|
char pr_immediate_string[8192];
|
|
int pr_error_count;
|
|
int pr_warning_count;
|
|
int pr_total_error_count;
|
|
const_t *CompilerConstant;
|
|
int numCompilerConstants;
|
|
int ForcedCRC;
|
|
bool recursivefunctiontype;
|
|
|
|
// read on until the end of the line
|
|
#define GoToEndLine() while(*pr_file_p != '\n' && *pr_file_p != '\0'){ pr_file_p++; }
|
|
|
|
// longer symbols must be before a shorter partial match
|
|
char *pr_punctuation1[] = {"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "(+)", "|=", "(-)", "++", "--", "->", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "<<", "<", ">>", ">" , "#" , "@", "&" , "|", "^", ":", NULL};
|
|
char *pr_punctuation2[] = {"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "|=", "|=", "(-)", "++", "--", ".", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "<<", "<", ">>", ">" , "#" , "@", "&" , "|", "^", ":", NULL};
|
|
|
|
optimisations_t pr_optimisations[] =
|
|
{
|
|
// level debug = include debug info
|
|
{&opt_writesources, "ws", "write source", FL_DBG, FLAG_V7_ONLY },
|
|
{&opt_writelinenums, "wl", "write linenums", FL_DBG, 0 },
|
|
{&opt_writetypes, "wt", "write types", FL_DBG, FLAG_V7_ONLY },
|
|
{&opt_laxcasts, "lx", "allow laxcasting", FL_DBG, 0 },
|
|
|
|
// level 0 = fixed some qcc errors
|
|
{&opt_nonvec_parms, "nv", "fix nonvec parms", FL_OP0, FLAG_DEFAULT },
|
|
|
|
// level 1 = size optimizations
|
|
{&opt_shortenifnots, "f", "shorten if(!a)", FL_OP1, FLAG_DEFAULT },
|
|
{&opt_assignments, "e", "assigments", FL_OP1, FLAG_DEFAULT },
|
|
{&opt_dupconstdefs, "j", "no dup constants", FL_OP1, FLAG_DEFAULT },
|
|
{&opt_noduplicatestrings, "k", "no dup strings", FL_OP1, 0, },
|
|
{&opt_locals, "l", "strip local names", FL_OP1, 0 },
|
|
{&opt_function_names, "m", "strip func names", FL_OP1, 0 },
|
|
{&opt_filenames, "n", "strip file names", FL_OP1, 0 },
|
|
{&opt_unreferenced, "o", "strip unreferenced", FL_OP1, FLAG_DEFAULT },
|
|
{&opt_overlaptemps, "p", "optimize overlaptemps", FL_OP1, FLAG_DEFAULT },
|
|
{&opt_constantarithmatic, "q", "precompute constnts", FL_OP1, FLAG_DEFAULT },
|
|
{&opt_compstrings, "x", "deflate prog strings", FL_OP1, FLAG_V7_ONLY },
|
|
|
|
// level 2 = speed optimizations
|
|
{&opt_constant_names, "h", "strip const names", FL_OP2, 0 },
|
|
{&opt_precache_file, "r", "strip precache files", FL_OP2, 0 },
|
|
{&opt_compfunctions, "y", "deflate prog funcs", FL_OP2, FLAG_V7_ONLY },
|
|
|
|
// level 3 = dodgy optimizations
|
|
{&opt_return_only, "s", "optimize return calls", FL_OP3, 0 },
|
|
{&opt_compound_jumps, "t", "optimize num of jumps", FL_OP3, 0 },
|
|
{&opt_stripfunctions, "u", "strip functions", FL_OP3, 0 },
|
|
{&opt_constant_names_strings, "i", "strip const strings", FL_OP3, 0 },
|
|
{&opt_compress_other, "z", "deflate all prog", FL_OP3, FLAG_V7_ONLY },
|
|
{&opt_logicops, "a", "optimize logic ops", FL_OP3, 0 },
|
|
{&opt_ifstring, "sf","if(string) fix", FL_OP3, FLAG_V7_ONLY },
|
|
|
|
// level 4 = use with caution, may be bugly
|
|
{&opt_locals_marshalling, "y", "reduce locals, buggly", FL_OP4, FLAG_V7_ONLY },
|
|
{&opt_vectorcalls, "w", "optimize vector calls", FL_OP4, FLAG_V7_ONLY },
|
|
{NULL, "", "", 0, 0 },
|
|
};
|
|
|
|
// simple types. function types are dynamically allocated
|
|
type_t *type_void;
|
|
type_t *type_string;
|
|
type_t *type_float;
|
|
type_t *type_vector;
|
|
type_t *type_entity;
|
|
type_t *type_field;
|
|
type_t *type_function;
|
|
type_t *type_pointer;
|
|
type_t *type_integer;
|
|
type_t *type_variant;
|
|
type_t *type_floatfield;
|
|
|
|
const int type_size[12] =
|
|
{
|
|
1, // void
|
|
sizeof(string_t)/4, // string
|
|
1, // float
|
|
3, // vector
|
|
1, // entity
|
|
1, // field
|
|
sizeof(func_t)/4, // function
|
|
sizeof(void *)/4, // pointer
|
|
1, // integer
|
|
1, // FIXME: how big should a variant be?
|
|
0, // ev_struct. variable sized.
|
|
0 // ev_union. variable sized.
|
|
};
|
|
|
|
char pr_parm_names[MAX_PARMS + MAX_PARMS_EXTRA][MAX_NAME];
|
|
def_t def_ret, def_parms[MAX_PARMS];
|
|
includechunk_t *currentchunk;
|
|
|
|
/*
|
|
=================
|
|
PR_SkipWhiteSpace
|
|
=================
|
|
*/
|
|
const char *PR_SkipWhiteSpace( const char *data_p, bool *newline )
|
|
{
|
|
int c;
|
|
|
|
while((c = *data_p) <= ' ')
|
|
{
|
|
if( !c ) return NULL;
|
|
if( c == '\n' )
|
|
*newline = true;
|
|
data_p++;
|
|
}
|
|
return data_p;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_ParseToken
|
|
|
|
Parse a token out of a string
|
|
==============
|
|
*/
|
|
char *PR_ParseToken( const char **data_p, bool allow_newline )
|
|
{
|
|
int c;
|
|
int len = 0;
|
|
const char *data;
|
|
bool newline = false;
|
|
|
|
pr_token[0] = 0;
|
|
data = *data_p;
|
|
|
|
if( !data )
|
|
{
|
|
*data_p = NULL;
|
|
return pr_token;
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
data = PR_SkipWhiteSpace( data, &newline );
|
|
if( !data )
|
|
{
|
|
*data_p = NULL;
|
|
return pr_token;
|
|
}
|
|
if( newline && !allow_newline )
|
|
{
|
|
*data_p = data;
|
|
return pr_token;
|
|
}
|
|
|
|
c = *data;
|
|
|
|
if( c=='/' && data[1] == '/' )
|
|
{
|
|
// skip // comments
|
|
while( *data && *data != '\n' )
|
|
data++;
|
|
}
|
|
else if( c=='/' && data[1] == '*' )
|
|
{
|
|
// skip /* comments
|
|
while( data[1] && (data[0] != '*' || data[1] != '/'))
|
|
data++;
|
|
if( *data ) data += 2;
|
|
}
|
|
else break; // an actual token
|
|
}
|
|
|
|
// handle quoted strings specially
|
|
if (*data == '\"' || *data == '\'')
|
|
{
|
|
data++;
|
|
while( 1 )
|
|
{
|
|
c = *data++;
|
|
if( c=='\"' || c=='\0' )
|
|
{
|
|
pr_token[len] = 0;
|
|
*data_p = data;
|
|
return pr_token;
|
|
}
|
|
pr_token[len++] = c;
|
|
}
|
|
}
|
|
|
|
// parse single characters
|
|
if( c == '{' || c == '}'|| c == ')' || c == '(' || c == '\'' || c == ':' || c == ',' )
|
|
{
|
|
pr_token[len] = c;
|
|
data++;
|
|
len++;
|
|
pr_token[len] = 0;
|
|
*data_p = data;
|
|
return pr_token;
|
|
}
|
|
|
|
// parse a regular word
|
|
do
|
|
{
|
|
pr_token[len] = c;
|
|
data++;
|
|
len++;
|
|
c = *data;
|
|
if( c == '{' || c == '}'|| c == ')'|| c == '(' || c == '\'' || c == ':' || c == ',' )
|
|
break;
|
|
} while( c > 32 );
|
|
|
|
pr_token[len] = 0;
|
|
*data_p = data;
|
|
return pr_token;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_ParseWord
|
|
|
|
Parse a word out of a string
|
|
==============
|
|
*/
|
|
char *PR_ParseWord( const char **data_p, bool allow_newline )
|
|
{
|
|
int c;
|
|
const char *data;
|
|
int len = 0;
|
|
bool newline = false;
|
|
|
|
pr_token[0] = 0;
|
|
data = *data_p;
|
|
|
|
if( !data )
|
|
{
|
|
*data_p = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
data = PR_SkipWhiteSpace( data, &newline );
|
|
if( !data )
|
|
{
|
|
*data_p = NULL;
|
|
return NULL;
|
|
}
|
|
if( newline && !allow_newline )
|
|
{
|
|
*data_p = data;
|
|
return pr_token;
|
|
}
|
|
|
|
c = *data;
|
|
|
|
if( c=='/' && data[1] == '/' )
|
|
{
|
|
// skip // comments
|
|
while( *data && *data != '\n' )
|
|
data++;
|
|
}
|
|
else if( c=='/' && data[1] == '*' )
|
|
{
|
|
// skip /* comments
|
|
while( data[1] && (data[0] != '*' || data[1] != '/'))
|
|
data++;
|
|
if( *data ) data += 2;
|
|
}
|
|
else break; // an actual token
|
|
}
|
|
|
|
// handle quoted strings specially
|
|
if( c == '\"' )
|
|
{
|
|
data++;
|
|
do
|
|
{
|
|
c = *data++;
|
|
if( c=='\"' || c=='\0' )
|
|
{
|
|
pr_token[len] = 0;
|
|
*data_p = data;
|
|
return pr_token;
|
|
}
|
|
pr_token[len] = c;
|
|
len++;
|
|
} while( 1 );
|
|
}
|
|
|
|
// parse numbers
|
|
if( c >= '0' && c <= '9' )
|
|
{
|
|
if( c == '0' && data[1] == 'x' )
|
|
{
|
|
// parse hex
|
|
pr_token[0] = '0';
|
|
c = 'x';
|
|
len = 1;
|
|
data++;
|
|
while( 1 )
|
|
{
|
|
// parse regular number
|
|
pr_token[len] = c;
|
|
data++;
|
|
len++;
|
|
c = *data;
|
|
if ((c < '0'|| c > '9') && (c < 'a'||c > 'f') && (c < 'A'|| c > 'F') && c != '.')
|
|
break;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
while( 1 )
|
|
{
|
|
// parse regular number
|
|
pr_token[len] = c;
|
|
data++;
|
|
len++;
|
|
c = *data;
|
|
if ((c < '0'|| c > '9') && c != '.')
|
|
break;
|
|
}
|
|
}
|
|
|
|
pr_token[len] = 0;
|
|
*data_p = data;
|
|
return pr_token;
|
|
}
|
|
|
|
// parse words
|
|
else if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')
|
|
{
|
|
do
|
|
{
|
|
pr_token[len] = c;
|
|
data++;
|
|
len++;
|
|
c = *data;
|
|
} while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_');
|
|
|
|
pr_token[len] = 0;
|
|
*data_p = data;
|
|
return pr_token;
|
|
}
|
|
else
|
|
{
|
|
pr_token[len] = c;
|
|
len++;
|
|
pr_token[len] = 0;
|
|
*data_p = data;
|
|
return pr_token;
|
|
}
|
|
}
|
|
|
|
void PR_IncludeChunkEx( char *data, bool duplicate, char *filename, const_t *cnst )
|
|
{
|
|
includechunk_t *chunk = Qalloc( sizeof(includechunk_t));
|
|
chunk->prev = currentchunk;
|
|
currentchunk = chunk;
|
|
|
|
chunk->currentdatapoint = pr_file_p;
|
|
chunk->currentlinenumber = pr_source_line;
|
|
chunk->cnst = cnst;
|
|
if( cnst ) cnst->inside++;
|
|
|
|
if (duplicate)
|
|
{
|
|
pr_file_p = Qalloc(com.strlen( data ) + 1);
|
|
com.strcpy( pr_file_p, data );
|
|
}
|
|
else pr_file_p = data;
|
|
}
|
|
|
|
void PR_IncludeChunk( char *data, bool duplicate, char *filename )
|
|
{
|
|
PR_IncludeChunkEx( data, duplicate, filename, NULL );
|
|
}
|
|
|
|
bool PR_UnInclude(void)
|
|
{
|
|
if (!currentchunk) return false;
|
|
|
|
if( currentchunk->cnst ) currentchunk->cnst->inside--;
|
|
pr_file_p = currentchunk->currentdatapoint;
|
|
pr_source_line = currentchunk->currentlinenumber;
|
|
|
|
currentchunk = currentchunk->prev;
|
|
return true;
|
|
}
|
|
|
|
type_t *PR_NewType( char *name, int basictype )
|
|
{
|
|
if( numtypeinfos >= maxtypeinfos ) PR_ParseError(ERR_INTERNAL, "Too many types");
|
|
Mem_Set(&qcc_typeinfo[numtypeinfos], 0, sizeof(type_t));
|
|
qcc_typeinfo[numtypeinfos].type = basictype;
|
|
qcc_typeinfo[numtypeinfos].name = name;
|
|
qcc_typeinfo[numtypeinfos].num_parms = 0;
|
|
qcc_typeinfo[numtypeinfos].param = NULL;
|
|
qcc_typeinfo[numtypeinfos].size = type_size[basictype];
|
|
|
|
numtypeinfos++;
|
|
return &qcc_typeinfo[numtypeinfos-1];
|
|
}
|
|
|
|
void PR_FindBestInclude( char *newfile, char *currentfile, char *rootpath )
|
|
{
|
|
char fullname[MAX_SYSPATH];
|
|
char *stripfrom;
|
|
char *end = fullname;
|
|
|
|
if( !*newfile ) return;
|
|
|
|
// allow to include ../pathes/
|
|
currentfile += com.strlen( rootpath ); // could this be bad?
|
|
|
|
for( stripfrom = currentfile + com.strlen(currentfile) - 1; stripfrom > currentfile; stripfrom-- )
|
|
{
|
|
if( *stripfrom == '/' || *stripfrom == '\\' )
|
|
{
|
|
stripfrom++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
com.strcpy( end, rootpath );
|
|
end = end + com.strlen(end);
|
|
|
|
if( *fullname && end[-1] != '/' )
|
|
{
|
|
com.strcpy( end, "/" );
|
|
end = end + com.strlen(end);
|
|
}
|
|
|
|
com.strncpy( end, currentfile, stripfrom - currentfile );
|
|
end += stripfrom - currentfile; *end = '\0';
|
|
|
|
// FIXME: clean code
|
|
com.strcpy( fullname, newfile );
|
|
|
|
PR_Include( fullname );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_Precompiler
|
|
|
|
Runs precompiler stage
|
|
==============
|
|
*/
|
|
bool PR_Precompiler(void)
|
|
{
|
|
char buf[1024];
|
|
char *msg = buf;
|
|
int ifmode;
|
|
int a;
|
|
static int ifs = 0;
|
|
int level; //#if level
|
|
bool eval = false;
|
|
|
|
if (*pr_file_p == '#')
|
|
{
|
|
char *directive;
|
|
for (directive = pr_file_p + 1; *directive; directive++) // so # define works
|
|
{
|
|
if (*directive == '\r' || *directive == '\n')
|
|
PR_ParseError(ERR_UNKNOWNPUCTUATION, "Hanging # with no directive\n");
|
|
if (*directive > ' ') break;
|
|
}
|
|
if (!com.strncmp(directive, "define", 6))
|
|
{
|
|
pr_file_p = directive;
|
|
PR_ConditionCompilation();
|
|
GoToEndLine();
|
|
}
|
|
else if (!com.strncmp(directive, "undef", 5))
|
|
{
|
|
pr_file_p = directive+5;
|
|
while(*pr_file_p <= ' ') pr_file_p++;
|
|
|
|
PR_SimpleGetToken ();
|
|
PR_UndefineName(pr_token);
|
|
GoToEndLine();
|
|
}
|
|
else if (!com.strncmp(directive, "if", 2))
|
|
{
|
|
int originalline = pr_source_line;
|
|
pr_file_p = directive + 2;
|
|
if (!com.strncmp(pr_file_p, "def ", 4))
|
|
{
|
|
ifmode = 0;
|
|
pr_file_p+=4;
|
|
}
|
|
else if (!com.strncmp(pr_file_p, "ndef ", 5))
|
|
{
|
|
ifmode = 1;
|
|
pr_file_p+=5;
|
|
}
|
|
else
|
|
{
|
|
ifmode = 2;
|
|
pr_file_p+=0;
|
|
}
|
|
|
|
PR_SimpleGetToken ();
|
|
level = 1;
|
|
|
|
GoToEndLine();
|
|
|
|
if (ifmode == 2)
|
|
{
|
|
if (com.atof(pr_token)) eval = true;
|
|
}
|
|
else
|
|
{
|
|
if (PR_CheckCompConstDefined(pr_token))
|
|
eval = true;
|
|
|
|
if (ifmode == 1) eval = eval ? false : true; //same as eval = !eval
|
|
}
|
|
|
|
if (eval) ifs++;
|
|
else
|
|
{
|
|
while (1)
|
|
{
|
|
while(*pr_file_p && (*pr_file_p==' ' || *pr_file_p == '\t'))
|
|
pr_file_p++;
|
|
|
|
if (!*pr_file_p)
|
|
{
|
|
pr_source_line = originalline;
|
|
PR_ParseError (ERR_NOENDIF, "#if with no endif");
|
|
}
|
|
|
|
if (*pr_file_p == '#')
|
|
{
|
|
pr_file_p++;
|
|
while(*pr_file_p==' ' || *pr_file_p == '\t')
|
|
pr_file_p++;
|
|
if (!com.strncmp(pr_file_p, "endif", 5))
|
|
level--;
|
|
if (!com.strncmp(pr_file_p, "if", 2))
|
|
level++;
|
|
if (!com.strncmp(pr_file_p, "else", 4) && level == 1)
|
|
{
|
|
ifs++;
|
|
GoToEndLine();
|
|
}
|
|
}
|
|
|
|
GoToEndLine();
|
|
if (level <= 0) break;
|
|
pr_file_p++; // next line
|
|
pr_source_line++;
|
|
}
|
|
}
|
|
}
|
|
else if (!com.strncmp(directive, "else", 4))
|
|
{
|
|
int originalline = pr_source_line;
|
|
|
|
ifs -= 1;
|
|
level = 1;
|
|
|
|
GoToEndLine();
|
|
while (1)
|
|
{
|
|
while(*pr_file_p && (*pr_file_p==' ' || *pr_file_p == '\t'))
|
|
pr_file_p++;
|
|
|
|
if (!*pr_file_p)
|
|
{
|
|
pr_source_line = originalline;
|
|
PR_ParseError(ERR_NOENDIF, "#if with no endif");
|
|
}
|
|
|
|
if (*pr_file_p == '#')
|
|
{
|
|
pr_file_p++;
|
|
while(*pr_file_p==' ' || *pr_file_p == '\t')
|
|
pr_file_p++;
|
|
|
|
if (!com.strncmp(pr_file_p, "endif", 5))
|
|
level--;
|
|
if (!com.strncmp(pr_file_p, "if", 2))
|
|
level++;
|
|
if (!com.strncmp(pr_file_p, "else", 4) && level == 1)
|
|
{
|
|
ifs+=1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
GoToEndLine();
|
|
if (level <= 0) break;
|
|
pr_file_p++; // go off the end
|
|
pr_source_line++;
|
|
}
|
|
}
|
|
else if (!com.strncmp(directive, "endif", 5))
|
|
{
|
|
GoToEndLine();
|
|
if (ifs <= 0) PR_ParseError(ERR_NOPRECOMPILERIF, "unmatched #endif");
|
|
else ifs -= 1;
|
|
}
|
|
else if (!com.strncmp(directive, "eof", 3))
|
|
{
|
|
pr_file_p = NULL;
|
|
return true;
|
|
}
|
|
else if (!com.strncmp(directive, "error", 5))
|
|
{
|
|
pr_file_p = directive+5;
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
GoToEndLine();
|
|
PR_ParseError(ERR_PRECOMPILERMESSAGE, "#error: %s", msg);
|
|
}
|
|
else if (!com.strncmp(directive, "warning", 7))
|
|
{
|
|
pr_file_p = directive+7;
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
GoToEndLine();
|
|
PR_ParseWarning(WARN_PRECOMPILERMESSAGE, "#warning: %s", msg);
|
|
}
|
|
else if (!com.strncmp(directive, "message", 7))
|
|
{
|
|
pr_file_p = directive+7;
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
GoToEndLine();
|
|
PR_Message("#message: %s\n", msg);
|
|
}
|
|
else if (!com.strncmp(directive, "copyright", 9))
|
|
{
|
|
pr_file_p = directive+9;
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
GoToEndLine();
|
|
if (com.strlen(msg) >= sizeof(v_copyright))
|
|
PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n");
|
|
com.strncpy(v_copyright, msg, sizeof(v_copyright)-1);
|
|
}
|
|
else if (!com.strncmp(directive, "forcecrc", 8))
|
|
{
|
|
pr_file_p=directive+8;
|
|
|
|
ForcedCRC = PR_LexInteger();
|
|
|
|
pr_file_p++;
|
|
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
GoToEndLine();
|
|
}
|
|
else if (!com.strncmp(directive, "includelist", 11))
|
|
{
|
|
pr_file_p=directive+11;
|
|
|
|
while(*pr_file_p <= ' ') pr_file_p++;
|
|
|
|
while(1)
|
|
{
|
|
PR_LexWhitespace();
|
|
if (!PR_SimpleGetToken())
|
|
{
|
|
if (!*pr_file_p)
|
|
PR_ParseError(ERR_INTERNAL, "eof in includelist");
|
|
else
|
|
{
|
|
pr_file_p++;
|
|
pr_source_line++;
|
|
}
|
|
continue;
|
|
}
|
|
if (!com.strcmp(pr_token, "#endlist"))
|
|
break;
|
|
|
|
PR_FindBestInclude(pr_token, compilingfile, sourcedir);
|
|
|
|
if (*pr_file_p == '\r')
|
|
pr_file_p++;
|
|
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
GoToEndLine();
|
|
}
|
|
GoToEndLine();
|
|
}
|
|
else if (!com.strncmp(directive, "include", 7))
|
|
{
|
|
char sm;
|
|
|
|
pr_file_p=directive+7;
|
|
|
|
while(*pr_file_p <= ' ') pr_file_p++;
|
|
|
|
msg[0] = '\0';
|
|
if (*pr_file_p == '\"') sm = '\"';
|
|
else if (*pr_file_p == '<') sm = '>';
|
|
else
|
|
{
|
|
PR_ParseError(0, "Not a string literal (on a #include)");
|
|
sm = 0;
|
|
}
|
|
|
|
pr_file_p++;
|
|
a = 0;
|
|
|
|
while(*pr_file_p != sm)
|
|
{
|
|
if (*pr_file_p == '\n')
|
|
{
|
|
PR_ParseError(0, "#include continued over line boundy\n");
|
|
break;
|
|
}
|
|
msg[a++] = *pr_file_p;
|
|
pr_file_p++;
|
|
}
|
|
msg[a] = 0;
|
|
|
|
PR_FindBestInclude(msg, compilingfile, sourcedir);
|
|
pr_file_p++;
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0' && *pr_file_p <= ' ')
|
|
pr_file_p++;
|
|
GoToEndLine();
|
|
}
|
|
else if (!com.strncmp(directive, "library", 8))
|
|
{
|
|
pr_file_p=directive+8;
|
|
|
|
while(*pr_file_p <= ' ') pr_file_p++;
|
|
|
|
PR_LexString();
|
|
PR_Message("Including library: %s\n", pr_token);
|
|
QCC_LoadData(pr_token);
|
|
|
|
pr_file_p++;
|
|
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
GoToEndLine();
|
|
}
|
|
else if (!com.strncmp(directive, "output", 6))
|
|
{
|
|
pr_file_p = directive + 6;
|
|
|
|
while(*pr_file_p <= ' ') pr_file_p++;
|
|
|
|
PR_LexString();
|
|
com.strcpy(progsoutname, pr_token);
|
|
PR_Message("Outputfile: %s\n", progsoutname);
|
|
|
|
pr_file_p++;
|
|
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
GoToEndLine();
|
|
}
|
|
else if (!com.strncmp(directive, "pragma", 6))
|
|
{
|
|
pr_file_p = directive+6;
|
|
while(*pr_file_p <= ' ') pr_file_p++;
|
|
|
|
pr_token[0] = '\0';
|
|
for(a = 0; *pr_file_p != '\n' && *pr_file_p != '\0'; pr_file_p++) // read on until the end of the line
|
|
{
|
|
if ((*pr_file_p == ' ' || *pr_file_p == '\t'|| *pr_file_p == '(') && !*pr_token)
|
|
{
|
|
msg[a] = '\0';
|
|
com.strcpy(pr_token, msg);
|
|
a=0;
|
|
continue;
|
|
}
|
|
msg[a++] = *pr_file_p;
|
|
}
|
|
|
|
msg[a] = '\0';
|
|
{
|
|
char *end;
|
|
for (end = msg + a-1; end>=msg && *end <= ' '; end--)
|
|
*end = '\0';
|
|
}
|
|
|
|
if (!*pr_token)
|
|
{
|
|
com.strcpy(pr_token, msg);
|
|
msg[0] = '\0';
|
|
}
|
|
|
|
{
|
|
char *end;
|
|
for (end = msg + a-1; end>=msg && *end <= ' '; end--)
|
|
*end = '\0';
|
|
}
|
|
|
|
if (!com.stricmp( pr_token, "DONT_COMPILE_THIS_FILE" ))
|
|
{
|
|
while (*pr_file_p)
|
|
{
|
|
GoToEndLine();
|
|
if (*pr_file_p == '\n')
|
|
{
|
|
PR_NewLine(false);
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
}
|
|
else if (!com.stricmp( pr_token, "COPYRIGHT" ))
|
|
{
|
|
if (com.strlen(msg) >= sizeof(v_copyright))
|
|
PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n");
|
|
com.strncpy(v_copyright, msg, sizeof(v_copyright)-1);
|
|
}
|
|
else if (!com.strncmp(directive, "forcecrc", 8))
|
|
{
|
|
ForcedCRC = com.atoi(msg);
|
|
}
|
|
else if (!com.stricmp( pr_token, "warning" ))
|
|
{
|
|
int st;
|
|
|
|
PR_ParseToken( &msg, true );
|
|
if (!com.stricmp(pr_token, "enable") || !com.stricmp(pr_token, "on")) st = 0;
|
|
else if (!com.stricmp(pr_token, "disable") || !com.stricmp(pr_token, "off")) st = 1;
|
|
else if (!com.stricmp(pr_token, "toggle")) st = 2;
|
|
else
|
|
{
|
|
PR_ParseWarning(WARN_BADPRAGMA, "warning state not recognized");
|
|
st = -1;
|
|
}
|
|
if (st >= 0)
|
|
{
|
|
int wn;
|
|
PR_ParseToken( &msg, true ); // just a number of warning
|
|
wn = com.atoi( pr_token );
|
|
if( wn < 0 || wn > WARN_CONSTANTCOMPARISON )
|
|
{
|
|
PR_ParseWarning(WARN_BADPRAGMA, "warning id not recognized");
|
|
}
|
|
else
|
|
{
|
|
if (st == 2) pr_warning[wn] = true - pr_warning[wn];
|
|
else pr_warning[wn] = st;
|
|
}
|
|
}
|
|
}
|
|
else PR_ParseWarning(WARN_BADPRAGMA, "Unknown pragma \'%s\'", pr_token);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_NewLine
|
|
|
|
Call at start of file and when *pr_file_p == '\n'
|
|
==============
|
|
*/
|
|
void PR_NewLine (bool incomment)
|
|
{
|
|
bool m;
|
|
|
|
if (*pr_file_p == '\n')
|
|
{
|
|
pr_file_p++;
|
|
m = true;
|
|
}
|
|
else m = false;
|
|
|
|
pr_source_line++;
|
|
pr_line_start = pr_file_p;
|
|
while(*pr_file_p==' ' || *pr_file_p == '\t') pr_file_p++;
|
|
|
|
if (incomment); // no constants if in a comment.
|
|
else if (PR_Precompiler());
|
|
if (m) pr_file_p--;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexString
|
|
|
|
Parses a quoted string
|
|
==============
|
|
*/
|
|
void PR_LexString (void)
|
|
{
|
|
int c;
|
|
int len = 0;
|
|
char *end, *cnst;
|
|
int texttype = 0;
|
|
|
|
pr_file_p++;
|
|
|
|
do
|
|
{
|
|
c = *pr_file_p++;
|
|
if (!c) PR_ParseError (ERR_EOF, "EOF inside quote");
|
|
if (c=='\n') PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, "newline inside quote");
|
|
if (c=='\\')
|
|
{
|
|
// escape char
|
|
c = *pr_file_p++;
|
|
if (!c) PR_ParseError (ERR_EOF, "EOF inside quote");
|
|
if (c == 'n') c = '\n';
|
|
else if (c == 'r') c = '\r';
|
|
else if (c == '"') c = '"';
|
|
else if (c == 't') c = '\t';
|
|
else if (c == 'a') c = '\a';
|
|
else if (c == 'v') c = '\v';
|
|
else if (c == 'f') c = '\f';
|
|
else if (c == 's' || c == 'b')
|
|
{
|
|
texttype ^= 128;
|
|
continue;
|
|
}
|
|
else if (c == '[') c = 16;
|
|
else if (c == ']') c = 17;
|
|
else if (c == '{')
|
|
{
|
|
int d;
|
|
c = 0;
|
|
while ((d = *pr_file_p++) != '}')
|
|
{
|
|
c = c * 10 + d - '0';
|
|
if (d < '0' || d > '9' || c > 255)
|
|
PR_ParseError(ERR_BADCHARACTURECODE, "Bad character code");
|
|
}
|
|
}
|
|
else if (c == '<') c = 29;
|
|
else if (c == '-') c = 30;
|
|
else if (c == '>') c = 31;
|
|
else if (c == '\\') c = '\\';
|
|
else if (c == '\'') c = '\'';
|
|
else if (c >= '0' && c <= '9') c = 18 + c - '0';
|
|
else if (c == '\r')
|
|
{
|
|
c = *pr_file_p++;// sigh
|
|
if (c != '\n') PR_ParseWarning(WARN_HANGINGSLASHR, "Hanging \\\\\r");
|
|
pr_source_line++;
|
|
}
|
|
else if (c == '\n')
|
|
{
|
|
pr_source_line++;// sigh
|
|
}
|
|
else PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, "Unknown escape char %c", c);
|
|
}
|
|
else if (c=='\"')
|
|
{
|
|
if (len >= sizeof(pr_immediate_string) - 1)
|
|
PR_ParseError(ERR_INTERNAL, "String length exceeds %i", sizeof(pr_immediate_string)-1);
|
|
|
|
while(*pr_file_p && *pr_file_p <= ' ')
|
|
{
|
|
if (*pr_file_p == '\n')
|
|
PR_NewLine(false);
|
|
pr_file_p++;
|
|
}
|
|
if (*pr_file_p == '\"')
|
|
{
|
|
// have annother go
|
|
pr_file_p++;
|
|
continue;
|
|
}
|
|
pr_token[len] = 0;
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_string;
|
|
com.strcpy (pr_immediate_string, pr_token);
|
|
return;
|
|
}
|
|
else if (c == '#')
|
|
{
|
|
for (end = pr_file_p;; end++)
|
|
{
|
|
if (*end <= ' ') break;
|
|
if (*end == ')') break;
|
|
if (*end == '(') break;
|
|
if (*end == '+') break;
|
|
if (*end == '-') break;
|
|
if (*end == '*') break;
|
|
if (*end == '/') break;
|
|
if (*end =='\\') break;
|
|
if (*end == '|') break;
|
|
if (*end == '&') break;
|
|
if (*end == '=') break;
|
|
if (*end == '^') break;
|
|
if (*end == '~') break;
|
|
if (*end == '[') break;
|
|
if (*end == ']') break;
|
|
if (*end =='\"') break;
|
|
if (*end == '{') break;
|
|
if (*end == '}') break;
|
|
if (*end == ';') break;
|
|
if (*end == ':') break;
|
|
if (*end == ',') break;
|
|
if (*end == '.') break;
|
|
if (*end == '#') break;
|
|
}
|
|
|
|
c = *end;
|
|
*end = '\0';
|
|
cnst = PR_CheakCompConstString(pr_file_p);
|
|
if (cnst == pr_file_p) cnst = NULL;
|
|
*end = c;
|
|
c = '#'; // undo
|
|
if (cnst)
|
|
{
|
|
PR_ParseWarning(WARN_MACROINSTRING, "Macro expansion in string");
|
|
if (len+com.strlen(cnst) >= sizeof(pr_token)-1)
|
|
PR_ParseError(ERR_INTERNAL, "String length exceeds %i", sizeof(pr_token)-1);
|
|
com.strcpy(pr_token+len, cnst);
|
|
len+=com.strlen(cnst);
|
|
pr_file_p = end;
|
|
continue;
|
|
}
|
|
}
|
|
else c |= texttype;
|
|
|
|
pr_token[len] = c;
|
|
len++;
|
|
if (len >= sizeof(pr_token)-1)
|
|
PR_ParseError(ERR_INTERNAL, "String length exceeds %i", sizeof(pr_token)-1);
|
|
} while (1);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexNumber
|
|
==============
|
|
*/
|
|
int PR_LexInteger (void)
|
|
{
|
|
int c = *pr_file_p;
|
|
int len = 0;
|
|
|
|
if (pr_file_p[0] == '0' && pr_file_p[1] == 'x')
|
|
{
|
|
pr_token[0] = '0';
|
|
pr_token[1] = 'x';
|
|
len = 2;
|
|
c = *(pr_file_p+=2);
|
|
}
|
|
do
|
|
{
|
|
pr_token[len] = c;
|
|
len++;
|
|
pr_file_p++;
|
|
c = *pr_file_p;
|
|
} while ((c >= '0' && c<= '9') || c == '.' || (c>='a' && c <= 'f'));
|
|
|
|
pr_token[len] = 0;
|
|
return com.atoi (pr_token);
|
|
}
|
|
|
|
void PR_LexNumber (void)
|
|
{
|
|
int num = 0;
|
|
int base = 10;
|
|
int c;
|
|
int sign = 1;
|
|
|
|
if (*pr_file_p == '-')
|
|
{
|
|
sign = -1;
|
|
pr_file_p++;
|
|
}
|
|
if (pr_file_p[1] == 'x')
|
|
{
|
|
pr_file_p += 2;
|
|
base = 16;
|
|
}
|
|
|
|
while((c = *pr_file_p))
|
|
{
|
|
if (c >= '0' && c <= '9')
|
|
{
|
|
num*=base;
|
|
num += c-'0';
|
|
}
|
|
else if (c >= 'a' && c <= 'f')
|
|
{
|
|
num*=base;
|
|
num += c -'a'+10;
|
|
}
|
|
else if (c >= 'A' && c <= 'F')
|
|
{
|
|
num*=base;
|
|
num += c -'A'+10;
|
|
}
|
|
else if (c == '.')
|
|
{
|
|
pr_file_p++;
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = (float)num;
|
|
num = 1;
|
|
while(1)
|
|
{
|
|
c = *pr_file_p;
|
|
if (c >= '0' && c <= '9')
|
|
{
|
|
num*=base;
|
|
pr_immediate._float += (c-'0')/(float)(num);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
pr_file_p++;
|
|
}
|
|
pr_immediate._float *= sign;
|
|
return;
|
|
}
|
|
else if (c == 'i')
|
|
{
|
|
pr_file_p++;
|
|
pr_immediate_type = type_integer;
|
|
pr_immediate._int = num*sign;
|
|
return;
|
|
}
|
|
else break;
|
|
pr_file_p++;
|
|
}
|
|
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = (float)(num*sign);
|
|
}
|
|
|
|
float PR_LexFloat( void )
|
|
{
|
|
int c;
|
|
int len;
|
|
|
|
len = 0;
|
|
c = *pr_file_p;
|
|
do
|
|
{
|
|
pr_token[len] = c;
|
|
len++;
|
|
pr_file_p++;
|
|
c = *pr_file_p;
|
|
// only allow a . if the next isn't too...
|
|
} while((c >= '0' && c <= '9') || (c == '.'&&pr_file_p[1]!='.') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
|
|
|
|
pr_token[len] = 0;
|
|
return (float)com.atof (pr_token);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexVector
|
|
|
|
Parses a single quoted vector
|
|
==============
|
|
*/
|
|
void PR_LexVector (void)
|
|
{
|
|
int i;
|
|
|
|
pr_file_p++;
|
|
|
|
if (*pr_file_p == '\\')
|
|
{
|
|
// extended characture constant
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
pr_file_p++;
|
|
switch(*pr_file_p)
|
|
{
|
|
case 'n':
|
|
pr_immediate._float = '\n';
|
|
break;
|
|
case 'r':
|
|
pr_immediate._float = '\r';
|
|
break;
|
|
case 't':
|
|
pr_immediate._float = '\t';
|
|
break;
|
|
case '\'':
|
|
pr_immediate._float = '\'';
|
|
break;
|
|
case '\"':
|
|
pr_immediate._float = '\"';
|
|
break;
|
|
case '\\':
|
|
pr_immediate._float = '\\';
|
|
break;
|
|
default:
|
|
PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad characture constant");
|
|
break;
|
|
}
|
|
if (*pr_file_p != '\'')
|
|
PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad characture constant");
|
|
pr_file_p++;
|
|
return;
|
|
}
|
|
if (pr_file_p[1] == '\'')
|
|
{
|
|
// character constant
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = pr_file_p[0];
|
|
pr_file_p+=2;
|
|
return;
|
|
}
|
|
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_vector;
|
|
PR_LexWhitespace ();
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
pr_immediate.vector[i] = PR_LexFloat ();
|
|
PR_LexWhitespace ();
|
|
}
|
|
|
|
if (*pr_file_p != '\'')
|
|
PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad vector");
|
|
pr_file_p++;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexName
|
|
|
|
Parses an identifier
|
|
==============
|
|
*/
|
|
void PR_LexName (void)
|
|
{
|
|
int c;
|
|
int len;
|
|
|
|
len = 0;
|
|
c = *pr_file_p;
|
|
do
|
|
{
|
|
pr_token[len] = c;
|
|
len++;
|
|
pr_file_p++;
|
|
c = *pr_file_p;
|
|
} while ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9'));
|
|
|
|
pr_token[len] = 0;
|
|
pr_token_type = tt_name;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexPunctuation
|
|
==============
|
|
*/
|
|
void PR_LexPunctuation (void)
|
|
{
|
|
int i;
|
|
int len;
|
|
char *p;
|
|
|
|
pr_token_type = tt_punct;
|
|
|
|
for (i = 0; (p = pr_punctuation1[i]) != NULL; i++)
|
|
{
|
|
len = com.strlen(p);
|
|
if (!com.strncmp(p, pr_file_p, len) )
|
|
{
|
|
com.strcpy (pr_token, pr_punctuation2[i]);
|
|
if (p[0] == '{') pr_bracelevel++;
|
|
else if (p[0] == '}') pr_bracelevel--;
|
|
pr_file_p += len;
|
|
return;
|
|
}
|
|
}
|
|
PR_ParseError (ERR_UNKNOWNPUCTUATION, "Unknown punctuation");
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexWhitespace
|
|
==============
|
|
*/
|
|
void PR_LexWhitespace (void)
|
|
{
|
|
int c;
|
|
|
|
while (1)
|
|
{
|
|
// skip whitespace
|
|
while ( (c = *pr_file_p) <= ' ')
|
|
{
|
|
if (c=='\n')
|
|
{
|
|
PR_NewLine (false);
|
|
if (!pr_file_p) return;
|
|
}
|
|
if (c == 0) return; // end of file
|
|
pr_file_p++;
|
|
}
|
|
|
|
// skip // comments
|
|
if (c=='/' && pr_file_p[1] == '/')
|
|
{
|
|
while (*pr_file_p && *pr_file_p != '\n')
|
|
pr_file_p++;
|
|
PR_NewLine(false);
|
|
pr_file_p++;
|
|
continue;
|
|
}
|
|
|
|
// skip /* */ comments
|
|
if (c=='/' && pr_file_p[1] == '*')
|
|
{
|
|
do
|
|
{
|
|
pr_file_p++;
|
|
if (pr_file_p[0]=='\n')
|
|
PR_NewLine(true);
|
|
if (pr_file_p[1] == 0)
|
|
{
|
|
pr_file_p++;
|
|
return;
|
|
}
|
|
} while (pr_file_p[-1] != '*' || pr_file_p[0] != '/');
|
|
pr_file_p++;
|
|
continue;
|
|
}
|
|
break; // a real character has been found
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
#define MAX_FRAMES 8192
|
|
char pr_framemodelname[64];
|
|
char pr_framemacros[MAX_FRAMES][16];
|
|
int pr_framemacrovalue[MAX_FRAMES];
|
|
int pr_nummacros, pr_oldmacros;
|
|
int pr_macrovalue;
|
|
int pr_savedmacro;
|
|
|
|
void PR_ClearGrabMacros (void)
|
|
{
|
|
pr_oldmacros = pr_nummacros;
|
|
pr_macrovalue = 0;
|
|
pr_savedmacro = -1;
|
|
}
|
|
|
|
int PR_FindMacro (char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = pr_nummacros - 1; i >= 0; i--)
|
|
{
|
|
if (!STRCMP (name, pr_framemacros[i]))
|
|
{
|
|
return pr_framemacrovalue[i];
|
|
}
|
|
}
|
|
for (i = pr_nummacros - 1; i >= 0; i--)
|
|
{
|
|
if (!com.stricmp (name, pr_framemacros[i]))
|
|
{
|
|
PR_ParseWarning(WARN_CASEINSENSATIVEFRAMEMACRO, "Case insensative frame macro");
|
|
return pr_framemacrovalue[i];
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void PR_ExpandMacro(void)
|
|
{
|
|
int i = PR_FindMacro(pr_token);
|
|
|
|
if (i < 0) PR_ParseError (ERR_BADFRAMEMACRO, "Unknown frame macro $%s", pr_token);
|
|
|
|
com.sprintf (pr_token,"%d", i);
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = (float)i;
|
|
}
|
|
|
|
bool PR_SimpleGetToken (void)
|
|
{
|
|
int c;
|
|
int i = 0;
|
|
|
|
// skip whitespace
|
|
while ( (c = *pr_file_p) <= ' ')
|
|
{
|
|
if (c=='\n' || c == 0)
|
|
return false;
|
|
pr_file_p++;
|
|
}
|
|
if (pr_file_p[0] == '/')
|
|
{
|
|
if (pr_file_p[1] == '/')
|
|
{ //comment alert
|
|
while(*pr_file_p && *pr_file_p != '\n')
|
|
pr_file_p++;
|
|
return false;
|
|
}
|
|
if (pr_file_p[1] == '*')
|
|
return false;
|
|
}
|
|
|
|
while ( (c = *pr_file_p) > ' ' && c != ',' && c != ';' && c != ')' && c != '(' && c != ']')
|
|
{
|
|
pr_token[i] = c;
|
|
i++;
|
|
pr_file_p++;
|
|
}
|
|
pr_token[i] = 0;
|
|
return i!=0;
|
|
}
|
|
|
|
void PR_MacroFrame(char *name, int value)
|
|
{
|
|
int i;
|
|
for (i = pr_nummacros - 1; i >= 0; i--)
|
|
{
|
|
if (!STRCMP (name, pr_framemacros[i]))
|
|
{
|
|
pr_framemacrovalue[i] = value;
|
|
if (i >= pr_oldmacros)
|
|
PR_ParseWarning(WARN_DUPLICATEMACRO, "Duplicate macro defined (%s)", pr_token);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( com.strlen(name)+1 > sizeof(pr_framemacros[0]))
|
|
PR_ParseWarning(ERR_TOOMANYFRAMEMACROS, "Name for frame macro %s is too long", name);
|
|
else
|
|
{
|
|
com.strcpy( pr_framemacros[pr_nummacros], name );
|
|
pr_framemacrovalue[pr_nummacros] = value;
|
|
pr_nummacros++;
|
|
if(pr_nummacros >= MAX_FRAMES) PR_ParseError(ERR_TOOMANYFRAMEMACROS, "Too many frame macros defined");
|
|
}
|
|
}
|
|
|
|
void PR_ParseFrame (void)
|
|
{
|
|
while (PR_SimpleGetToken ())
|
|
{
|
|
PR_MacroFrame(pr_token, pr_macrovalue++);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexGrab
|
|
|
|
Deals with counting sequence numbers and replacing frame macros
|
|
==============
|
|
*/
|
|
void PR_LexGrab (void)
|
|
{
|
|
pr_file_p++; // skip the $
|
|
|
|
if (*pr_file_p <= ' ') PR_ParseError (ERR_BADFRAMEMACRO, "hanging $");
|
|
PR_SimpleGetToken();
|
|
if (!*pr_token) PR_ParseError (ERR_BADFRAMEMACRO, "hanging $");
|
|
|
|
// check for $frame
|
|
if (!STRCMP (pr_token, "frame") || !STRCMP (pr_token, "framesave"))
|
|
{
|
|
PR_ParseFrame();
|
|
PR_Lex();
|
|
}
|
|
|
|
// ignore other known $commands - just for model/spritegen
|
|
else if (!STRCMP (pr_token, "cd"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "load"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "type"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "load"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "spritename"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "framegroupstart"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "framegroupend"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "origin"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "base"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "flags"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "scale"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "skin"))
|
|
{
|
|
// skip to end of line
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "flush"))
|
|
{
|
|
PR_ClearGrabMacros();
|
|
while (PR_SimpleGetToken ());
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "framevalue"))
|
|
{
|
|
PR_SimpleGetToken ();
|
|
pr_macrovalue = com.atoi(pr_token);
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "framerestore"))
|
|
{
|
|
PR_SimpleGetToken ();
|
|
PR_ExpandMacro();
|
|
pr_macrovalue = (int)pr_immediate._float;
|
|
PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "modelname"))
|
|
{
|
|
int i;
|
|
PR_SimpleGetToken ();
|
|
|
|
if (*pr_framemodelname)
|
|
PR_MacroFrame(pr_framemodelname, pr_macrovalue);
|
|
|
|
com.strncpy(pr_framemodelname, pr_token, sizeof(pr_framemodelname)-1);
|
|
pr_framemodelname[sizeof(pr_framemodelname)-1] = '\0';
|
|
|
|
i = PR_FindMacro(pr_framemodelname);
|
|
if (i) pr_macrovalue = i;
|
|
else i = 0;
|
|
PR_Lex ();
|
|
}
|
|
else PR_ExpandMacro (); // look for a frame name macro
|
|
}
|
|
|
|
bool PR_UndefineName(char *name)
|
|
{
|
|
const_t *c = Hash_Get(&compconstantstable, name);
|
|
|
|
if(!c)
|
|
{
|
|
PR_ParseWarning(WARN_UNDEFNOTDEFINED, "Precompiler constant %s was not defined", name);
|
|
return false;
|
|
}
|
|
|
|
Hash_Remove(&compconstantstable, name);
|
|
return true;
|
|
}
|
|
|
|
const_t *PR_DefineName(char *name)
|
|
{
|
|
int i;
|
|
const_t *cnst;
|
|
|
|
if (com.strlen(name) >= MAX_NAME || !*name)
|
|
PR_ParseError(ERR_CONSTANTTOOLONG, "Compiler constant name length is too long or short");
|
|
|
|
cnst = Hash_Get(&compconstantstable, name);
|
|
if (cnst )
|
|
{
|
|
PR_ParseWarning(WARN_DUPLICATEDEFINITION, "Duplicate definition for Precompiler constant %s", name);
|
|
Hash_Remove(&compconstantstable, name);
|
|
}
|
|
|
|
cnst = Qalloc(sizeof(const_t));
|
|
cnst->used = false;
|
|
cnst->numparams = 0;
|
|
com.strcpy(cnst->name, name);
|
|
cnst->namelen = com.strlen(name);
|
|
*cnst->value = '\0';
|
|
for (i = 0; i < MAX_PARMS; i++) cnst->params[i][0] = '\0';
|
|
|
|
Hash_Add(&compconstantstable, cnst->name, cnst, Qalloc(sizeof(bucket_t)));
|
|
|
|
if (!STRCMP(name, "OP_NODUP")) opt_noduplicatestrings = true;
|
|
|
|
// group - optimize for a fast compiler
|
|
if (!STRCMP(name, "OP_TIME"))
|
|
{
|
|
PR_UndefineName("OP_SIZE");
|
|
PR_UndefineName("OP_SPEED");
|
|
|
|
PR_UndefineName("OP_NODUP");
|
|
PR_UndefineName("OP_COMP_ALL");
|
|
}
|
|
|
|
// group - optimize run speed
|
|
if (!STRCMP(name, "OP_SPEED"))
|
|
{
|
|
PR_UndefineName("OP_SIZE");
|
|
PR_UndefineName("OP_TIME");
|
|
PR_UndefineName("OP_COMP_ALL");
|
|
}
|
|
|
|
// group - produce small output.
|
|
if (!STRCMP(name, "OP_SIZE"))
|
|
{
|
|
PR_UndefineName("OP_SPEED");
|
|
PR_UndefineName("OP_TIME");
|
|
PR_DefineName("OP_NODUP");
|
|
PR_DefineName("OP_COMP_ALL");
|
|
}
|
|
|
|
// group - compress the output
|
|
if (!STRCMP(name, "OP_COMP_ALL"))
|
|
{
|
|
PR_DefineName("OP_COMP_STATEMENTS");
|
|
PR_DefineName("OP_COMP_DEFS");
|
|
PR_DefineName("OP_COMP_FIELDS");
|
|
PR_DefineName("OP_COMP_FUNCTIONS");
|
|
PR_DefineName("OP_COMP_STRINGS");
|
|
PR_DefineName("OP_COMP_GLOBALS");
|
|
PR_DefineName("OP_COMP_LINES");
|
|
PR_DefineName("OP_COMP_TYPES");
|
|
}
|
|
return cnst;
|
|
}
|
|
|
|
void PR_Undefine(void)
|
|
{
|
|
PR_SimpleGetToken ();
|
|
PR_UndefineName(pr_token);
|
|
}
|
|
|
|
void PR_ConditionCompilation( void )
|
|
{
|
|
char *oldval;
|
|
char *d;
|
|
char *s;
|
|
int quote=false;
|
|
const_t *cnst;
|
|
|
|
PR_SimpleGetToken ();
|
|
|
|
if (!PR_SimpleGetToken()) PR_ParseError(ERR_NONAME, "No name defined for compiler constant");
|
|
|
|
cnst = Hash_Get(&compconstantstable, pr_token);
|
|
if (cnst)
|
|
{
|
|
oldval = cnst->value;
|
|
Hash_Remove(&compconstantstable, pr_token);
|
|
}
|
|
else oldval = NULL;
|
|
|
|
cnst = PR_DefineName(pr_token);
|
|
|
|
if (*pr_file_p == '(')
|
|
{
|
|
s = pr_file_p + 1;
|
|
while(*pr_file_p++)
|
|
{
|
|
if (*pr_file_p == ',')
|
|
{
|
|
com.strncpy(cnst->params[cnst->numparams], s, pr_file_p-s);
|
|
cnst->params[cnst->numparams][pr_file_p-s] = '\0';
|
|
cnst->numparams++;
|
|
if (cnst->numparams > MAX_PARMS)
|
|
PR_ParseError(ERR_MACROTOOMANYPARMS, "May not have more than %i parameters to a macro", MAX_PARMS);
|
|
pr_file_p++;
|
|
s = pr_file_p;
|
|
}
|
|
if (*pr_file_p == ')')
|
|
{
|
|
com.strncpy(cnst->params[cnst->numparams], s, pr_file_p-s);
|
|
cnst->params[cnst->numparams][pr_file_p-s] = '\0';
|
|
cnst->numparams++;
|
|
if (cnst->numparams > MAX_PARMS)
|
|
PR_ParseError(ERR_MACROTOOMANYPARMS, "May not have more than %i parameters to a macro", MAX_PARMS);
|
|
pr_file_p++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else cnst->numparams = -1;
|
|
|
|
s = pr_file_p;
|
|
d = cnst->value;
|
|
while(*s == ' ' || *s == '\t') s++;
|
|
while(1)
|
|
{
|
|
if( *s == '\\' )
|
|
{
|
|
// read over a newline if necessary
|
|
if( s[1] == '\n' || s[1] == '\r' )
|
|
{
|
|
s++;
|
|
*d++ = *s++;
|
|
if(s[-1] == '\r' && s[0] == '\n') *d++ = *s++;
|
|
}
|
|
}
|
|
else if(*s == '\r' || *s == '\n' || *s == '\0')
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!quote && s[0]=='/'&&(s[1]=='/'||s[1]=='*')) break;
|
|
if (*s == '\"') quote=!quote;
|
|
*d = *s;
|
|
d++, s++;
|
|
}
|
|
|
|
*d = '\0';
|
|
d--;
|
|
while(*d<= ' ' && d >= cnst->value) *d-- = '\0';
|
|
|
|
if (com.strlen(cnst->value) >= sizeof(cnst->value)) //this is too late.
|
|
PR_ParseError(ERR_CONSTANTTOOLONG, "Macro %s too long (%i not %i)", cnst->name, com.strlen(cnst->value), sizeof(cnst->value));
|
|
|
|
if (oldval)
|
|
{
|
|
// we always warn if it was already defined
|
|
// we use different warning codes so that -Wno-mundane can be used to ignore identical redefinitions.
|
|
if (com.strcmp(oldval, cnst->value))
|
|
PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, "Alternate precompiler definition of %s", pr_token);
|
|
else PR_ParseWarning(WARN_IDENTICALPRECOMPILER, "Identical precompiler definition of %s", pr_token);
|
|
}
|
|
pr_file_p = s;
|
|
}
|
|
|
|
int PR_CheakCompConst( void )
|
|
{
|
|
char *oldpr_file_p = pr_file_p;
|
|
int whitestart;
|
|
const_t *c;
|
|
char *end;
|
|
|
|
for (end = pr_file_p; ; end++)
|
|
{
|
|
if (*end <= ' ') break;
|
|
if (*end == ')') break;
|
|
if (*end == '(') break;
|
|
if (*end == '+') break;
|
|
if (*end == '-') break;
|
|
if (*end == '*') break;
|
|
if (*end == '/') break;
|
|
if (*end == '|') break;
|
|
if (*end == '&') break;
|
|
if (*end == '=') break;
|
|
if (*end == '^') break;
|
|
if (*end == '~') break;
|
|
if (*end == '[') break;
|
|
if (*end == ']') break;
|
|
if (*end =='\"') break;
|
|
if (*end == '{') break;
|
|
if (*end == '}') break;
|
|
if (*end == ';') break;
|
|
if (*end == ':') break;
|
|
if (*end == ',') break;
|
|
if (*end == '.') break;
|
|
if (*end == '#') break;
|
|
}
|
|
|
|
Mem_Copy(pr_token, pr_file_p, end - pr_file_p);
|
|
pr_token[end - pr_file_p] = '\0';
|
|
|
|
c = Hash_Get(&compconstantstable, pr_token);
|
|
|
|
if (c && !c->inside)
|
|
{
|
|
pr_file_p = oldpr_file_p + com.strlen(c->name);
|
|
while(*pr_file_p == ' ' || *pr_file_p == '\t') pr_file_p++;
|
|
if (c->numparams>=0)
|
|
{
|
|
if (*pr_file_p == '(')
|
|
{
|
|
int p;
|
|
char *start;
|
|
char buffer[1024];
|
|
char *paramoffset[MAX_PARMS+1];
|
|
int param=0;
|
|
int plevel=0;
|
|
|
|
pr_file_p++;
|
|
while(*pr_file_p == ' ' || *pr_file_p == '\t') pr_file_p++;
|
|
start = pr_file_p;
|
|
|
|
while(1)
|
|
{
|
|
// handle strings correctly by ignoring them
|
|
if( *pr_file_p == '\"' )
|
|
{
|
|
do
|
|
{
|
|
pr_file_p++;
|
|
} while((pr_file_p[-1] == '\\' || pr_file_p[0] != '\"') && *pr_file_p && *pr_file_p != '\n');
|
|
}
|
|
if(*pr_file_p == '(') plevel++;
|
|
else if(!plevel && (*pr_file_p == ',' || *pr_file_p == ')'))
|
|
{
|
|
paramoffset[param++] = start;
|
|
start = pr_file_p+1;
|
|
if (*pr_file_p == ')')
|
|
{
|
|
*pr_file_p = '\0';
|
|
pr_file_p++;
|
|
break;
|
|
}
|
|
*pr_file_p = '\0';
|
|
pr_file_p++;
|
|
while(*pr_file_p == ' ' || *pr_file_p == '\t') pr_file_p++;
|
|
|
|
// move back by one char because we move forward by one at the end of the loop
|
|
pr_file_p--;
|
|
if (param == MAX_PARMS)
|
|
PR_ParseError(ERR_TOOMANYPARAMS, "Too many parameters in macro call");
|
|
}
|
|
else if(*pr_file_p == ')') plevel--;
|
|
|
|
if(!*pr_file_p) PR_ParseError( ERR_EOF, "EOF on macro call" );
|
|
pr_file_p++;
|
|
}
|
|
if (param < c->numparams)
|
|
PR_ParseError(ERR_TOOFEWPARAMS, "Not enough macro parameters");
|
|
paramoffset[param] = start;
|
|
|
|
*buffer = '\0';
|
|
oldpr_file_p = pr_file_p;
|
|
pr_file_p = c->value;
|
|
|
|
while( 1 )
|
|
{
|
|
whitestart = p = com.strlen(buffer);
|
|
while(*pr_file_p <= ' ') // copy across whitespace
|
|
{
|
|
if (!*pr_file_p) break;
|
|
buffer[p++] = *pr_file_p++;
|
|
}
|
|
buffer[p] = 0;
|
|
|
|
// if you ask for #a##b you will be shot. use #a #b instead, or chain macros.
|
|
if (*pr_file_p == '#')
|
|
{
|
|
if (pr_file_p[1] == '#')
|
|
{
|
|
// concatinate (skip out whitespace)
|
|
buffer[whitestart] = '\0';
|
|
pr_file_p+=2;
|
|
}
|
|
else
|
|
{ // stringify
|
|
pr_file_p++;
|
|
PR_ParseWord( &pr_file_p, true );
|
|
if (!pr_file_p) break;
|
|
|
|
for (p = 0; p < param; p++)
|
|
{
|
|
if (!STRCMP(pr_token, c->params[p]))
|
|
{
|
|
com.strcat(buffer, "\"");
|
|
com.strcat(buffer, paramoffset[p]);
|
|
com.strcat(buffer, "\"");
|
|
break;
|
|
}
|
|
}
|
|
if (p == param)
|
|
{
|
|
com.strcat(buffer, "#");
|
|
com.strcat(buffer, pr_token);
|
|
PR_ParseWarning(0, "Stringification ignored");
|
|
}
|
|
continue;// already did this one
|
|
}
|
|
}
|
|
PR_ParseWord( &pr_file_p, true );
|
|
if (!pr_file_p) break;
|
|
|
|
for (p = 0; p < param; p++)
|
|
{
|
|
if (!STRCMP(pr_token, c->params[p]))
|
|
{
|
|
com.strcat(buffer, paramoffset[p]);
|
|
break;
|
|
}
|
|
}
|
|
if (p == param) com.strcat(buffer, pr_token);
|
|
}
|
|
|
|
for (p = 0; p < param-1; p++)
|
|
paramoffset[p][com.strlen(paramoffset[p])] = ',';
|
|
paramoffset[p][com.strlen(paramoffset[p])] = ')';
|
|
pr_file_p = oldpr_file_p;
|
|
PR_IncludeChunkEx(buffer, true, NULL, c );
|
|
}
|
|
else PR_ParseError(ERR_TOOFEWPARAMS, "Macro without opening brace");
|
|
}
|
|
else PR_IncludeChunkEx(c->value, false, NULL, c );
|
|
|
|
PR_Lex();
|
|
return true;
|
|
}
|
|
|
|
// start of macros variables
|
|
if (!com.strncmp(pr_file_p, "__TIME__", 8))
|
|
{
|
|
pr_file_p = (char *)timestamp( TIME_TIME_ONLY );
|
|
PR_Lex();// translate the macro's value
|
|
pr_file_p = oldpr_file_p + 8;
|
|
|
|
return true;
|
|
}
|
|
if (!com.strncmp(pr_file_p, "__DATE__", 8))
|
|
{
|
|
pr_file_p = (char *)timestamp( TIME_DATE_ONLY );
|
|
PR_Lex(); // translate the macro's value
|
|
pr_file_p = oldpr_file_p + 8;
|
|
|
|
return true;
|
|
}
|
|
if (!com.strncmp(pr_file_p, "__FILE__", 8))
|
|
{
|
|
static char retbuf[256];
|
|
com.sprintf(retbuf, "\"%s\"", strings + s_file);
|
|
pr_file_p = retbuf;
|
|
PR_Lex(); // translate the macro's value
|
|
pr_file_p = oldpr_file_p + 8;
|
|
|
|
return true;
|
|
}
|
|
if (!com.strncmp(pr_file_p, "__LINE__", 8))
|
|
{
|
|
static char retbuf[256];
|
|
com.sprintf(retbuf, "\"%i\"", pr_source_line);
|
|
pr_file_p = retbuf;
|
|
PR_Lex(); //translate the macro's value
|
|
pr_file_p = oldpr_file_p + 8;
|
|
return true;
|
|
}
|
|
if (!com.strncmp(pr_file_p, "__FUNC__", 8))
|
|
{
|
|
static char retbuf[256];
|
|
com.sprintf(retbuf, "\"%s\"", pr_scope->name);
|
|
pr_file_p = retbuf;
|
|
PR_Lex(); //translate the macro's value
|
|
pr_file_p = oldpr_file_p+8;
|
|
return true;
|
|
}
|
|
if (!com.strncmp(pr_file_p, "__NULL__", 8))
|
|
{
|
|
static char retbuf[256];
|
|
com.sprintf(retbuf, "~0");
|
|
pr_file_p = retbuf;
|
|
PR_Lex(); //translate the macro's value
|
|
pr_file_p = oldpr_file_p + 8;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
char *PR_CheakCompConstString(char *def)
|
|
{
|
|
char *s;
|
|
const_t *c;
|
|
|
|
c = Hash_Get(&compconstantstable, def);
|
|
if (c)
|
|
{
|
|
s = PR_CheakCompConstString(c->value);
|
|
return s;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
const_t *PR_CheckCompConstDefined(char *def)
|
|
{
|
|
return Hash_Get(&compconstantstable, def);
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
==============
|
|
PR_Lex
|
|
|
|
Advanced version of Com_ParseToken()
|
|
Sets pr_token, pr_token_type, and possibly pr_immediate and pr_immediate_type
|
|
==============
|
|
*/
|
|
void PR_Lex (void)
|
|
{
|
|
int c;
|
|
|
|
pr_token[0] = 0;
|
|
|
|
if (!pr_file_p) goto end_of_file;
|
|
|
|
PR_LexWhitespace();
|
|
|
|
if (!pr_file_p) goto end_of_file;
|
|
c = *pr_file_p;
|
|
if (!c) goto end_of_file;
|
|
|
|
// handle quoted strings as a unit
|
|
if (c == '\"')
|
|
{
|
|
PR_LexString ();
|
|
return;
|
|
}
|
|
|
|
// handle quoted vectors as a unit
|
|
if (c == '\'')
|
|
{
|
|
PR_LexVector ();
|
|
return;
|
|
}
|
|
|
|
// if the first character is a valid identifier, parse until a non-id
|
|
// character is reached
|
|
if ( c == '~' || c == '%')
|
|
{
|
|
// let's see which one we make into an operator first... possibly both...
|
|
pr_file_p++;
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_integer;
|
|
pr_immediate._int = PR_LexInteger ();
|
|
return;
|
|
}
|
|
if ( c == '0' && pr_file_p[1] == 'x')
|
|
{
|
|
pr_token_type = tt_immediate;
|
|
PR_LexNumber();
|
|
return;
|
|
}
|
|
if ( (c == '.'&&pr_file_p[1] >='0' && pr_file_p[1] <= '9') || (c >= '0' && c <= '9') || ( c=='-' && pr_file_p[1]>='0' && pr_file_p[1] <='9') )
|
|
{
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = PR_LexFloat ();
|
|
return;
|
|
}
|
|
|
|
if (c == '#' && !(pr_file_p[1]=='-' || (pr_file_p[1]>='0' && pr_file_p[1] <='9')))
|
|
{
|
|
// hash and not number
|
|
pr_file_p++;
|
|
if (!PR_CheakCompConst())
|
|
{
|
|
if (!PR_SimpleGetToken()) com.strcpy(pr_token, "unknown");
|
|
PR_ParseError(ERR_CONSTANTNOTDEFINED, "Explicit precompiler usage when not defined %s", pr_token);
|
|
}
|
|
else if (pr_token_type == tt_eof) PR_Lex();
|
|
return;
|
|
}
|
|
|
|
if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' )
|
|
{
|
|
if (!PR_CheakCompConst()) PR_LexName (); // look for a macro.
|
|
else if (pr_token_type == tt_eof) goto end_of_file;
|
|
return;
|
|
}
|
|
if (c == '$')
|
|
{
|
|
PR_LexGrab ();
|
|
return;
|
|
}
|
|
|
|
// parse symbol strings until a non-symbol is found
|
|
PR_LexPunctuation ();
|
|
return;
|
|
|
|
end_of_file:
|
|
if (PR_UnInclude())
|
|
{
|
|
PR_Lex();
|
|
return;
|
|
}
|
|
pr_token_type = tt_eof;
|
|
}
|
|
|
|
void PR_ParsePrintDef (int type, def_t *def)
|
|
{
|
|
if(pr_warning[type]) return;
|
|
if(def->s_file) PR_Message ("%s:%i: '%s' is defined here\n", strings + def->s_file, def->s_line, def->name);
|
|
}
|
|
|
|
/*
|
|
============
|
|
PR_ParseError
|
|
============
|
|
*/
|
|
void PR_ParseError( int errortype, char *error, ... )
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start( argptr, error );
|
|
com.vsnprintf(string, sizeof(string) - 1, error, argptr);
|
|
va_end( argptr );
|
|
|
|
if( errortype == ERR_INTERNAL )
|
|
{
|
|
// because sys_error hide message in non-developer mode
|
|
// but in-game engine replaced Sys_Error with Host_Error and
|
|
// we can use it here
|
|
if( host_instance == HOST_NORMAL || host_instance == HOST_DEDICATED )
|
|
{
|
|
PR_Message( "^3Error:^7 %s\n", string );
|
|
prvm_state = comp_error; // abort compilation
|
|
longjmp( pr_int_error, 1 );
|
|
}
|
|
else Sys_Break( "internal error: %s\n", string );
|
|
}
|
|
else
|
|
{
|
|
PR_Message("%s:%i: error: %s\n", strings + s_file, pr_source_line, string);
|
|
longjmp( pr_parse_abort, 1 );
|
|
}
|
|
}
|
|
|
|
void PR_ParseErrorPrintDef( int errortype, def_t *def, char *error, ... )
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start (argptr,error);
|
|
com.vsnprintf (string, sizeof(string)-1, error, argptr);
|
|
va_end (argptr);
|
|
|
|
PR_Message ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string);
|
|
|
|
PR_ParsePrintDef(WARN_ERROR, def);
|
|
longjmp (pr_parse_abort, 1);
|
|
}
|
|
|
|
/*
|
|
============
|
|
PR_ParseWarning
|
|
============
|
|
*/
|
|
void PR_ParseWarning (int type, char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
if (type < ERR_PARSEERRORS && pr_warning[type]) return;
|
|
|
|
va_start (argptr,error);
|
|
com.vsnprintf (string,sizeof(string) - 1, error,argptr);
|
|
va_end (argptr);
|
|
|
|
if(type == ERR_INTERNAL)
|
|
{
|
|
// instead of sys error
|
|
pr_total_error_count++;
|
|
com.error( "internal error C%i: %s\n", type, string );
|
|
}
|
|
else if (type > ERR_INTERNAL)
|
|
{
|
|
PR_Message("%s:%i: error C%i: %s\n", strings + s_file, pr_source_line, type, string);
|
|
pr_total_error_count++;
|
|
pr_error_count++;
|
|
}
|
|
else
|
|
{
|
|
PR_Message("%s:%i: warning C%i: %s\n", strings + s_file, pr_source_line, type, string);
|
|
pr_warning_count++;
|
|
}
|
|
}
|
|
|
|
void PR_Warning (int type, char *file, int line, char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
if (pr_warning[type]) return;
|
|
|
|
va_start (argptr,error);
|
|
com.vsnprintf (string,sizeof(string) - 1, error,argptr);
|
|
va_end (argptr);
|
|
|
|
if (file) PR_Message ("%s(%i) : warning C%i: %s\n", file, line, type, string);
|
|
else PR_Message ("warning C%i: %s\n", type, string);
|
|
pr_warning_count++;
|
|
}
|
|
|
|
void PR_Message( char *message, ... )
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start (argptr, message);
|
|
com.vsnprintf (string, sizeof(string) - 1, message, argptr);
|
|
va_end (argptr);
|
|
|
|
com.print( string );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
PR_Expect
|
|
|
|
Issues an error if the current token isn't equal to string
|
|
Gets the next token
|
|
=============
|
|
*/
|
|
void PR_Expect (char *string)
|
|
{
|
|
if(STRCMP(string, pr_token))
|
|
PR_ParseError (ERR_EXPECTED, "expected %s, found %s",string, pr_token);
|
|
PR_Lex ();
|
|
}
|
|
|
|
/*
|
|
=============
|
|
PR_Check
|
|
|
|
Returns true and gets the next token if the current token equals string
|
|
Returns false and does nothing otherwise
|
|
=============
|
|
*/
|
|
bool PR_CheckToken (char *string)
|
|
{
|
|
if (STRCMP (string, pr_token))
|
|
return false;
|
|
|
|
PR_Lex ();
|
|
return true;
|
|
}
|
|
|
|
bool PR_CheckName(char *string)
|
|
{
|
|
if (STRCMP(string, pr_token))
|
|
return false;
|
|
|
|
PR_Lex ();
|
|
return true;
|
|
}
|
|
|
|
bool PR_CheckForKeyword( int keyword )
|
|
{
|
|
if(!PR_MatchKeyword( keyword ))
|
|
return false;
|
|
|
|
PR_Lex ();
|
|
return true;
|
|
}
|
|
|
|
bool PR_MatchKeyword( int keyword )
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < NUM_KEYWORDS; i++ )
|
|
{
|
|
if(pr_keywords[i].number == keyword)
|
|
{
|
|
// not keyword for current version
|
|
if(!STRCMP(pr_token, pr_keywords[i].name)) return true;
|
|
if(com.strlen(pr_keywords[i].alias) && !STRCMP(pr_token, pr_keywords[i].alias))
|
|
return true; // use alias
|
|
break; // match found
|
|
}
|
|
}
|
|
|
|
// this keyword is present, but not defined
|
|
// just in case
|
|
return false;
|
|
}
|
|
|
|
bool PR_KeywordEnabled( int keyword )
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < NUM_KEYWORDS; i++ )
|
|
{
|
|
// found it
|
|
if(pr_keywords[i].number == keyword)
|
|
return true;
|
|
}
|
|
|
|
// not exist ?
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
============
|
|
PR_ParseName
|
|
|
|
Checks to see if the current token is a valid name
|
|
============
|
|
*/
|
|
char *PR_ParseName (void)
|
|
{
|
|
static char ident[MAX_NAME];
|
|
char *ret;
|
|
|
|
if (pr_token_type != tt_name) PR_ParseError (ERR_NOTANAME, "\"%s\" - not a name", pr_token);
|
|
if (com.strlen(pr_token) >= MAX_NAME-1) PR_ParseError (ERR_NAMETOOLONG, "name too long");
|
|
|
|
com.strcpy (ident, pr_token);
|
|
PR_Lex ();
|
|
|
|
ret = Qalloc(com.strlen(ident) + 1);
|
|
com.strcpy(ret, ident);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
============
|
|
PR_FindType
|
|
|
|
Returns a preexisting complex type that matches the parm, or allocates
|
|
a new one and copies it out.
|
|
============
|
|
*/
|
|
int typecmp(type_t *a, type_t *b)
|
|
{
|
|
if (a == b) return 0;
|
|
if (!a || !b) return 1; // different (^ and not both null)
|
|
|
|
if (a->type != b->type) return 1;
|
|
if (a->num_parms != b->num_parms) return 1;
|
|
if (a->size != b->size) return 1;
|
|
if (typecmp(a->aux_type, b->aux_type)) return 1;
|
|
|
|
if (a->param || b->param)
|
|
{
|
|
a = a->param;
|
|
b = b->param;
|
|
|
|
while(a || b)
|
|
{
|
|
if(typecmp(a, b)) return 1;
|
|
|
|
a = a->next;
|
|
b = b->next;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
type_t *PR_DuplicateType(type_t *in)
|
|
{
|
|
type_t *out, *op, *ip;
|
|
|
|
if (!in) return NULL;
|
|
out = PR_NewType(in->name, in->type);
|
|
out->aux_type = PR_DuplicateType(in->aux_type);
|
|
out->param = PR_DuplicateType(in->param);
|
|
ip = in->param;
|
|
op = NULL;
|
|
|
|
while(ip)
|
|
{
|
|
if (!op) out->param = op = PR_DuplicateType(ip);
|
|
else op = (op->next = PR_DuplicateType(ip));
|
|
ip = ip->next;
|
|
}
|
|
|
|
out->size = in->size;
|
|
out->num_parms = in->num_parms;
|
|
out->ofs = in->ofs;
|
|
out->name = in->name;
|
|
out->parentclass = in->parentclass;
|
|
|
|
return out;
|
|
}
|
|
|
|
char *TypeName(type_t *type)
|
|
{
|
|
static char buffer[2][512];
|
|
static int op;
|
|
char *ret;
|
|
|
|
op++;
|
|
ret = buffer[op&1];
|
|
|
|
if (type->type == ev_field)
|
|
{
|
|
type = type->aux_type;
|
|
*ret++ = '.';
|
|
}
|
|
*ret = 0;
|
|
|
|
if (type->type == ev_function)
|
|
{
|
|
com.strcat(ret, type->aux_type->name);
|
|
com.strcat(ret, " (");
|
|
type = type->param;
|
|
|
|
while(type)
|
|
{
|
|
com.strcat(ret, type->name);
|
|
type = type->next;
|
|
if (type) com.strcat(ret, ", ");
|
|
}
|
|
com.strcat(ret, ")");
|
|
}
|
|
else if (type->type == ev_entity && type->parentclass)
|
|
{
|
|
ret = buffer[op&1];
|
|
*ret = 0;
|
|
com.strcat(ret, "class ");
|
|
com.strcat(ret, type->name);
|
|
}
|
|
else com.strcpy(ret, type->name);
|
|
|
|
return buffer[op&1];
|
|
}
|
|
|
|
type_t *PR_FindType (type_t *type)
|
|
{
|
|
int t;
|
|
for (t = 0; t < numtypeinfos; t++)
|
|
{
|
|
if (typecmp(&qcc_typeinfo[t], type))
|
|
continue;
|
|
return &qcc_typeinfo[t];
|
|
}
|
|
|
|
PR_ParseError(ERR_INTERNAL, "Error with type");
|
|
return type;
|
|
}
|
|
|
|
type_t *TypeForName(char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < numtypeinfos; i++)
|
|
{
|
|
if (!STRCMP(qcc_typeinfo[i].name, name))
|
|
return &qcc_typeinfo[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
============
|
|
PR_SkipToSemicolon
|
|
|
|
For error recovery, also pops out of nested braces
|
|
============
|
|
*/
|
|
void PR_SkipToSemicolon (void)
|
|
{
|
|
do
|
|
{
|
|
if (!pr_bracelevel && PR_CheckToken (";"))
|
|
return;
|
|
PR_Lex ();
|
|
} while (pr_token_type != tt_eof);
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
PR_ParseType
|
|
|
|
Parses a variable type, including field and functions types
|
|
============
|
|
*/
|
|
type_t *PR_ParseFunctionType (int newtype, type_t *returntype)
|
|
{
|
|
type_t *ftype, *ptype, *nptype;
|
|
char *name;
|
|
int definenames = !recursivefunctiontype;
|
|
|
|
recursivefunctiontype++;
|
|
ftype = PR_NewType(type_function->name, ev_function);
|
|
|
|
ftype->aux_type = returntype; // return type
|
|
ftype->num_parms = 0;
|
|
ptype = NULL;
|
|
|
|
if (!PR_CheckToken (")"))
|
|
{
|
|
if (PR_CheckToken ("...")) ftype->num_parms = -1; // variable args
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (ftype->num_parms>=MAX_PARMS+MAX_PARMS_EXTRA)
|
|
PR_ParseError(ERR_TOOMANYTOTALPARAMETERS, "Too many parameters. Sorry. (limit is %i)\n", MAX_PARMS+MAX_PARMS_EXTRA);
|
|
|
|
if (PR_CheckToken ("..."))
|
|
{
|
|
ftype->num_parms = (ftype->num_parms * -1) - 1;
|
|
break;
|
|
}
|
|
nptype = PR_ParseType(true);
|
|
|
|
if (nptype->type == ev_void) break;
|
|
if (!ptype)
|
|
{
|
|
ptype = nptype;
|
|
ftype->param = ptype;
|
|
}
|
|
else
|
|
{
|
|
ptype->next = nptype;
|
|
ptype = ptype->next;
|
|
}
|
|
if (STRCMP(pr_token, ",") && STRCMP(pr_token, ")"))
|
|
{
|
|
name = PR_ParseName ();
|
|
if (definenames) com.strcpy (pr_parm_names[ftype->num_parms], name);
|
|
}
|
|
else if (definenames) com.strcpy (pr_parm_names[ftype->num_parms], "");
|
|
ftype->num_parms++;
|
|
} while (PR_CheckToken (","));
|
|
}
|
|
PR_Expect (")");
|
|
}
|
|
|
|
recursivefunctiontype--;
|
|
if (newtype) return ftype;
|
|
return PR_FindType (ftype);
|
|
}
|
|
|
|
type_t *PR_PointerType (type_t *pointsto)
|
|
{
|
|
type_t *ptype;
|
|
char name[128];
|
|
|
|
com.sprintf(name, "*%s", pointsto->name);
|
|
ptype = PR_NewType(name, ev_pointer);
|
|
ptype->aux_type = pointsto;
|
|
|
|
return PR_FindType (ptype);
|
|
}
|
|
|
|
type_t *PR_FieldType (type_t *pointsto)
|
|
{
|
|
type_t *ptype;
|
|
char name[128];
|
|
|
|
com.sprintf(name, "FIELD TYPE(%s)", pointsto->name);
|
|
ptype = PR_NewType(name, ev_field);
|
|
ptype->aux_type = pointsto;
|
|
ptype->size = ptype->aux_type->size;
|
|
|
|
return PR_FindType (ptype);
|
|
}
|
|
|
|
type_t *PR_ParseType (int newtype)
|
|
{
|
|
type_t *newparm;
|
|
type_t *newt;
|
|
type_t *type;
|
|
char *name;
|
|
int i;
|
|
|
|
if (PR_CheckToken ("..")) //so we don't end up with the user specifying '. .vector blah'
|
|
{
|
|
newt = PR_NewType("FIELD TYPE", ev_field);
|
|
newt->aux_type = PR_ParseType (false);
|
|
newt->size = newt->aux_type->size;
|
|
newt = PR_FindType (newt);
|
|
|
|
type = PR_NewType("FIELD TYPE", ev_field);
|
|
type->aux_type = newt;
|
|
type->size = type->aux_type->size;
|
|
|
|
if (newtype) return type;
|
|
return PR_FindType (type);
|
|
}
|
|
if (PR_CheckToken ("."))
|
|
{
|
|
newt = PR_NewType("FIELD TYPE", ev_field);
|
|
newt->aux_type = PR_ParseType (false);
|
|
newt->size = newt->aux_type->size;
|
|
|
|
if (newtype) return newt;
|
|
return PR_FindType (newt);
|
|
}
|
|
|
|
name = PR_CheakCompConstString(pr_token);
|
|
|
|
if (PR_CheckForKeyword( KEYWORD_CLASS ))
|
|
{
|
|
type_t *fieldtype;
|
|
char membername[2048];
|
|
char *classname = PR_ParseName();
|
|
|
|
newt = PR_NewType(classname, ev_entity);
|
|
newt->size = type_entity->size;
|
|
type = NULL;
|
|
|
|
if (PR_CheckToken(":"))
|
|
{
|
|
char *parentname = PR_ParseName();
|
|
newt->parentclass = TypeForName(parentname);
|
|
if (!newt->parentclass)
|
|
PR_ParseError(ERR_NOTANAME, "Parent class %s was not defined", parentname);
|
|
}
|
|
else newt->parentclass = type_entity;
|
|
|
|
PR_Expect("{");
|
|
if (PR_CheckToken(",")) PR_ParseError(ERR_NOTANAME, "member missing name");
|
|
|
|
while (!PR_CheckToken("}"))
|
|
{
|
|
newparm = PR_ParseType(true);
|
|
|
|
// we wouldn't be able to handle it.
|
|
if (newparm->type == ev_struct || newparm->type == ev_union)
|
|
PR_ParseError(ERR_INTERNAL, "Struct or union in class %s", classname);
|
|
|
|
if (!PR_CheckToken(";"))
|
|
{
|
|
newparm->name = PR_CopyString(pr_token, opt_noduplicatestrings ) + strings;
|
|
PR_Lex();
|
|
if (PR_CheckToken("["))
|
|
{
|
|
type->next->size*=com.atoi(pr_token);
|
|
PR_Lex();
|
|
PR_Expect("]");
|
|
}
|
|
PR_CheckToken(";");
|
|
}
|
|
else newparm->name = PR_CopyString("", opt_noduplicatestrings )+strings;
|
|
|
|
com.sprintf(membername, "%s::"MEMBERFIELDNAME, classname, newparm->name);
|
|
fieldtype = PR_NewType(newparm->name, ev_field);
|
|
fieldtype->aux_type = newparm;
|
|
fieldtype->size = newparm->size;
|
|
PR_GetDef(fieldtype, membername, pr_scope, 2, 1);
|
|
|
|
newparm->ofs = 0; // newt->size;
|
|
newt->num_parms++;
|
|
|
|
if (type) type->next = newparm;
|
|
else newt->param = newparm;
|
|
type = newparm;
|
|
}
|
|
PR_Expect(";");
|
|
return NULL;
|
|
}
|
|
if (PR_CheckForKeyword( KEYWORD_STRUCT ))
|
|
{
|
|
newt = PR_NewType("struct", ev_struct);
|
|
newt->size=0;
|
|
PR_Expect("{");
|
|
|
|
type = NULL;
|
|
if (PR_CheckToken(",")) PR_ParseError(ERR_NOTANAME, "element missing name");
|
|
|
|
newparm = NULL;
|
|
while (!PR_CheckToken("}"))
|
|
{
|
|
if (PR_CheckToken(","))
|
|
{
|
|
if (!newparm) PR_ParseError(ERR_NOTANAME, "element missing type");
|
|
newparm = PR_NewType(newparm->name, newparm->type);
|
|
}
|
|
else newparm = PR_ParseType(true);
|
|
|
|
if (!PR_CheckToken(";"))
|
|
{
|
|
newparm->name = PR_CopyString(pr_token, opt_noduplicatestrings )+strings;
|
|
PR_Lex();
|
|
if (PR_CheckToken("["))
|
|
{
|
|
newparm->size*=com.atoi(pr_token);
|
|
PR_Lex();
|
|
PR_Expect("]");
|
|
}
|
|
PR_CheckToken(";");
|
|
}
|
|
else newparm->name = PR_CopyString("", opt_noduplicatestrings )+strings;
|
|
newparm->ofs = newt->size;
|
|
newt->size += newparm->size;
|
|
newt->num_parms++;
|
|
|
|
if (type) type->next = newparm;
|
|
else newt->param = newparm;
|
|
type = newparm;
|
|
}
|
|
return newt;
|
|
}
|
|
if(PR_CheckForKeyword( KEYWORD_UNION ))
|
|
{
|
|
newt = PR_NewType("union", ev_union);
|
|
newt->size = 0;
|
|
PR_Expect("{");
|
|
|
|
type = NULL;
|
|
if (PR_CheckToken(",")) PR_ParseError(ERR_NOTANAME, "element missing name");
|
|
newparm = NULL;
|
|
|
|
while (!PR_CheckToken("}"))
|
|
{
|
|
if (PR_CheckToken(","))
|
|
{
|
|
if (!newparm) PR_ParseError(ERR_NOTANAME, "element missing type");
|
|
newparm = PR_NewType(newparm->name, newparm->type);
|
|
}
|
|
else newparm = PR_ParseType(true);
|
|
|
|
if (PR_CheckToken(";")) newparm->name = PR_CopyString("", opt_noduplicatestrings )+strings;
|
|
else
|
|
{
|
|
newparm->name = PR_CopyString(pr_token, opt_noduplicatestrings )+strings;
|
|
PR_Lex();
|
|
PR_Expect(";");
|
|
}
|
|
|
|
newparm->ofs = 0;
|
|
if (newparm->size > newt->size) newt->size = newparm->size;
|
|
newt->num_parms++;
|
|
|
|
if (type) type->next = newparm;
|
|
else newt->param = newparm;
|
|
type = newparm;
|
|
}
|
|
return newt;
|
|
}
|
|
|
|
type = NULL;
|
|
for (i = 0; i < numtypeinfos; i++)
|
|
{
|
|
if (!STRCMP(qcc_typeinfo[i].name, name))
|
|
{
|
|
type = &qcc_typeinfo[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == numtypeinfos)
|
|
{
|
|
if (!*name) return NULL;
|
|
PR_ParseError (ERR_NOTATYPE, "\"%s\" is not a type", name);
|
|
type = type_float; // shut up compiler warning
|
|
}
|
|
PR_Lex ();
|
|
|
|
// this is followed by parameters. Must be a function.
|
|
if (PR_CheckToken ("(")) return PR_ParseFunctionType(newtype, type);
|
|
else
|
|
{
|
|
if (newtype) type = PR_DuplicateType(type);
|
|
return type;
|
|
}
|
|
} |