mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-25 13:21:36 +00:00
6682 lines
165 KiB
C
6682 lines
165 KiB
C
#if !defined(MINIMAL) && !defined(OMIT_QCC)
|
|
|
|
#include "qcc.h"
|
|
#include "time.h"
|
|
|
|
#define MEMBERFIELDNAME "__m%s"
|
|
|
|
#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1,s2)) //saves about 2-6 out of 120 - expansion of idea from fastqcc
|
|
|
|
void QCC_PR_PreProcessor_Define(pbool append);
|
|
pbool QCC_PR_UndefineName(const char *name);
|
|
const char *QCC_PR_CheckCompConstString(const char *def);
|
|
CompilerConstant_t *QCC_PR_CheckCompConstDefined(const char *def);
|
|
int QCC_PR_CheckCompConst(void);
|
|
pbool QCC_Include(const char *filename);
|
|
void QCC_FreeDef(QCC_def_t *def);
|
|
void QCC_PR_LexComment(char **comment);
|
|
|
|
extern pbool destfile_explicit;
|
|
extern char destfile[1024];
|
|
//static const QCC_sref_t nullsref = {0};
|
|
|
|
#define MAXINCLUDEDIRS 8
|
|
char qccincludedir[MAXINCLUDEDIRS][256]; //the -src path, for #includes
|
|
struct qccincludeonced_s
|
|
{
|
|
struct qccincludeonced_s *next;
|
|
char name[1];
|
|
} *qccincludeonced; //the -src path, for #includes
|
|
|
|
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_precomment;
|
|
char pr_token[8192];
|
|
token_type_t pr_token_type;
|
|
int pr_token_line;
|
|
int pr_token_line_last;
|
|
QCC_type_t *pr_immediate_type;
|
|
QCC_eval_t pr_immediate;
|
|
|
|
char pr_immediate_string[8192];
|
|
size_t pr_immediate_strlen;
|
|
|
|
int pr_error_count;
|
|
int pr_warning_count;
|
|
|
|
extern pbool expandedemptymacro;
|
|
|
|
//really these should not be in here
|
|
extern unsigned int locals_end, locals_start;
|
|
extern QCC_type_t *pr_classtype;
|
|
QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type, pbool dowrap);
|
|
QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto);
|
|
|
|
static void Q_strlcpy(char *dest, const char *src, int sizeofdest)
|
|
{
|
|
if (sizeofdest)
|
|
{
|
|
int slen = strlen(src);
|
|
slen = min((sizeofdest-1), slen);
|
|
memcpy(dest, src, slen);
|
|
dest[slen] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
char *pr_punctuation[] =
|
|
// longer symbols must be before a shorter partial match
|
|
{"&&", "||", "<=>", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "(+)", "(-)", "|=", "&~=", "&=", "++", "--", "->", "^=", "::", ";", ",", "!", "*^", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "><", "<<=", "<<", "<", ">>=", ">>", ">" , "?", "#" , "@", "&" , "|", "%", "^^", "^", "~", ":", NULL};
|
|
|
|
char *pr_punctuationremap[] = //a nice bit of evilness.
|
|
//(+) -> |=
|
|
//-> -> .
|
|
//(-) -> &~=
|
|
{"&&", "||", "<=>", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "|=", "&~=", "|=", "&~=", "&=", "++", "--", ".", "^=", "::", ";", ",", "!", "*^", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "><", "<<=", "<<", "<", ">>=", ">>", ">" , "?", "#" , "@", "&" , "|", "%", "^^", "^", "~", ":", NULL};
|
|
|
|
// simple types. function types are dynamically allocated
|
|
QCC_type_t *type_void; //void
|
|
QCC_type_t *type_string; //string
|
|
QCC_type_t *type_float; //float
|
|
QCC_type_t *type_double; //double
|
|
QCC_type_t *type_vector; //vector
|
|
QCC_type_t *type_entity; //entity
|
|
QCC_type_t *type_field; //.void
|
|
QCC_type_t *type_function; //void()
|
|
QCC_type_t *type_floatfunction; //float()
|
|
QCC_type_t *type_pointer; //??? * - careful with this one
|
|
QCC_type_t *type_integer; //int32
|
|
QCC_type_t *type_uint; //uint32
|
|
QCC_type_t *type_int64; //int64
|
|
QCC_type_t *type_uint64; //uint64
|
|
QCC_type_t *type_variant; //__variant
|
|
QCC_type_t *type_invalid; //technically void, but shouldn't really ever be used.
|
|
QCC_type_t *type_floatpointer; //float *
|
|
QCC_type_t *type_intpointer; //int *
|
|
QCC_type_t *type_bint; //int (0 or 1)
|
|
QCC_type_t *type_bfloat; //float (0.0 or 1.0, and never -0.0)
|
|
|
|
QCC_type_t *type_floatfield;// = {ev_field/*, &def_field*/, NULL, &type_float};
|
|
|
|
QCC_def_t def_ret, def_parms[MAX_PARMS];
|
|
|
|
//QCC_def_t *def_for_type[9] = {&def_void, &def_string, &def_float, &def_vector, &def_entity, &def_field, &def_function, &def_pointer, &def_integer};
|
|
|
|
void QCC_PR_LexWhitespace (pbool inhibitpreprocessor);
|
|
|
|
|
|
QCC_type_t *QCC_PR_ParseEnum(pbool flags);
|
|
|
|
//for compiler constants and file includes.
|
|
|
|
qcc_includechunk_t *currentchunk;
|
|
void QCC_PR_CloseProcessor(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAXINCLUDEDIRS; i++)
|
|
*qccincludedir[i] = 0;
|
|
currentchunk = NULL;
|
|
qccincludeonced = NULL;
|
|
}
|
|
void QCC_PR_AddIncludePath(const char *newinc)
|
|
{
|
|
int i;
|
|
|
|
if (!*newinc)
|
|
{
|
|
newinc = ".";
|
|
// QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Invalid include path.");
|
|
// return;
|
|
}
|
|
|
|
for (i = 0; i < MAXINCLUDEDIRS; i++)
|
|
{
|
|
if (!*qccincludedir[i])
|
|
{
|
|
pbool trunc;
|
|
const char *e = newinc + strlen(newinc)-1;
|
|
trunc = !QC_strlcpy(qccincludedir[i], newinc, sizeof(qccincludedir));
|
|
if (*e != '/' && *e != '\\')
|
|
trunc |= !QC_strlcat(qccincludedir[i], "/", sizeof(qccincludedir));
|
|
if (trunc)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Include path too long.");
|
|
*qccincludedir[i] = 0;
|
|
}
|
|
break;
|
|
}
|
|
if (!strcmp(qccincludedir[i], newinc))
|
|
break;
|
|
}
|
|
if (i == MAXINCLUDEDIRS)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Too many include dirs. Ignoring and hoping the stars align.");
|
|
}
|
|
}
|
|
static void QCC_PR_IncludeChunkEx (char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst)
|
|
{
|
|
qcc_includechunk_t *chunk = qccHunkAlloc(sizeof(qcc_includechunk_t));
|
|
chunk->prev = currentchunk;
|
|
currentchunk = chunk;
|
|
|
|
chunk->currentdatapoint = pr_file_p;
|
|
chunk->currentfilename = s_filen;
|
|
chunk->currentlinenumber = pr_source_line;
|
|
chunk->cnst = cnst;
|
|
if( cnst )
|
|
{
|
|
#if 0
|
|
s_filen = cnst->fromfile;
|
|
pr_source_line = cnst->fromline;
|
|
#else
|
|
int b = strlen(s_filen)+1+8+strlen(cnst->name);
|
|
char *p;
|
|
if (b > 128)
|
|
b = 128;
|
|
s_filen = p = qccHunkAlloc(b);
|
|
QC_snprintfz(p, b, "%s:%i:%s", chunk->currentfilename, chunk->currentlinenumber, cnst->name);
|
|
pr_source_line = 1;
|
|
#endif
|
|
cnst->inside++;
|
|
}
|
|
else
|
|
pr_source_line = 1;
|
|
|
|
if (duplicate)
|
|
{
|
|
pr_file_p = qccHunkAlloc(strlen(data)+1);
|
|
strcpy(pr_file_p, data);
|
|
}
|
|
else
|
|
pr_file_p = data;
|
|
chunk->datastart = pr_file_p;
|
|
}
|
|
void QCC_PR_IncludeChunk (char *data, pbool duplicate, char *filename)
|
|
{
|
|
QCC_PR_IncludeChunkEx(data, duplicate, filename, NULL);
|
|
}
|
|
|
|
pbool QCC_PR_UnInclude(void)
|
|
{
|
|
if (!currentchunk)
|
|
return false;
|
|
|
|
if( currentchunk->cnst )
|
|
currentchunk->cnst->inside--;
|
|
|
|
pr_file_p = currentchunk->currentdatapoint;
|
|
pr_source_line = currentchunk->currentlinenumber;
|
|
s_filen = currentchunk->currentfilename;
|
|
|
|
currentchunk = currentchunk->prev;
|
|
|
|
return true;
|
|
}
|
|
|
|
//expresses a relative path relative to an existing FILEname. any directories in base must be / terminated properly.
|
|
void QCC_JoinPaths(char *fullname, size_t fullnamesize, const char *newfile, const char *base)
|
|
{
|
|
char *end;
|
|
|
|
if (*newfile == '/' || *newfile == '\\')
|
|
{ //its an absolute path...
|
|
QC_strlcpy(fullname, newfile, fullnamesize);
|
|
return;
|
|
}
|
|
QC_strlcpy(fullname, base, fullnamesize);
|
|
end = fullname+strlen(fullname);
|
|
while (end > fullname)
|
|
{
|
|
end--;
|
|
if (*end == '/' || *end == '\\')
|
|
{
|
|
end++;
|
|
break;
|
|
}
|
|
}
|
|
QC_strlcpy(end, newfile, fullnamesize - (end-fullname));
|
|
|
|
//FIXME: do we want to convert /segment/../ into just / ?
|
|
//fteqw might insist on it for its filesystem sandboxing.
|
|
//but it breaks symlink weirdness (itself a possible security hole).
|
|
//should probably be a separate function.
|
|
}
|
|
|
|
extern char qccmsourcedir[];
|
|
//also meant to include it.
|
|
void QCC_FindBestInclude(char *newfile, char *currentfile, pbool includetype)
|
|
{
|
|
struct qccincludeonced_s *onced;
|
|
int includepath = 0;
|
|
char fullname[1024];
|
|
|
|
if (!*newfile)
|
|
return;
|
|
|
|
while(1)
|
|
{
|
|
if (includepath)
|
|
{
|
|
if (includepath > MAXINCLUDEDIRS || !*qccincludedir[includepath-1])
|
|
QCC_Error(ERR_COULDNTOPENFILE, "Couldn't open file %s", newfile);
|
|
|
|
currentfile = qccincludedir[includepath-1];
|
|
}
|
|
|
|
QCC_JoinPaths(fullname, sizeof(fullname), newfile, currentfile);
|
|
|
|
{
|
|
extern progfuncs_t *qccprogfuncs;
|
|
if (qccprogfuncs->funcs.parms->FileSize(fullname) == -1)
|
|
{
|
|
includepath++;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
for(onced = qccincludeonced; onced; onced = onced->next)
|
|
{
|
|
if (!strcmp(onced->name, fullname))
|
|
return;
|
|
}
|
|
|
|
if (includetype && verbose >= VERBOSE_PROGRESS)
|
|
{
|
|
if (includetype == 2)
|
|
{
|
|
if (autoprototype)
|
|
externs->Printf("prototyping %s\n", fullname);
|
|
else
|
|
externs->Printf("compiling %s\n", fullname);
|
|
}
|
|
else
|
|
{
|
|
if (autoprototype)
|
|
externs->Printf("prototyping include %s\n", fullname);
|
|
else
|
|
externs->Printf("including %s\n", fullname);
|
|
}
|
|
}
|
|
QCC_Include(fullname);
|
|
}
|
|
|
|
pbool defaultnoref;
|
|
pbool defaultnosave;
|
|
pbool defaultstatic;
|
|
int ForcedCRC;
|
|
float qcc_framerate;
|
|
|
|
int QCC_PR_LexInteger (void);
|
|
void QCC_AddFile (char *filename);
|
|
void QCC_PR_LexString (void);
|
|
pbool QCC_PR_SimpleGetToken (void);
|
|
pbool QCC_PR_SimpleGetString(void);
|
|
|
|
#define PPI_VALUE 0
|
|
#define PPI_NOT 1
|
|
#define PPI_DEFINED 2
|
|
#define PPI_COMPARISON 3
|
|
#define PPI_LOGICAL 4
|
|
#define PPI_TOPLEVEL 5
|
|
static int ParsePrecompilerIf(int level)
|
|
{
|
|
CompilerConstant_t *c;
|
|
int eval = 0;
|
|
// pbool notted = false;
|
|
|
|
//single term end-of-chain
|
|
if (level == PPI_VALUE)
|
|
{
|
|
/*skip whitespace*/
|
|
while (*pr_file_p && qcc_iswhite(*pr_file_p) && *pr_file_p != '\n')
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
|
|
if (*pr_file_p == '(')
|
|
{ //try brackets
|
|
pr_file_p++;
|
|
eval = ParsePrecompilerIf(PPI_TOPLEVEL);
|
|
while (*pr_file_p == ' ' || *pr_file_p == '\t')
|
|
pr_file_p++;
|
|
if (*pr_file_p != ')')
|
|
QCC_PR_ParseError(ERR_EXPECTED, "unclosed bracket condition\n");
|
|
pr_file_p++;
|
|
}
|
|
else if (*pr_file_p == '!')
|
|
{ //try brackets
|
|
pr_file_p++;
|
|
eval = !ParsePrecompilerIf(PPI_NOT);
|
|
}
|
|
else
|
|
{ //simple token...
|
|
if (!strncmp(pr_file_p, "defined", 7))
|
|
{
|
|
pr_file_p+=7;
|
|
while (*pr_file_p == ' ' || *pr_file_p == '\t')
|
|
pr_file_p++;
|
|
if (*pr_file_p != '(')
|
|
{
|
|
eval = false;
|
|
QCC_PR_ParseError(ERR_EXPECTED, "no opening bracket after defined\n");
|
|
}
|
|
else
|
|
{
|
|
pr_file_p++;
|
|
|
|
QCC_PR_SimpleGetToken();
|
|
eval = !!QCC_PR_CheckCompConstDefined(pr_token);
|
|
|
|
while (*pr_file_p == ' ' || *pr_file_p == '\t')
|
|
pr_file_p++;
|
|
if (*pr_file_p != ')')
|
|
QCC_PR_ParseError(ERR_EXPECTED, "unclosed defined condition\n");
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!QCC_PR_SimpleGetToken())
|
|
QCC_PR_ParseError(ERR_EXPECTED, "unexpected end-of-line\n");
|
|
c = QCC_PR_CheckCompConstDefined(pr_token);
|
|
if (!c)
|
|
eval = atoi(pr_token);
|
|
else
|
|
eval = atoi(c->value);
|
|
}
|
|
}
|
|
return eval;
|
|
}
|
|
|
|
eval = ParsePrecompilerIf(level-1);
|
|
|
|
while (*pr_file_p && qcc_iswhite(*pr_file_p) && *pr_file_p != '\n')
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
|
|
switch(level)
|
|
{
|
|
case PPI_LOGICAL:
|
|
if (!strncmp(pr_file_p, "||", 2))
|
|
{
|
|
pr_file_p+=2;
|
|
eval = ParsePrecompilerIf(level)||eval;
|
|
}
|
|
else if (!strncmp(pr_file_p, "&&", 2))
|
|
{
|
|
pr_file_p+=2;
|
|
eval = ParsePrecompilerIf(level)&&eval;
|
|
}
|
|
break;
|
|
case PPI_COMPARISON:
|
|
if (!strncmp(pr_file_p, "<=", 2))
|
|
{
|
|
pr_file_p+=2;
|
|
eval = eval <= ParsePrecompilerIf(level);
|
|
}
|
|
else if (!strncmp(pr_file_p, ">=", 2))
|
|
{
|
|
pr_file_p += 2;
|
|
eval = eval >= ParsePrecompilerIf(level);
|
|
}
|
|
else if (!strncmp(pr_file_p, "<", 1))
|
|
{
|
|
pr_file_p += 1;
|
|
eval = eval < ParsePrecompilerIf(level);
|
|
}
|
|
else if (!strncmp(pr_file_p, ">", 1))
|
|
{
|
|
pr_file_p += 1;
|
|
eval = eval > ParsePrecompilerIf(level);
|
|
}
|
|
else if (!strncmp(pr_file_p, "!=", 2))
|
|
{
|
|
pr_file_p += 2;
|
|
eval = eval != ParsePrecompilerIf(level);
|
|
}
|
|
else if (!strncmp(pr_file_p, "==", 2))
|
|
{
|
|
pr_file_p += 2;
|
|
eval = eval == ParsePrecompilerIf(level);
|
|
}
|
|
break;
|
|
}
|
|
return eval;
|
|
}
|
|
|
|
struct deflist_s
|
|
{
|
|
char *buffer;
|
|
size_t length;
|
|
size_t buffersize;
|
|
};
|
|
static void QCC_PR_GetDefinesListEnumerate(void *vctx, void *data)
|
|
{
|
|
struct deflist_s *ctx = vctx;
|
|
CompilerConstant_t *def = data;
|
|
char term[8192];
|
|
size_t termsize;
|
|
pbool success = true;
|
|
|
|
QC_snprintfz(term, sizeof(term), "\n%s", def->name);
|
|
if (def->numparams >= 0)
|
|
{
|
|
int i;
|
|
success &= QC_strlcat(term, "(", sizeof(term));
|
|
for (i = 0; i < def->numparams; i++)
|
|
{
|
|
if (i)
|
|
success &= QC_strlcat(term, ",", sizeof(term));
|
|
success &= QC_strlcat(term, def->params[i], sizeof(term));
|
|
}
|
|
success &= QC_strlcat(term, ")", sizeof(term));
|
|
}
|
|
if (def->value && *def->value)
|
|
{
|
|
char *o, *i;
|
|
success &= QC_strlcat(term, "=", sizeof(term));
|
|
|
|
//annoying logic to skip whitespace... hopefully it won't fuck stuff up too much.
|
|
for (o = term+strlen(term), i = def->value; o < term + sizeof(term)-1 && *i; )
|
|
{
|
|
if (*i == ' ' || *i == '\t' || *i == '\n' || *i == '\r')
|
|
i++;
|
|
else
|
|
*o++ = *i++;
|
|
}
|
|
*o = 0;
|
|
}
|
|
if (!success) //this define was too long. don't show truncated stuff.
|
|
return;
|
|
|
|
termsize = strlen(term);
|
|
if (ctx->length + termsize+1 > ctx->buffersize)
|
|
{
|
|
ctx->buffersize = (ctx->length + termsize+1)*2;
|
|
ctx->buffer = realloc(ctx->buffer, ctx->buffersize);
|
|
}
|
|
memcpy(ctx->buffer+ctx->length, term, termsize);
|
|
ctx->length += termsize;
|
|
ctx->buffer[ctx->length] = 0;
|
|
}
|
|
char *QCC_PR_GetDefinesList(void)
|
|
{
|
|
struct deflist_s ctx = {NULL};
|
|
Hash_Enumerate(&compconstantstable, QCC_PR_GetDefinesListEnumerate, &ctx);
|
|
return ctx.buffer;
|
|
}
|
|
|
|
//returns true if it was white/comments only. false if there was actual text that was skipped.
|
|
static void QCC_PR_SkipToEndOfLine(pbool errorifnonwhite)
|
|
{
|
|
pbool handleccomments = true;
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
if (*pr_file_p == '/' && pr_file_p[1] == '*' && handleccomments)
|
|
{
|
|
pr_file_p += 2;
|
|
while(*pr_file_p)
|
|
{
|
|
if (*pr_file_p == '*' && pr_file_p[1] == '/')
|
|
{
|
|
pr_file_p+=2;
|
|
break;
|
|
}
|
|
if (*pr_file_p == '\n')
|
|
pr_source_line++;
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
else if (*pr_file_p == '/' && pr_file_p[1] == '/' && handleccomments)
|
|
{
|
|
handleccomments = false;
|
|
pr_file_p += 2;
|
|
/*while(*pr_file_p)
|
|
{
|
|
if (*pr_file_p == '\n')
|
|
break;
|
|
pr_file_p++;
|
|
}*/
|
|
}
|
|
else if (*pr_file_p == '\\' && pr_file_p[1] == '\r' && pr_file_p[2] == '\n')
|
|
{ /*windows endings*/
|
|
pr_file_p+=3;
|
|
pr_source_line++;
|
|
}
|
|
else if (*pr_file_p == '\"' && handleccomments)
|
|
{
|
|
if (errorifnonwhite)
|
|
{
|
|
errorifnonwhite = false;
|
|
QCC_PR_ParseWarning (ERR_UNKNOWNPUCTUATION, "unexpected tokens at end of line");
|
|
}
|
|
pr_file_p++;
|
|
while (*pr_file_p)
|
|
{
|
|
if (*pr_file_p == '\n')
|
|
break; //this text is junk/ignored, so ignore the obvious error here.
|
|
else if (*pr_file_p == '\"')
|
|
{
|
|
pr_file_p++;
|
|
break;
|
|
}
|
|
else if (*pr_file_p == '\\' && pr_file_p[1] == '\"')
|
|
pr_file_p+=2; //don't trip on "\"/*"
|
|
else if (*pr_file_p == '\\' && pr_file_p[1] == '\\')
|
|
pr_file_p+=2; //don't trip on "\\"//"foo
|
|
else
|
|
pr_file_p++;
|
|
//any other \ should be part of the actual string, which we don't care about here
|
|
}
|
|
}
|
|
else if (*pr_file_p == '\\' && pr_file_p[1] == '\n')
|
|
{ /*linux endings*/
|
|
pr_file_p+=2;
|
|
|
|
pr_source_line++;
|
|
}
|
|
else
|
|
{
|
|
if (errorifnonwhite && handleccomments && !qcc_iswhite(*pr_file_p))
|
|
{
|
|
errorifnonwhite = false;
|
|
QCC_PR_ParseWarning(ERR_UNKNOWNPUCTUATION, "unexpected tokens at end of line");
|
|
}
|
|
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//if hadtrue, then we allow elses, otherwise we skip them.
|
|
static pbool QCC_PR_FalsePreProcessorIf(pbool hadtrue, int originalline)
|
|
{
|
|
int eval;
|
|
int level = 1;
|
|
while (1)
|
|
{
|
|
while(*pr_file_p && (*pr_file_p==' ' || *pr_file_p == '\t'))
|
|
pr_file_p++;
|
|
|
|
if (!*pr_file_p)
|
|
{
|
|
pr_source_line = originalline;
|
|
QCC_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 (!strncmp(pr_file_p, "endif", 5))
|
|
level--;
|
|
if (!strncmp(pr_file_p, "if", 2))
|
|
level++;
|
|
if (!hadtrue && !strncmp(pr_file_p, "else", 4) && level == 1)
|
|
{
|
|
pr_file_p+=4;
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
return true;
|
|
}
|
|
if (!hadtrue && !strncmp(pr_file_p, "elif", 4) && level == 1)
|
|
{
|
|
// QCC_PR_ParseError(ERR_UNKNOWNPUCTUATION, "#elif not supported\n");
|
|
|
|
pr_file_p += 4;
|
|
if (!strncmp(pr_file_p, "def", 3))
|
|
{
|
|
eval = 1;
|
|
pr_file_p += 3;
|
|
}
|
|
else if (!strncmp(pr_file_p, "ndef", 4))
|
|
{
|
|
eval = 0;
|
|
pr_file_p += 4;
|
|
}
|
|
else
|
|
eval = 2;
|
|
if (*pr_file_p != ' ' && *pr_file_p != '\t')
|
|
QCC_PR_ParseError(ERR_UNKNOWNPUCTUATION, "malformed #elif\n");
|
|
if (eval == 2)
|
|
eval = ParsePrecompilerIf(PPI_TOPLEVEL);
|
|
else
|
|
{
|
|
QCC_PR_SimpleGetToken ();
|
|
if (eval)
|
|
eval = !!QCC_PR_CheckCompConstDefined(pr_token);
|
|
else
|
|
eval = !QCC_PR_CheckCompConstDefined(pr_token);
|
|
}
|
|
if (eval)
|
|
{
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
QCC_PR_SkipToEndOfLine(false);
|
|
|
|
if (level <= 0)
|
|
return false;
|
|
pr_file_p++; //next line
|
|
pr_source_line++;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static void QCC_PR_PackagerMessage(void *userctx, char *message, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start (argptr,message);
|
|
QC_vsnprintf (string,sizeof(string)-1,message,argptr);
|
|
va_end (argptr);
|
|
|
|
externs->Printf ("%s", string);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
==============
|
|
QCC_PR_Precompiler
|
|
==============
|
|
|
|
Runs precompiler stage
|
|
*/
|
|
static pbool QCC_PR_Precompiler(void)
|
|
{
|
|
char msg[1024];
|
|
int ifmode;
|
|
int a;
|
|
static int ifs = 0;
|
|
pbool eval = false;
|
|
|
|
if (*pr_file_p == '#')
|
|
{
|
|
char *directive;
|
|
for (directive = pr_file_p+1; *directive; directive++) //so # define works
|
|
{
|
|
if (*directive == '\r' || *directive == '\n')
|
|
QCC_PR_ParseError(ERR_UNKNOWNPUCTUATION, "Hanging # with no directive\n");
|
|
if (*directive > ' ')
|
|
break;
|
|
}
|
|
if (!strncmp(directive, "define", 6))
|
|
{
|
|
pr_file_p = directive;
|
|
QCC_PR_PreProcessor_Define(false);
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
}
|
|
else if (!strncmp(directive, "append", 6))
|
|
{
|
|
pr_file_p = directive;
|
|
QCC_PR_PreProcessor_Define(true);
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
}
|
|
else if (!strncmp(directive, "undef", 5))
|
|
{
|
|
pr_file_p = directive+5;
|
|
while(qcc_iswhitesameline(*pr_file_p))
|
|
pr_file_p++;
|
|
|
|
QCC_PR_SimpleGetToken ();
|
|
QCC_PR_UndefineName(pr_token);
|
|
|
|
// QCC_PR_ConditionCompilation();
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
}
|
|
else if (!strncmp(directive, "if", 2))
|
|
{
|
|
int originalline = pr_source_line;
|
|
pr_file_p = directive+2;
|
|
if (!strncmp(pr_file_p, "def", 3))
|
|
{
|
|
ifmode = 0;
|
|
pr_file_p+=3;
|
|
}
|
|
else if (!strncmp(pr_file_p, "ndef", 4))
|
|
{
|
|
ifmode = 1;
|
|
pr_file_p+=4;
|
|
}
|
|
else
|
|
{
|
|
ifmode = 2;
|
|
pr_file_p+=0;
|
|
//QCC_PR_ParseError("bad \"#if\" type");
|
|
}
|
|
|
|
if (!qcc_iswhite(*pr_file_p))
|
|
{
|
|
pr_file_p = directive;
|
|
QCC_PR_SimpleGetToken ();
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "Unknown pragma \'%s\'", qcc_token);
|
|
}
|
|
else
|
|
{
|
|
if (ifmode == 2)
|
|
{
|
|
eval = ParsePrecompilerIf(PPI_TOPLEVEL);
|
|
}
|
|
else
|
|
{
|
|
QCC_PR_SimpleGetToken ();
|
|
|
|
// if (!STRCMP(pr_token, "COOP_MODE"))
|
|
// eval = false;
|
|
if (QCC_PR_CheckCompConstDefined(pr_token))
|
|
eval = true;
|
|
|
|
if (ifmode == 1)
|
|
eval = eval?false:true;
|
|
}
|
|
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
|
|
if (eval)
|
|
ifs+=1;
|
|
else
|
|
ifs += QCC_PR_FalsePreProcessorIf(false, originalline);
|
|
}
|
|
}
|
|
else if (!strncmp(directive, "else", 4) || !strncmp(directive, "elif", 4))
|
|
{
|
|
int originalline = pr_source_line;
|
|
|
|
if (!ifs)
|
|
QCC_PR_ParseError(ERR_UNKNOWNPUCTUATION, "#else outside of #if\n");
|
|
|
|
ifs -= 1;
|
|
|
|
pr_file_p = directive+4;
|
|
if (!strncmp(directive, "elif", 4))
|
|
QCC_PR_SkipToEndOfLine(false);
|
|
else
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
|
|
ifs += QCC_PR_FalsePreProcessorIf(true, originalline);
|
|
}
|
|
else if (!strncmp(directive, "endif", 5))
|
|
{
|
|
pr_file_p = directive+5;
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
if (ifs <= 0)
|
|
QCC_PR_ParseError(ERR_NOPRECOMPILERIF, "unmatched #endif");
|
|
else
|
|
ifs-=1;
|
|
}
|
|
else if (!strncmp(directive, "eof", 3))
|
|
{
|
|
pr_file_p = NULL;
|
|
return true;
|
|
}
|
|
else if (!strncmp(directive, "error", 5))
|
|
{
|
|
pr_file_p = directive+5;
|
|
for (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\t' && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
msg[a] = '\0';
|
|
|
|
QCC_PR_SkipToEndOfLine(false);
|
|
|
|
QCC_PR_ParseError(ERR_HASHERROR, "#Error: %s", msg);
|
|
}
|
|
else if (!strncmp(directive, "warning", 7))
|
|
{
|
|
pr_file_p = directive+7;
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\r' && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
msg[a] = '\0';
|
|
|
|
QCC_PR_SkipToEndOfLine(false);
|
|
|
|
QCC_PR_ParseWarning(WARN_PRECOMPILERMESSAGE, "#warning: %s", msg);
|
|
}
|
|
else if (!strncmp(directive, "message", 7))
|
|
{
|
|
pr_file_p = directive+7;
|
|
for (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\r' && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
msg[a] = '\0';
|
|
|
|
if (flag_msvcstyle)
|
|
externs->Printf ("%s(%i) : #message: %s\n", s_filen, pr_source_line, msg);
|
|
else
|
|
externs->Printf ("%s:%i: #message: %s\n", s_filen, pr_source_line, msg);
|
|
QCC_PR_SkipToEndOfLine(false);
|
|
}
|
|
else if (!strncmp(directive, "copyright", 9))
|
|
{
|
|
pr_file_p = directive+9;
|
|
for (a = 0; a < sizeof(msg)-1 && qcc_iswhite(pr_file_p[a]) && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
msg[a] = '\0';
|
|
|
|
QCC_PR_SkipToEndOfLine(false);
|
|
|
|
if (strlen(msg) >= sizeof(QCC_copyright))
|
|
QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n");
|
|
QC_strlcpy(QCC_copyright, msg, sizeof(QCC_copyright)-1);
|
|
}
|
|
else if (!strncmp(directive, "package", 7))
|
|
{
|
|
pr_file_p=directive+7;
|
|
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
|
|
#if 0
|
|
if (!autoprototype)
|
|
{
|
|
struct pkgctx_s *ctx = Packager_Create(QCC_PR_PackagerMessage, NULL);
|
|
Packager_ParseText(ctx, pr_file_p);
|
|
Packager_WriteDataset(ctx, NULL);
|
|
Packager_Destroy(ctx);
|
|
}
|
|
#endif
|
|
|
|
pr_file_p += strlen(pr_file_p);
|
|
}
|
|
else if (!strncmp(directive, "pack", 4))
|
|
{
|
|
ifmode = 0;
|
|
pr_file_p=directive+4;
|
|
if (!strncmp(pr_file_p, "id", 2))
|
|
pr_file_p+=3;
|
|
else
|
|
{
|
|
ifmode = QCC_PR_LexInteger();
|
|
if (ifmode == 0)
|
|
ifmode = 1;
|
|
pr_file_p++;
|
|
}
|
|
for (a = 0; a < sizeof(msg)-1 && qcc_iswhite(pr_file_p[a]) && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
msg[a] = '\0';
|
|
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
|
|
if (ifmode == 0)
|
|
QCC_packid = atoi(msg);
|
|
else if (ifmode <= 5)
|
|
strcpy(QCC_Packname[ifmode-1], msg);
|
|
else
|
|
QCC_PR_ParseError(ERR_TOOMANYPACKFILES, "No more than 5 packs are allowed");
|
|
}
|
|
else if (!strncmp(directive, "forcecrc", 8))
|
|
{
|
|
pr_file_p=directive+8;
|
|
|
|
ForcedCRC = QCC_PR_LexInteger();
|
|
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
}
|
|
else if (!strncmp(directive, "merge", 5))
|
|
{
|
|
pr_file_p=directive+5;
|
|
|
|
while(qcc_iswhitesameline(*pr_file_p))
|
|
pr_file_p++;
|
|
|
|
QCC_PR_SimpleGetString();
|
|
externs->Printf("Merging from %s\n", pr_token);
|
|
QCC_ImportProgs(pr_token);
|
|
if (!*destfile && !destfile_explicit)
|
|
{
|
|
QCC_JoinPaths(destfile, sizeof(destfile), pr_token, compilingfile);
|
|
externs->Printf("Outputfile: %s\n", destfile);
|
|
}
|
|
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
}
|
|
else if (!strncmp(directive, "includelist", 11))
|
|
{
|
|
int defines=0;
|
|
pr_file_p=directive+11;
|
|
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
|
|
while(1)
|
|
{
|
|
QCC_PR_LexWhitespace(false);
|
|
if (!flag_hashonly && QCC_PR_CheckCompConst())
|
|
{
|
|
defines++;
|
|
continue;
|
|
}
|
|
if (!QCC_PR_SimpleGetToken())
|
|
{
|
|
if (!*pr_file_p)
|
|
{
|
|
if (defines>0)
|
|
{
|
|
defines--;
|
|
QCC_PR_UnInclude();
|
|
continue;
|
|
}
|
|
QCC_Error(ERR_EOF, "eof in includelist");
|
|
}
|
|
else
|
|
{
|
|
pr_file_p++;
|
|
pr_source_line++;
|
|
}
|
|
continue;
|
|
}
|
|
if (!strcmp(pr_token, "#endlist"))
|
|
{
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
break;
|
|
}
|
|
|
|
QCC_FindBestInclude(pr_token, compilingfile, true);
|
|
|
|
if (*pr_file_p == '\r')
|
|
pr_file_p++;
|
|
|
|
// QCC_PR_SkipToEndOfLine(true);
|
|
}
|
|
}
|
|
else if (!strncmp(directive, "include", 7))
|
|
{
|
|
char sm;
|
|
|
|
pr_file_p=directive+7;
|
|
|
|
while(qcc_iswhitesameline(*pr_file_p))
|
|
pr_file_p++;
|
|
|
|
msg[0] = '\0';
|
|
if (*pr_file_p == '\"')
|
|
sm = '\"';
|
|
else if (*pr_file_p == '<')
|
|
sm = '>';
|
|
else
|
|
{
|
|
QCC_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')
|
|
{
|
|
QCC_PR_ParseError(0, "#include continued over line boundry\n");
|
|
break;
|
|
}
|
|
msg[a++] = *pr_file_p;
|
|
pr_file_p++;
|
|
}
|
|
msg[a] = 0;
|
|
pr_file_p++;
|
|
|
|
QCC_FindBestInclude(msg, compilingfile, false);
|
|
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
}
|
|
else if (!strncmp(directive, "datafile", 8))
|
|
{
|
|
pr_file_p=directive+8;
|
|
|
|
while(qcc_iswhitesameline(*pr_file_p))
|
|
pr_file_p++;
|
|
|
|
QCC_PR_SimpleGetString();
|
|
externs->Printf("Including datafile: %s\n", pr_token);
|
|
QCC_AddFile(pr_token);
|
|
|
|
pr_file_p++;
|
|
|
|
for (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
else if (!strncmp(directive, "output", 6))
|
|
{
|
|
pr_file_p=directive+6;
|
|
|
|
while(qcc_iswhitesameline(*pr_file_p))
|
|
pr_file_p++;
|
|
|
|
QCC_PR_SimpleGetString();
|
|
if (!destfile_explicit)
|
|
{
|
|
QCC_JoinPaths(destfile, sizeof(destfile), pr_token, compilingfile);
|
|
externs->Printf("Outputfile: %s\n", pr_token);
|
|
}
|
|
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
}
|
|
else if (!strncmp(directive, "pragma", 6))
|
|
{
|
|
pr_file_p=directive+6;
|
|
while(qcc_iswhitesameline(*pr_file_p))
|
|
pr_file_p++;
|
|
|
|
qcc_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 == '(') && !*qcc_token)
|
|
{
|
|
msg[a] = '\0';
|
|
strcpy(qcc_token, msg);
|
|
a=0;
|
|
if (*pr_file_p != '(')
|
|
continue;
|
|
}
|
|
msg[a++] = *pr_file_p;
|
|
}
|
|
|
|
msg[a] = '\0';
|
|
{
|
|
char *end;
|
|
for (end = msg + a-1; end>=msg && qcc_iswhite(*end); end--)
|
|
*end = '\0';
|
|
}
|
|
|
|
if (!*qcc_token)
|
|
{
|
|
strcpy(qcc_token, msg);
|
|
msg[0] = '\0';
|
|
}
|
|
|
|
{
|
|
char *end;
|
|
for (end = msg + a-1; end>=msg && qcc_iswhite(*end); end--)
|
|
*end = '\0';
|
|
}
|
|
|
|
if (!QC_strcasecmp(qcc_token, "DONT_COMPILE_THIS_FILE"))
|
|
{
|
|
QCC_PR_LexWhitespace(false);
|
|
while (*pr_file_p)
|
|
{
|
|
if (!qcc_iswhite(*pr_file_p))
|
|
pr_file_p++;
|
|
QCC_PR_LexWhitespace(false);
|
|
}
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "COPYRIGHT"))
|
|
{
|
|
char *e = strrchr(msg+1, '\"');
|
|
if (*msg == '\"' && e && e != msg)
|
|
{ //FIXME: handle \ns
|
|
memmove(msg, msg+1, e-(msg+1));
|
|
msg[e-(msg+1)] = 0;
|
|
}
|
|
if (!QC_strlcpy(QCC_copyright, msg, sizeof(QCC_copyright)))
|
|
QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n");
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "compress"))
|
|
{
|
|
extern pbool compressoutput;
|
|
compressoutput = atoi(msg);
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "forcecrc"))
|
|
{
|
|
ForcedCRC = atoi(msg);
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "framerate"))
|
|
{
|
|
qcc_framerate = atof(msg);
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "once"))
|
|
{
|
|
struct qccincludeonced_s *onced = qccHunkAlloc(sizeof(*onced) + strlen(compilingfile));
|
|
strcpy(onced->name, compilingfile);
|
|
onced->next = qccincludeonced;
|
|
qccincludeonced = onced;
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "file"))
|
|
{ //#pragma file(foobar.qc)
|
|
if (!flag_nopragmafileline)
|
|
{
|
|
char *e;
|
|
char *m = msg;
|
|
if (*m == '(')
|
|
{
|
|
m++;
|
|
e = strchr(m, ')');
|
|
if (e)
|
|
*e = 0;
|
|
}
|
|
|
|
s_filen = e = qccHunkAlloc(strlen(m)+1);
|
|
strcpy(e, m);
|
|
if (opt_filenames)
|
|
{
|
|
optres_filenames += strlen(m);
|
|
s_filed = 0;
|
|
}
|
|
else
|
|
s_filed = QCC_CopyString (m);
|
|
}
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "line"))
|
|
{ //#pragma line(666)
|
|
if (!flag_nopragmafileline)
|
|
{
|
|
char *m = msg;
|
|
if (*m == '(')
|
|
m++;
|
|
pr_source_line = strtoul(m, &m, 0)-1;
|
|
}
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "includedir"))
|
|
{
|
|
char newinc[1024];
|
|
int i;
|
|
QCC_COM_Parse(msg);
|
|
|
|
if (*qcc_token)
|
|
{
|
|
i = qcc_token[strlen(qcc_token)-1];
|
|
if (i != '/' && i != '\\')
|
|
QC_strlcat(qcc_token, "/", sizeof(qcc_token));
|
|
}
|
|
|
|
QCC_JoinPaths(newinc, sizeof(newinc), qcc_token, compilingfile);
|
|
QCC_PR_AddIncludePath(newinc);
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "noref"))
|
|
defaultnoref = !!atoi(msg);
|
|
else if (!QC_strcasecmp(qcc_token, "nosave"))
|
|
defaultnosave = !!atoi(msg);
|
|
else if (!QC_strcasecmp(qcc_token, "defaultstatic"))
|
|
defaultstatic = !!atoi(msg);
|
|
else if (!QC_strcasecmp(qcc_token, "autoproto"))
|
|
{
|
|
if (!autoprototyped)
|
|
{
|
|
if (numpr_globals != RESERVED_OFS)
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "#pragma autoproto must appear before any definitions");
|
|
else
|
|
autoprototype = *msg?!!atoi(msg):true;
|
|
}
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "wrasm"))
|
|
{
|
|
pbool on = atoi(msg);
|
|
|
|
if (asmfile && !on)
|
|
{
|
|
fclose(asmfile);
|
|
asmfile = NULL;
|
|
}
|
|
if (!asmfile && on)
|
|
{
|
|
if (asmfilebegun)
|
|
asmfile = fopen("qc.asm", "ab");
|
|
else
|
|
asmfile = fopen("qc.asm", "wb");
|
|
if (asmfile)
|
|
asmfilebegun = true;
|
|
}
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "optimise") || !QC_strcasecmp(qcc_token, "optimize")) //bloomin' americans.
|
|
{
|
|
int o;
|
|
extern pbool qcc_nopragmaoptimise;
|
|
if (pr_scope)
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "pragma %s: unable to change optimisation options mid-function", qcc_token);
|
|
else if (*msg >= '0' && *msg <= '3')
|
|
{
|
|
int lev = atoi(msg);
|
|
pbool state;
|
|
int once = false;
|
|
for (o = 0; optimisations[o].enabled; o++)
|
|
{
|
|
state = optimisations[o].optimisationlevel <= lev;
|
|
if (qcc_nopragmaoptimise && *optimisations[o].enabled != state)
|
|
{
|
|
if (!once++)
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "pragma %s %s: overriden by commandline", qcc_token, msg);
|
|
}
|
|
else
|
|
*optimisations[o].enabled = state;
|
|
}
|
|
}
|
|
else if (!strnicmp(msg, "addon", 5) || !strnicmp(msg, "mutator", 7))
|
|
{
|
|
int lev = 2;
|
|
pbool state = false;
|
|
for (o = 0; optimisations[o].enabled; o++)
|
|
{
|
|
if (optimisations[o].optimisationlevel > lev)
|
|
{
|
|
if (qcc_nopragmaoptimise && *optimisations[o].enabled != state)
|
|
QCC_PR_ParseWarning(WARN_IGNORECOMMANDLINE, "pragma %s %s: disabling %s", qcc_token, msg, optimisations[o].fullname);
|
|
*optimisations[o].enabled = state;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char *opt = msg;
|
|
pbool state = true;
|
|
if (!strnicmp(msg, "no-", 3))
|
|
{
|
|
state = false;
|
|
opt += 3;
|
|
}
|
|
for (o = 0; optimisations[o].enabled; o++)
|
|
if ((*optimisations[o].abbrev && !stricmp(opt, optimisations[o].abbrev)) || !stricmp(opt, optimisations[o].fullname))
|
|
{
|
|
if (qcc_nopragmaoptimise && *optimisations[o].enabled != state)
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "pragma %s %s: overriden by commandline", qcc_token, optimisations[o].fullname);
|
|
else
|
|
*optimisations[o].enabled = state;
|
|
break;
|
|
}
|
|
if (!optimisations[o].enabled)
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "pragma %s: %s unsupported", qcc_token, opt);
|
|
}
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "sourcefile"))
|
|
{
|
|
char *s = msg;
|
|
while ((s = QCC_COM_Parse(s)))
|
|
QCC_RegisterSourceFile(qcc_token);
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "TARGET"))
|
|
{
|
|
QCC_COM_Parse(msg);
|
|
if (!QCC_OPCodeSetTargetName(qcc_token))
|
|
QCC_PR_ParseWarning(WARN_BADTARGET, "Unknown target \'%s\'. Ignored.\nValid targets are: ID, HEXEN2, FTE, FTEH2, KK7, DP(patched)", qcc_token);
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "PROGS_SRC"))
|
|
{ //doesn't make sense, but silenced if you are switching between using a certain precompiler app used with CuTF.
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "PROGS_DAT"))
|
|
{ //doesn't make sence, but silenced if you are switching between using a certain precompiler app used with CuTF.
|
|
char olddest[1024];
|
|
Q_strlcpy(olddest, destfile, sizeof(olddest));
|
|
QCC_COM_Parse(msg);
|
|
|
|
if (!destfile_explicit) //if output file is named on the commandline, don't change it mid-compile
|
|
QCC_JoinPaths(destfile, sizeof(destfile), qcc_token, compilingfile);
|
|
|
|
if (strcmp(destfile, olddest))
|
|
externs->Printf("Outputfile: %s\n", destfile);
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "opcode"))
|
|
{
|
|
int st;
|
|
char *s = QCC_COM_Parse(msg);
|
|
if (!QC_strcasecmp(qcc_token, "enable") || !QC_strcasecmp(qcc_token, "on"))
|
|
st = 1;
|
|
else if (!QC_strcasecmp(qcc_token, "disable") || !QC_strcasecmp(qcc_token, "off"))
|
|
st = 0;
|
|
else
|
|
{
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "opcode state not recognised");
|
|
st = -1;
|
|
}
|
|
|
|
if (st >= 0)
|
|
{
|
|
int f;
|
|
while ((s = QCC_COM_Parse(s)))
|
|
{
|
|
for (f = 0; pr_opcodes[f].opname; f++)
|
|
{
|
|
if (!QC_strcasecmp(pr_opcodes[f].opname, qcc_token))
|
|
{
|
|
if (f >= OP_NUMREALOPS)
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "opcode %s is internal", qcc_token); //these will change with later opcodes, do not allow them to be written into the output.
|
|
else if (st)
|
|
pr_opcodes[f].flags |= OPF_VALID;
|
|
else
|
|
pr_opcodes[f].flags &= ~OPF_VALID;
|
|
break;
|
|
}
|
|
}
|
|
if (!pr_opcodes[f].opname)
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "opcode %s not recognised", qcc_token);
|
|
}
|
|
}
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "keyword") || !QC_strcasecmp(qcc_token, "flag"))
|
|
{
|
|
char *s;
|
|
int st;
|
|
s = QCC_COM_Parse(msg);
|
|
if (!QC_strcasecmp(qcc_token, "enable") || !QC_strcasecmp(qcc_token, "on"))
|
|
st = 1;
|
|
else if (!QC_strcasecmp(qcc_token, "disable") || !QC_strcasecmp(qcc_token, "off"))
|
|
st = 0;
|
|
else
|
|
{
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "compiler flag state not recognised");
|
|
st = -1;
|
|
}
|
|
if (st >= 0)
|
|
{
|
|
int f;
|
|
while ((s = QCC_COM_Parse(s)))
|
|
{
|
|
for (f = 0; compiler_flag[f].enabled; f++)
|
|
{
|
|
if (!QC_strcasecmp(compiler_flag[f].abbrev, qcc_token))
|
|
{
|
|
if (compiler_flag[f].flags & FLAG_MIDCOMPILE)
|
|
{
|
|
*compiler_flag[f].enabled = st;
|
|
if (compiler_flag[f].enabled == &flag_cpriority)
|
|
QCC_PrioritiseOpcodes();
|
|
}
|
|
else
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "Cannot enable/disable keyword/flag via a pragma");
|
|
break;
|
|
}
|
|
}
|
|
if (!compiler_flag[f].enabled)
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "keyword/flag %s not recognised", qcc_token);
|
|
}
|
|
}
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "warning"))
|
|
{
|
|
int st;
|
|
char *s;
|
|
s = QCC_COM_Parse(msg);
|
|
if (!stricmp(qcc_token, "enable") || !stricmp(qcc_token, "on"))
|
|
st = WA_WARN;
|
|
else if (!stricmp(qcc_token, "disable") || !stricmp(qcc_token, "off") || !stricmp(qcc_token, "ignore"))
|
|
st = WA_IGNORE;
|
|
else if (!stricmp(qcc_token, "error"))
|
|
st = WA_ERROR;
|
|
else if (!stricmp(qcc_token, "toggle"))
|
|
st = 3;
|
|
else
|
|
{
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "warning state not recognised");
|
|
st = -1;
|
|
}
|
|
if (st>=0)
|
|
{
|
|
int wn;
|
|
while ((s = QCC_COM_Parse(s)))
|
|
{
|
|
wn = QCC_WarningForName(qcc_token);
|
|
if (wn < 0)
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "warning id not recognised");
|
|
else
|
|
{
|
|
if (st == 3) //toggle
|
|
qccwarningaction[wn] = !!qccwarningaction[wn];
|
|
else
|
|
qccwarningaction[wn] = st;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QCC_PR_SkipToEndOfLine(false);
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "Unknown pragma \'%s\'", qcc_token);
|
|
}
|
|
|
|
QCC_PR_SkipToEndOfLine(true);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_NewLine
|
|
|
|
Call at start of file and when *pr_file_p == '\n'
|
|
==============
|
|
*/
|
|
void QCC_PR_NewLine (pbool incomment)
|
|
{
|
|
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 (QCC_PR_Precompiler())
|
|
{
|
|
}
|
|
|
|
// if (pr_dumpasm)
|
|
// PR_PrintNextLine ();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexString
|
|
|
|
Parses a quoted string
|
|
==============
|
|
*/
|
|
int QCC_PR_LexEscapedCodepoint(void)
|
|
{ //for "\foo" or '\foo' handling.
|
|
//caller will have read the \ already.
|
|
int t;
|
|
int c = *pr_file_p++;
|
|
if (!c)
|
|
QCC_PR_ParseError (ERR_EOF, "EOF inside quote");
|
|
if (c == 'n')
|
|
c = '\n';
|
|
else if (c == 'r')
|
|
c = '\r';
|
|
else if (c == '#') //avoid preqcc expansion in strings.
|
|
c = '#';
|
|
else if (c == '"')
|
|
c = '"';
|
|
else if (c == 't')
|
|
c = '\t'; //tab
|
|
else if (c == 'a')
|
|
c = '\a'; //bell
|
|
else if (c == 'v')
|
|
c = '\v'; //vertical tab
|
|
else if (c == 'f')
|
|
c = '\f'; //form feed
|
|
// else if (c == 's' || c == 'b')
|
|
// c = 0; //invalid...
|
|
//else if (c == 'b')
|
|
// c = '\b';
|
|
else if (c == '[')
|
|
c = 0xe010; //quake specific
|
|
else if (c == ']')
|
|
c = 0xe011; //quake specific
|
|
else if (c == '{')
|
|
{
|
|
int d;
|
|
c = 0;
|
|
if (*pr_file_p == 'x')
|
|
{
|
|
pr_file_p++;
|
|
while ((d = *pr_file_p++) != '}')
|
|
{
|
|
if (d >= '0' && d <= '9')
|
|
c = c * 16 + d - '0';
|
|
else if (d >= 'a' && d <= 'f')
|
|
c = c * 16 + 10+d - 'a';
|
|
else if (d >= 'A' && d <= 'F')
|
|
c = c * 16 + 10+d - 'A';
|
|
else
|
|
QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while ((d = *pr_file_p++) != '}')
|
|
{
|
|
if (d >= '0' && d <= '9')
|
|
c = c * 10 + d - '0';
|
|
else
|
|
QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code");
|
|
}
|
|
}
|
|
}
|
|
else if (c == '.')
|
|
c = 0xe01c;
|
|
else if (c == '<')
|
|
c = 0xe01d; //separator start
|
|
else if (c == '-')
|
|
c = 0xe01e; //separator middle
|
|
else if (c == '>')
|
|
c = 0xe01f; //separator end
|
|
else if (c == '(')
|
|
c = 0xe080; //slider start
|
|
else if (c == '=')
|
|
c = 0xe081; //slider middle
|
|
else if (c == ')')
|
|
c = 0xe082; //slider end
|
|
else if (c == '+')
|
|
c = 0xe083; //slider box
|
|
else if (c == 'u' || c == 'U')
|
|
{
|
|
//lower case u specifies exactly 4 nibbles.
|
|
//upper case U specifies exactly 8 nibbles.
|
|
unsigned int nibbles = (c=='u')?4:8;
|
|
c = 0;
|
|
while (nibbles --> 0)
|
|
{
|
|
t = (unsigned char)*pr_file_p;
|
|
if (t >= '0' && t <= '9')
|
|
c = (c*16) + (t - '0');
|
|
else if (t >= 'A' && t <= 'F')
|
|
c = (c*16) + (t - 'A') + 10;
|
|
else if (t >= 'a' && t <= 'f')
|
|
c = (c*16) + (t - 'a') + 10;
|
|
else
|
|
break;
|
|
pr_file_p++;
|
|
}
|
|
if (nibbles)
|
|
QCC_PR_ParseWarning(ERR_BADCHARACTERCODE, "Unicode character terminated unexpectedly");
|
|
}
|
|
else if (c == 'x' || c == 'X')
|
|
{
|
|
int d;
|
|
c = 0;
|
|
|
|
d = (unsigned char)*pr_file_p++;
|
|
if (d >= '0' && d <= '9')
|
|
c += d - '0';
|
|
else if (d >= 'A' && d <= 'F')
|
|
c += d - 'A' + 10;
|
|
else if (d >= 'a' && d <= 'f')
|
|
c += d - 'a' + 10;
|
|
else
|
|
QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code");
|
|
|
|
c *= 16;
|
|
|
|
d = (unsigned char)*pr_file_p++;
|
|
if (d >= '0' && d <= '9')
|
|
c += d - '0';
|
|
else if (d >= 'A' && d <= 'F')
|
|
c += d - 'A' + 10;
|
|
else if (d >= 'a' && d <= 'f')
|
|
c += d - 'a' + 10;
|
|
else
|
|
QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code");
|
|
}
|
|
else if (c == '\\')
|
|
c = '\\';
|
|
else if (c == '\'')
|
|
c = '\'';
|
|
else if (c >= '0' && c <= '9') //WARNING: This is not octal, but uses 'yellow' numbers instead (as on hud).
|
|
c = 0xe012 + c - '0';
|
|
else if (c == '\r')
|
|
{ //sigh
|
|
c = *pr_file_p++;
|
|
if (c != '\n')
|
|
QCC_PR_ParseWarning(WARN_HANGINGSLASHR, "Hanging \\\\\r");
|
|
pr_source_line++;
|
|
}
|
|
else if (c == '\n')
|
|
{ //sigh
|
|
pr_source_line++;
|
|
}
|
|
else
|
|
QCC_PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, "Unknown escape char %c", c);
|
|
|
|
return c;
|
|
}
|
|
void QCC_PR_LexString (void)
|
|
{
|
|
unsigned int c, t;
|
|
int bytecount;
|
|
int len = 0;
|
|
char *end;
|
|
const char *cnst;
|
|
int raw;
|
|
char rawdelim[64];
|
|
int stringtype;
|
|
//0 - quake output, input is 8bit. warnings when its not ascii. \u will still give utf-8 text, other chars as-is. Expect \s to screw everything up with utf-8 output.
|
|
//1 - quake output, input is utf-8. due to editors not supporting it, that generally means the input (ab)uses markup.
|
|
//2 - utf-8 output, input is utf-8. welcome to the future! unfortunately not the present.
|
|
|
|
int texttype;
|
|
pbool first = true;
|
|
|
|
for(;;)
|
|
{
|
|
raw = 0;
|
|
texttype = 0;
|
|
|
|
QCC_PR_LexComment(&pr_token_precomment);
|
|
|
|
if (flag_qccx && *pr_file_p == ':')
|
|
{
|
|
pr_file_p++;
|
|
pr_token[len++] = 0;
|
|
continue;
|
|
}
|
|
|
|
if (*pr_file_p == 'R' && pr_file_p[1] == '\"')
|
|
{
|
|
/*R"delim(fo
|
|
o)delim" -> "fo\no"
|
|
the []
|
|
*/
|
|
raw = 1;
|
|
pr_file_p+=2;
|
|
|
|
while (1)
|
|
{
|
|
c = *pr_file_p++;
|
|
if (c == '(')
|
|
{
|
|
rawdelim[0] = ')';
|
|
break;
|
|
}
|
|
if (!c || raw >= sizeof(rawdelim)-1)
|
|
QCC_PR_ParseError (ERR_EOF, "EOF while parsing raw string delimiter. Expected: R\"delim(string)delim\"");
|
|
rawdelim[raw++] = c;
|
|
}
|
|
rawdelim[raw++] = '\"';
|
|
|
|
//these two conditions are generally part of the C preprocessor.
|
|
if (!strncmp(pr_file_p, "\\\r\n", 3))
|
|
{ //dos format
|
|
pr_file_p += 3;
|
|
pr_source_line++;
|
|
}
|
|
else if (!strncmp(pr_file_p, "\\\r", 2) || !strncmp(pr_file_p, "\\\n", 2))
|
|
{ //mac + unix format
|
|
pr_file_p += 2;
|
|
pr_source_line++;
|
|
}
|
|
stringtype = 0;
|
|
}
|
|
else if (*pr_file_p == 'Q' && pr_file_p[1] == '\"')
|
|
{ //quake output with utf-8 input (expect to need markup).
|
|
stringtype = 1;
|
|
pr_file_p+=2;
|
|
}
|
|
else if ((*pr_file_p == 'U' || *pr_file_p == 'u' || *pr_file_p == 'L') && pr_file_p[1] == '\"')
|
|
{ //unicode string, char32_t, char16_t, wchar_t respectively. we spit out utf-8 regardless.
|
|
QCC_PR_ParseWarning(WARN_NOTUTF8, "char32_t/char16_t/wchar_t strings are not supported, treating as u8 prefix (as utf-8)");
|
|
stringtype = 2;
|
|
pr_file_p+=2;
|
|
}
|
|
else if (*pr_file_p == 'u' && pr_file_p[1] == '8' && pr_file_p[2] == '\"')
|
|
{ //utf-8 string.
|
|
stringtype = 2;
|
|
pr_file_p+=3;
|
|
}
|
|
else if (*pr_file_p == '\"')
|
|
{
|
|
stringtype = flag_utf8strings?2:0;
|
|
pr_file_p++;
|
|
}
|
|
else if (first)
|
|
{
|
|
stringtype = 0;
|
|
QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Expected string constant");
|
|
}
|
|
else
|
|
break;
|
|
first = false;
|
|
|
|
for(;;)
|
|
{
|
|
c = *pr_file_p++;
|
|
if (!c)
|
|
QCC_PR_ParseError (ERR_EOF, "EOF inside quote");
|
|
|
|
if (raw)
|
|
{
|
|
//raw strings contain very little parsing. just delimiter and initial \NL support.
|
|
if (c == rawdelim[0] && !strncmp(pr_file_p, rawdelim+1, raw-1))
|
|
{
|
|
pr_file_p += raw-1;
|
|
break;
|
|
}
|
|
|
|
//make sure line numbers are correct though.
|
|
if (c == '\r' && *pr_file_p != '\n')
|
|
pr_source_line++; //mac
|
|
if (c == '\n') //dos/unix
|
|
pr_source_line++;
|
|
goto forcebyte;
|
|
}
|
|
else
|
|
{
|
|
if (c=='\n')
|
|
QCC_PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, "newline inside quote");
|
|
if (c=='\\')
|
|
{ // escape char
|
|
c = *pr_file_p; //peek at it, for our hacks.
|
|
if (c == 's' || c == 'b')
|
|
{
|
|
pr_file_p++;
|
|
texttype ^= 0xe080;
|
|
continue;
|
|
}
|
|
else if (c == '.')
|
|
{
|
|
pr_file_p++;
|
|
c = 0xe01c | texttype;
|
|
}
|
|
else if (c == 'u' || c == 'U')
|
|
{ //special hack, \u is a utf-8 code regardless of output encoding...
|
|
c = QCC_PR_LexEscapedCodepoint();
|
|
goto forceutf8;
|
|
}
|
|
else if (c == 'x' || c == 'X')
|
|
{ //special hack, \xXX in a string is an explicit byte regardless of encoding.
|
|
c = QCC_PR_LexEscapedCodepoint();
|
|
// if (c > 0xff)
|
|
// QCC_PR_ParseWarning(ERR_BADCHARACTERCODE, "Bad unicode character code - codepoint %#x is above 0xFF", c);
|
|
goto forcebyte;
|
|
}
|
|
else
|
|
{
|
|
c = QCC_PR_LexEscapedCodepoint();
|
|
// if (stringtype != 2 && c > 0xff)
|
|
// QCC_PR_ParseWarning(ERR_BADCHARACTERCODE, "Bad legacy character code - codepoint %#x is above 0xFF", c);
|
|
}
|
|
}
|
|
else if (c=='\"')
|
|
{
|
|
break;
|
|
}
|
|
else if (c == '#' && flag_macroinstrings)
|
|
{
|
|
for (end = pr_file_p; ; end++)
|
|
{
|
|
if (qcc_iswhite(*end))
|
|
break;
|
|
|
|
if (*end == ')'
|
|
|| *end == '('
|
|
|| *end == '+'
|
|
|| *end == '-'
|
|
|| *end == '*'
|
|
|| *end == '/'
|
|
|| *end == '\\'
|
|
|| *end == '|'
|
|
|| *end == '&'
|
|
|| *end == '='
|
|
|| *end == '^'
|
|
|| *end == '~'
|
|
|| *end == '['
|
|
|| *end == ']'
|
|
|| *end == '\"'
|
|
|| *end == '{'
|
|
|| *end == '}'
|
|
|| *end == ';'
|
|
|| *end == ':'
|
|
|| *end == ','
|
|
|| *end == '.'
|
|
|| *end == '#')
|
|
break;
|
|
}
|
|
|
|
c = *end;
|
|
*end = '\0';
|
|
cnst = QCC_PR_CheckCompConstString(pr_file_p);
|
|
if (cnst==pr_file_p)
|
|
{
|
|
if (*pr_file_p)
|
|
QCC_PR_ParseWarning(WARN_MACROINSTRING, "Unable to expand string macro %s", pr_file_p);
|
|
|
|
}
|
|
else if (cnst)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_MACROINSTRING, "Macro %s expansion in string", pr_file_p);
|
|
*end = c;
|
|
|
|
if (len+strlen(cnst) >= sizeof(pr_token)-1)
|
|
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_token)-1);
|
|
|
|
strcpy(pr_token+len, cnst);
|
|
bytecount=strlen(cnst);
|
|
while (bytecount > 0 && qcc_iswhitesameline(pr_token[len+bytecount-1]))
|
|
bytecount--; //make sure there's no trailing whitespace on the end.
|
|
len+=bytecount;
|
|
pr_file_p = end;
|
|
continue;
|
|
}
|
|
*end = c;
|
|
c = '#'; //undo
|
|
}
|
|
else if (c == 0x7C && flag_acc) //reacc support... reacc is strange.
|
|
c = '\n';
|
|
else
|
|
{
|
|
unsigned int cp = c;
|
|
unsigned int len = stringtype?utf8_check(pr_file_p-1, &cp):0;
|
|
if (!len)
|
|
{ //invalid utf-8 encoding? don't treat it as utf-8!
|
|
if (stringtype)
|
|
QCC_PR_ParseWarning(ERR_BADCHARACTERCODE, "Input string is not valid utf-8");
|
|
if (c >= ' ')
|
|
c |= texttype;
|
|
goto forcebyte;
|
|
}
|
|
if (texttype)
|
|
{
|
|
if (cp < ' ')
|
|
c = cp; //don't mask C0 chars like \t or \n
|
|
else if (cp < 0x80)
|
|
c = cp|0xe080; //DO mask other ascii chars (and map to the private-use range at the same time, because this isn't standard unicode any more)
|
|
else
|
|
{
|
|
QCC_PR_ParseWarning(ERR_BADCHARACTERCODE, "Unable to mask non-ascii chars. Attempting to mask bytes");
|
|
c |= texttype;
|
|
goto forcequake;
|
|
}
|
|
}
|
|
else
|
|
c = cp;
|
|
pr_file_p += len-1;
|
|
}
|
|
}
|
|
|
|
// if (c >= 0x20 && c < 0x80)
|
|
// c |= 0xe000; //TEST
|
|
if (stringtype == 2)
|
|
{ //we're outputting a utf-8 string.
|
|
forceutf8:
|
|
if (c > 0x10FFFFu) //RFC 3629 imposes the same limit as UTF-16 surrogate pairs.
|
|
QCC_PR_ParseWarning(WARN_NOTUTF8, "Bad unicode character code - codepoint is above 0x10FFFFu");
|
|
|
|
//figure out the count of bytes required to encode this char
|
|
bytecount = 1;
|
|
t = 0x80;
|
|
while (c >= t)
|
|
{
|
|
if (bytecount == 1)
|
|
t <<= 4;
|
|
else if (bytecount < 7)
|
|
t <<= 5;
|
|
else
|
|
t <<= 6;
|
|
bytecount++;
|
|
}
|
|
|
|
//error if needed
|
|
if (len+bytecount >= sizeof(pr_token))
|
|
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_token)-1);
|
|
|
|
//output it.
|
|
if (bytecount == 1)
|
|
pr_token[len++] = (unsigned char)(c&0x7f);
|
|
else
|
|
{
|
|
t = bytecount*6;
|
|
t = t-6;
|
|
pr_token[len++] = (unsigned char)((c>>t)&(0x0000007f>>bytecount)) | (0xffffff00 >> bytecount);
|
|
do
|
|
{
|
|
t = t-6;
|
|
pr_token[len++] = (unsigned char)((c>>t)&0x3f) | 0x80;
|
|
} while(t);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
forcequake:
|
|
//we need to convert it to a quake char...
|
|
if (c >= 0xe000 && c <= 0xe0ff)
|
|
c = c & 0xff; //this private use range is commonly used for quake's glyphs.
|
|
else if (c >= 0 && c <= 0x7f)
|
|
; //FIXME: SOME c0 codes are known to quake, but many got reused for random glyphs. however I'm going to treat quake as full ascii.
|
|
else if (c >= 0x80)
|
|
{
|
|
//FIXME: spit it out as ^{xxxxxx} instead
|
|
QCC_PR_ParseWarning(WARN_NOTUTF8, "Cannot convert character to quake's charset");
|
|
}
|
|
|
|
forcebyte:
|
|
if (len >= sizeof(pr_token)-1)
|
|
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_token)-1);
|
|
pr_token[len] = c;
|
|
len++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (len > sizeof(pr_immediate_string)-1)
|
|
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_immediate_string)-1);
|
|
|
|
pr_token[len] = 0;
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_string;
|
|
memcpy(pr_immediate_string, pr_token, len+1);
|
|
pr_immediate_strlen = len;
|
|
|
|
/*if (qccwarningaction[WARN_NOTUTF8] && stringtype != 1)
|
|
{
|
|
unsigned int code;
|
|
size_t c;
|
|
for (c = 0; c < pr_immediate_strlen; )
|
|
{
|
|
len = utf8_check(&pr_token[c], &code);
|
|
if (!len || c+len>pr_immediate_strlen)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_NOTUTF8, "String literal is not valid utf-8");
|
|
break;
|
|
}
|
|
c += len;
|
|
}
|
|
}*/
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexNumber
|
|
==============
|
|
*/
|
|
int QCC_PR_LexInteger (void)
|
|
{
|
|
int c;
|
|
int len;
|
|
|
|
len = 0;
|
|
c = *pr_file_p;
|
|
if (pr_file_p[0] == '0' && (pr_file_p[1] == 'x' || 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 == '.'&&pr_file_p[1]!='.') || (c>='a' && c <= 'f'));
|
|
pr_token[len] = 0;
|
|
return atoi (pr_token);
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#define longlong __int64
|
|
#define LL(x) x##i64
|
|
#else
|
|
#define longlong long long
|
|
#define LL(x) x##ll
|
|
#endif
|
|
|
|
static void QCC_PR_LexNumber (void)
|
|
{
|
|
int tokenlen = 0;
|
|
longlong num=0;
|
|
int base=0;
|
|
int c;
|
|
int sign=1;
|
|
if (*pr_file_p == '-')
|
|
{
|
|
sign=-1;
|
|
pr_file_p++;
|
|
|
|
pr_token[tokenlen++] = '-';
|
|
}
|
|
if (pr_file_p[0] == '0' && (pr_file_p[1] == 'x' || pr_file_p[1] == 'X'))
|
|
{
|
|
pr_file_p+=2;
|
|
base = 16;
|
|
|
|
pr_token[tokenlen++] = '0';
|
|
pr_token[tokenlen++] = 'x';
|
|
}
|
|
else if (pr_file_p[0] == '0')
|
|
{
|
|
pr_file_p++;
|
|
if (*pr_file_p >= '0' && *pr_file_p <= '9')
|
|
QCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, "A leading 0 is interpreted as base-8.");
|
|
base = 8;
|
|
|
|
pr_token[tokenlen++] = '0';
|
|
}
|
|
|
|
pr_immediate_type = NULL;
|
|
//assume base 10 if not stated
|
|
if (!base)
|
|
base = 10;
|
|
|
|
while((c = *pr_file_p))
|
|
{
|
|
if (c >= '0' && c <= '9' && c < '0'+base)
|
|
{
|
|
pr_token[tokenlen++] = c;
|
|
num*=base;
|
|
num += c-'0';
|
|
}
|
|
else if (c >= 'a' && c <= 'z' && c < 'a'+base-10)
|
|
{
|
|
pr_token[tokenlen++] = c;
|
|
num*=base;
|
|
num += c -'a'+10;
|
|
}
|
|
else if (c >= 'A' && c <= 'Z' && c < 'A'+base-10)
|
|
{
|
|
pr_token[tokenlen++] = c;
|
|
num*=base;
|
|
num += c -'A'+10;
|
|
}
|
|
else if (c == '.' && pr_file_p[1]!='.')
|
|
{
|
|
pr_token[tokenlen++] = c;
|
|
pr_file_p++;
|
|
pr_immediate_type = flag_assume_double?type_double:type_float;
|
|
while(1)
|
|
{
|
|
c = *pr_file_p;
|
|
if (c >= '0' && c <= '9')
|
|
{
|
|
pr_token[tokenlen++] = c;
|
|
}
|
|
else if (c == 'f' || c == 'F')
|
|
{
|
|
pr_file_p++;
|
|
break;
|
|
}
|
|
else if (c == 'd' || c == 'D')
|
|
{
|
|
pr_immediate_type = type_double;
|
|
pr_file_p++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
pr_file_p++;
|
|
}
|
|
pr_token[tokenlen++] = 0;
|
|
if (pr_immediate_type == type_double)
|
|
pr_immediate._double = atof(pr_token);
|
|
else
|
|
pr_immediate._float = (float)atof(pr_token);
|
|
goto checkjunk;
|
|
}
|
|
else if (c == 'f' || c == 'F')
|
|
{
|
|
pr_token[tokenlen++] = c;
|
|
pr_token[tokenlen++] = 0;
|
|
pr_file_p++;
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = num*sign;
|
|
|
|
num*=sign;
|
|
if ((longlong)pr_immediate._float != (longlong)num)
|
|
QCC_PR_ParseWarning(WARN_OVERFLOW, "numerical overflow");
|
|
goto checkjunk;
|
|
}
|
|
else if (c == 'd' || c == 'D')
|
|
{ //note: conflicts with hex. add a dot before it or something.
|
|
pr_token[tokenlen++] = c;
|
|
pr_token[tokenlen++] = 0;
|
|
pr_file_p++;
|
|
pr_immediate_type = type_double;
|
|
pr_immediate._double = num*sign;
|
|
|
|
num*=sign;
|
|
if ((longlong)pr_immediate._double != (longlong)num)
|
|
QCC_PR_ParseWarning(WARN_OVERFLOW, "numerical overflow");
|
|
goto checkjunk;
|
|
}
|
|
else if (c == 'i' || c == 'u' || c == 'l' || c == 'I' || c == 'U' || c == 'L')
|
|
{ //length and sign flags can be any order. LL suffix must have the same case (but not necessarily match the sign suffix)
|
|
int isunsigned;
|
|
int islong = (c == 'l')||(c == 'L');
|
|
pr_token[tokenlen++] = c;
|
|
pr_file_p++;
|
|
if (islong)
|
|
{ //length suffix was first.
|
|
//long-long?
|
|
if (*pr_file_p == c)
|
|
pr_token[tokenlen++] = *pr_file_p++;
|
|
//check for signed suffix...
|
|
c = *pr_file_p;
|
|
isunsigned = (c == 'u')||(c=='U');
|
|
if (c == 'i' || c == 'I' || isunsigned) //ignore an explicit redundant 'i' char, for not-a-float.
|
|
pr_token[tokenlen++] = *pr_file_p++;
|
|
}
|
|
else
|
|
{
|
|
isunsigned = (c == 'u')||(c=='U');
|
|
//we already made sure it u or i, and its not an l
|
|
c = *pr_file_p;
|
|
if (c == 'l' || c == 'L')
|
|
{
|
|
pr_token[tokenlen++] = *pr_file_p++;
|
|
islong = true;
|
|
//long-long?
|
|
if (*pr_file_p == c)
|
|
pr_token[tokenlen++] = *pr_file_p++;
|
|
}
|
|
}
|
|
pr_token[tokenlen++] = 0;
|
|
num *= sign;
|
|
if (islong)
|
|
{
|
|
pr_immediate_type = (isunsigned)?type_uint64:type_int64;
|
|
pr_immediate.i64 = num;
|
|
}
|
|
else
|
|
{
|
|
pr_immediate_type = (isunsigned)?type_uint:type_integer;
|
|
pr_immediate._int = num;
|
|
|
|
if ((longlong)pr_immediate._int != (longlong)num)
|
|
{
|
|
if (((longlong)pr_immediate._int & LL(0xffffffff80000000)) != LL(0xffffffff80000000))
|
|
QCC_PR_ParseWarning(WARN_OVERFLOW, "numerical overflow");
|
|
}
|
|
}
|
|
goto checkjunk;
|
|
}
|
|
else
|
|
break;
|
|
pr_file_p++;
|
|
}
|
|
pr_token[tokenlen++] = 0;
|
|
|
|
if (!pr_immediate_type)
|
|
{
|
|
//float f = num;
|
|
if (flag_assume_integer)// || (base != 10 && sign > 0 && (long long)f != (long long)num))
|
|
pr_immediate_type = type_integer;
|
|
else if (flag_qccx && base == 16)
|
|
{
|
|
pr_immediate_type = type_float;
|
|
goto qccxhex;
|
|
}
|
|
else
|
|
pr_immediate_type = type_float;
|
|
}
|
|
|
|
if (pr_immediate_type == type_integer)
|
|
{
|
|
pr_immediate_type = type_integer;
|
|
qccxhex:
|
|
pr_immediate._int = num*sign;
|
|
|
|
num*=sign;
|
|
if ((longlong)pr_immediate._int != (longlong)num)
|
|
{
|
|
if (((longlong)pr_immediate._int & LL(0xffffffff80000000)) != LL(0xffffffff80000000))
|
|
QCC_PR_ParseWarning(WARN_OVERFLOW, "numerical overflow");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pr_immediate_type = type_float;
|
|
// at this point, we know there's no . in it, so the NaN bug shouldn't happen
|
|
// and we cannot use atof on tokens like 0xabc, so use num*sign, it SHOULD be safe
|
|
//pr_immediate._float = atof(pr_token);
|
|
pr_immediate._float = (float)(num*sign);
|
|
|
|
num*=sign;
|
|
if ((longlong)pr_immediate._float != (longlong)num && base == 16)
|
|
QCC_PR_ParseWarning(WARN_OVERFLOW, "numerical overflow %lld will be rounded to %f", num, pr_immediate._float);
|
|
}
|
|
|
|
checkjunk:
|
|
c = *pr_file_p;
|
|
if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') || (c & 0x80))
|
|
QCC_PR_ParseWarning(ERR_NOTANUMBER, "bad suffix on number %s", pr_token);
|
|
}
|
|
|
|
|
|
static float QCC_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;
|
|
} while ((c >= '0' && c<= '9') || (c == '.'&&pr_file_p[1]!='.')); //only allow a . if the next isn't too...
|
|
if (*pr_file_p == 'f')
|
|
pr_file_p++;
|
|
pr_token[len] = 0;
|
|
return (float)atof (pr_token);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexVector
|
|
|
|
Parses a single quoted vector
|
|
==============
|
|
*/
|
|
static void QCC_PR_LexVector (void)
|
|
{
|
|
int i;
|
|
|
|
pr_file_p++; //skip the leading ' char
|
|
|
|
if (*pr_file_p == '\\')
|
|
{//extended character constant
|
|
pr_file_p++;
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = QCC_PR_LexEscapedCodepoint();
|
|
if (*pr_file_p != '\'')
|
|
QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad character constant");
|
|
pr_file_p++;
|
|
return;
|
|
}
|
|
if ((unsigned char)*pr_file_p >= 0x80)
|
|
{
|
|
int b = utf8_check(pr_file_p, &pr_immediate._int); //utf-8 codepoint.
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
if (flag_qccx)
|
|
QCC_PR_ParseWarning(WARN_DENORMAL, "char constant: denormal");
|
|
else
|
|
pr_immediate._float = pr_immediate._int;
|
|
pr_file_p+=b+1;
|
|
return;
|
|
}
|
|
else if (pr_file_p[1] == '\'')
|
|
{//character constant
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
if (flag_qccx)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_DENORMAL, "char constant: denormal");
|
|
pr_immediate._int = pr_file_p[0];
|
|
}
|
|
else
|
|
pr_immediate._float = pr_file_p[0];
|
|
pr_file_p+=2;
|
|
return;
|
|
}
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_vector;
|
|
QCC_PR_LexWhitespace (false);
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
pr_immediate.vector[i] = QCC_PR_LexFloat ();
|
|
QCC_PR_LexWhitespace (false);
|
|
|
|
if (*pr_file_p == '\'' && i == 1)
|
|
{
|
|
if (i < 2)
|
|
QCC_PR_ParseWarning (WARN_FTE_SPECIFIC, "2d vector");
|
|
|
|
for (i++ ; i<3 ; i++)
|
|
pr_immediate.vector[i] = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (*pr_file_p != '\'')
|
|
QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad vector");
|
|
pr_file_p++;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexName
|
|
|
|
Parses an identifier
|
|
==============
|
|
*/
|
|
static void QCC_PR_LexName (void)
|
|
{
|
|
unsigned int c;
|
|
int len;
|
|
|
|
len = 0;
|
|
do
|
|
{
|
|
int b = utf8_check(pr_file_p, &c);
|
|
if (!b)
|
|
{
|
|
unsigned char lead = *pr_file_p++;
|
|
char *o;
|
|
while(*pr_file_p && !utf8_check(pr_file_p, &c))
|
|
pr_file_p++;
|
|
o = pr_file_p;
|
|
while (qcc_iswhite(*pr_file_p))
|
|
{
|
|
if (*pr_file_p == '\n')
|
|
break;
|
|
pr_file_p++;
|
|
}
|
|
if (*pr_file_p == '\n')
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Invalid UTF-8 code sequence at end of line. Lead byte was %#2x", lead);
|
|
else
|
|
{
|
|
len = 0;
|
|
while (*pr_file_p && !qcc_iswhite(*pr_file_p))
|
|
pr_token[len++] = *pr_file_p++;
|
|
pr_token[len++] = 0;
|
|
pr_file_p = o;
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Invalid UTF-8 code sequence before %s. Lead byte was %#2x", pr_token, lead);
|
|
}
|
|
return;
|
|
}
|
|
while(b-->0)
|
|
{
|
|
pr_token[len] = *pr_file_p++;
|
|
len++;
|
|
}
|
|
c = *pr_file_p;
|
|
} while ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
|
|
|| (c >= '0' && c <= '9') || (c & 0x80));
|
|
|
|
pr_token[len] = 0;
|
|
pr_token_type = tt_name;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexPunctuation
|
|
==============
|
|
*/
|
|
static void QCC_PR_LexPunctuation (void)
|
|
{
|
|
int i;
|
|
int len;
|
|
char *p;
|
|
|
|
pr_token_type = tt_punct;
|
|
|
|
if (pr_file_p[0] == '*' && pr_file_p[1] == '*' && flag_dblstarexp)
|
|
{ //for compat with gmqcc. fteqcc uses *^ internally (which does not conflict with multiplying by dereferenced pointers - sucks for MSCLR c++ syntax)
|
|
QCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, "** is unsafe around pointers, use *^ instead.", pr_token);
|
|
strcpy (pr_token, "*^");
|
|
pr_file_p += 2;
|
|
return;
|
|
}
|
|
|
|
for (i=0 ; (p = pr_punctuation[i]) != NULL ; i++)
|
|
{
|
|
len = strlen(p);
|
|
if (!strncmp(p, pr_file_p, len) )
|
|
{
|
|
strcpy (pr_token, pr_punctuationremap[i]);
|
|
if (p[0] == '{')
|
|
pr_bracelevel++;
|
|
else if (p[0] == '}')
|
|
pr_bracelevel--;
|
|
pr_file_p += len;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((unsigned char)*pr_file_p == (unsigned char)'\\' && pr_file_p[1] == '\r' && pr_file_p[2] == '\n')
|
|
pr_file_p+=3;
|
|
else if ((unsigned char)*pr_file_p == (unsigned char)'\\' && (pr_file_p[1] == '\r' || pr_file_p[1] == '\n'))
|
|
pr_file_p+=2;
|
|
else
|
|
{
|
|
if ((unsigned char)*pr_file_p == (unsigned char)0xa0)
|
|
QCC_PR_ParseWarning (ERR_UNKNOWNPUCTUATION, "Unsupported punctuation: '\\x%x' - non-breaking space", (unsigned char)*pr_file_p);
|
|
else
|
|
QCC_PR_ParseWarning (ERR_UNKNOWNPUCTUATION, "Unknown punctuation: '\\x%x'", *pr_file_p);
|
|
pr_file_p++;
|
|
}
|
|
|
|
QCC_PR_Lex();
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
PR_LexWhitespace
|
|
==============
|
|
*/
|
|
void QCC_PR_LexWhitespace (pbool inhibitpreprocessor)
|
|
{
|
|
int c;
|
|
|
|
while (1)
|
|
{
|
|
// skip whitespace
|
|
while ((c = *pr_file_p) && qcc_iswhite(c))
|
|
{
|
|
if (qcc_islineending(c, pr_file_p[1]))
|
|
{
|
|
pr_file_p++;
|
|
if (!inhibitpreprocessor)
|
|
QCC_PR_NewLine (false);
|
|
else
|
|
pr_source_line++;
|
|
if (!pr_file_p)
|
|
return;
|
|
}
|
|
else
|
|
pr_file_p++;
|
|
}
|
|
if (c == 0)
|
|
return; // end of file
|
|
|
|
// skip // comments
|
|
if (c=='/' && pr_file_p[1] == '/')
|
|
{
|
|
while (*pr_file_p && !qcc_islineending(pr_file_p[0], pr_file_p[1]))
|
|
pr_file_p++;
|
|
|
|
if (*pr_file_p)
|
|
pr_file_p++; //don't break on eof.
|
|
if (!inhibitpreprocessor)
|
|
QCC_PR_NewLine(false);
|
|
else
|
|
pr_source_line++;
|
|
continue;
|
|
}
|
|
|
|
// skip /* */ comments
|
|
if (c=='/' && pr_file_p[1] == '*')
|
|
{
|
|
pr_file_p+=1;
|
|
do
|
|
{
|
|
pr_file_p++;
|
|
if (qcc_islineending(pr_file_p[0], pr_file_p[1]))
|
|
{
|
|
if (!inhibitpreprocessor)
|
|
QCC_PR_NewLine(true);
|
|
else
|
|
pr_source_line++;
|
|
}
|
|
if (pr_file_p[1] == 0)
|
|
{
|
|
QCC_PR_ParseError(0, "EOF inside comment\n");
|
|
pr_file_p++;
|
|
return;
|
|
}
|
|
if (pr_file_p[0] == '/' && pr_file_p[1] == '*')
|
|
QCC_PR_ParseWarning(WARN_NESTEDCOMMENT, "\"/*\" inside comment");
|
|
} while (pr_file_p[0] != '*' || pr_file_p[1] != '/');
|
|
pr_file_p+=2;
|
|
continue;
|
|
}
|
|
|
|
break; // a real character has been found
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
#define MAX_FRAMES 8192
|
|
char pr_framemodelname[64];
|
|
struct
|
|
{
|
|
char name[64];
|
|
int value;
|
|
const char *file; //compare to s_filen to see if its current or not
|
|
} pr_framemacro[MAX_FRAMES];
|
|
int pr_nummacros;
|
|
int pr_macrovalue; //next value to use
|
|
int pr_savedmacro; //for sub-groups.
|
|
|
|
void QCC_PR_ClearGrabMacros (pbool newfile)
|
|
{
|
|
if (!newfile)
|
|
pr_nummacros = 0;
|
|
pr_macrovalue = 0;
|
|
pr_savedmacro = -1;
|
|
}
|
|
|
|
static int QCC_PR_FindMacro (char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i=pr_nummacros-1 ; i>=0 ; i--)
|
|
{
|
|
if (!STRCMP (name, pr_framemacro[i].name))
|
|
{
|
|
if (pr_framemacro[i].file != s_filen)
|
|
QCC_PR_ParseWarning(WARN_STALEMACRO, "Stale macro used (%s, defined in %s)", pr_token, pr_framemacro[i].file);
|
|
return pr_framemacro[i].value;
|
|
}
|
|
}
|
|
for (i=pr_nummacros-1 ; i>=0 ; i--)
|
|
{
|
|
if (!stricmp (name, pr_framemacro[i].name))
|
|
{
|
|
QCC_PR_ParseWarning(WARN_CASEINSENSITIVEFRAMEMACRO, "Case insensitive frame macro (using %s)", pr_framemacro[i].name);
|
|
if (pr_framemacro[i].file != s_filen)
|
|
QCC_PR_ParseWarning(WARN_STALEMACRO, "Stale macro used (%s, defined in %s)", pr_token, pr_framemacro[i].file);
|
|
return pr_framemacro[i].value;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void QCC_PR_ExpandMacro(void)
|
|
{
|
|
int i = QCC_PR_FindMacro(pr_token);
|
|
|
|
if (i < 0)
|
|
QCC_PR_ParseError (ERR_BADFRAMEMACRO, "Unknown frame macro $%s", pr_token);
|
|
|
|
QC_snprintfz(pr_token, sizeof(pr_token),"%d", i);
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = (float)i;
|
|
}
|
|
|
|
pbool QCC_PR_SimpleGetString(void)
|
|
{
|
|
int c;
|
|
int i = 0;
|
|
char *f;
|
|
|
|
pr_token[0] = 0;
|
|
|
|
// skip whitespace
|
|
while ((c = *pr_file_p) && qcc_iswhite(c))
|
|
{
|
|
if (c=='\n')
|
|
return false;
|
|
pr_file_p++;
|
|
}
|
|
if (c == 0) //eof
|
|
return false;
|
|
//abort if there's a comment.
|
|
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;
|
|
}
|
|
|
|
if (*pr_file_p != '\"')
|
|
return false; //nope, not a string.
|
|
f = pr_file_p+1;
|
|
while (*f)
|
|
{
|
|
if (*f == '\n' || !*f)
|
|
{ //bad string
|
|
QCC_Error (ERR_INTERNAL, "new line inside string");
|
|
pr_token[0] = 0;
|
|
return false;
|
|
}
|
|
if (*f == '\"')
|
|
{ //end-of-string
|
|
pr_token[i] = 0;
|
|
pr_file_p = f+1;
|
|
return false;
|
|
}
|
|
if (i == sizeof(qcc_token)-1)
|
|
QCC_Error (ERR_INTERNAL, "token exceeds %i chars", i);
|
|
if (*f == '\\')
|
|
{
|
|
f++;
|
|
if (!*f)
|
|
f = "";
|
|
else if (*f == 'n')
|
|
{
|
|
pr_token[i++] = '\n';
|
|
f++;
|
|
}
|
|
else if (*f == 'r')
|
|
{
|
|
pr_token[i++] = '\r';
|
|
f++;
|
|
}
|
|
else if (*f == 't')
|
|
{
|
|
pr_token[i++] = '\t';
|
|
f++;
|
|
}
|
|
else
|
|
pr_token[i++] = *f++;
|
|
}
|
|
else
|
|
pr_token[i++] = *f++;
|
|
}
|
|
return true;
|
|
}
|
|
// just parses text, returning false if an eol is reached
|
|
pbool QCC_PR_SimpleGetToken (void)
|
|
{
|
|
int c;
|
|
int i;
|
|
|
|
pr_token[0] = 0;
|
|
|
|
// skip whitespace
|
|
while ((c = *pr_file_p) && qcc_iswhite(c))
|
|
{
|
|
if (c=='\n')
|
|
return false;
|
|
pr_file_p++;
|
|
}
|
|
if (c == 0) //eof
|
|
return false;
|
|
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;
|
|
}
|
|
|
|
i = 0;
|
|
while ((c = *pr_file_p) && !qcc_iswhite(c) && c != ',' && c != ';' && c != ')' && c != '(' && c != ']' && !(c == '/' && pr_file_p[1] == '/'))
|
|
{
|
|
if (i == sizeof(qcc_token)-1)
|
|
QCC_Error (ERR_INTERNAL, "token exceeds %i chars", i);
|
|
pr_token[i] = c;
|
|
i++;
|
|
pr_file_p++;
|
|
}
|
|
pr_token[i] = 0;
|
|
return i!=0;
|
|
}
|
|
|
|
static pbool QCC_PR_LexMacroName(void)
|
|
{
|
|
int c;
|
|
int i;
|
|
|
|
pr_token[0] = 0;
|
|
|
|
// skip whitespace
|
|
while ((c = *pr_file_p) && qcc_iswhite(c))
|
|
{
|
|
if (c=='\n')
|
|
return false;
|
|
pr_file_p++;
|
|
}
|
|
if (!c)
|
|
return false;
|
|
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;
|
|
}
|
|
|
|
i = 0;
|
|
while ( (c = *pr_file_p) > ' ' && c != '\n' && c != ',' && c != ';' && c != '&' && c != '|' && c != ')' && c != '(' && c != ']' && !(pr_file_p[0] == '.' && pr_file_p[1] == '.'))
|
|
{
|
|
if (i == sizeof(qcc_token)-1)
|
|
QCC_Error (ERR_INTERNAL, "token exceeds %i chars", i);
|
|
pr_token[i] = c;
|
|
i++;
|
|
pr_file_p++;
|
|
}
|
|
pr_token[i] = 0;
|
|
return i!=0;
|
|
}
|
|
|
|
static void QCC_PR_MacroFrame(char *name, int value, pbool force)
|
|
{
|
|
int i;
|
|
for (i=pr_nummacros-1 ; i>=0 ; i--)
|
|
{
|
|
if (!STRCMP (name, pr_framemacro[i].name))
|
|
{
|
|
//vanilla macro behaviour is to not realise that there's dupes. lookups find the first, so dupes end up as dead gaps.
|
|
//our caller incremented the value externally
|
|
//so warn+ignore if its from the same file
|
|
if (pr_framemacro[i].file == s_filen && !force)
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEMACRO, "Duplicate macro defined (%s). Rename it.", pr_token);
|
|
else
|
|
{
|
|
pr_framemacro[i].value = value; //old file, override it, whatever the old value was is redundant now
|
|
pr_framemacro[i].file = s_filen;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (strlen(name)+1 > sizeof(pr_framemacro[0].name))
|
|
QCC_PR_ParseWarning(ERR_TOOMANYFRAMEMACROS, "Name for frame macro %s is too long", name);
|
|
else
|
|
{
|
|
strcpy (pr_framemacro[pr_nummacros].name, name);
|
|
pr_framemacro[pr_nummacros].value = value;
|
|
pr_framemacro[pr_nummacros].file = s_filen;
|
|
pr_nummacros++;
|
|
if (pr_nummacros >= MAX_FRAMES)
|
|
QCC_PR_ParseError(ERR_TOOMANYFRAMEMACROS, "Too many frame macros defined");
|
|
}
|
|
}
|
|
|
|
static void QCC_PR_ParseFrame (void)
|
|
{
|
|
while (QCC_PR_LexMacroName ())
|
|
{
|
|
QCC_PR_MacroFrame(pr_token, pr_macrovalue++, false);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexGrab
|
|
|
|
Deals with counting sequence numbers and replacing frame macros
|
|
==============
|
|
*/
|
|
static void QCC_PR_LexGrab (void)
|
|
{
|
|
pr_file_p++; // skip the $
|
|
// if (!QCC_PR_SimpleGetToken ())
|
|
// QCC_PR_ParseError ("hanging $");
|
|
if (qcc_iswhite(*pr_file_p))
|
|
QCC_PR_ParseError (ERR_BADFRAMEMACRO, "hanging $");
|
|
QCC_PR_LexMacroName();
|
|
if (!*pr_token)
|
|
QCC_PR_ParseError (ERR_BADFRAMEMACRO, "hanging $");
|
|
|
|
// check for $frame
|
|
if (!STRCMP (pr_token, "frame") || !STRCMP (pr_token, "framesave"))
|
|
{
|
|
QCC_PR_ParseFrame ();
|
|
QCC_PR_Lex ();
|
|
}
|
|
// ignore other known $commands - just for model/spritegen
|
|
else if (!STRCMP (pr_token, "cd")
|
|
|| !STRCMP (pr_token, "origin")
|
|
|| !STRCMP (pr_token, "base")
|
|
|| !STRCMP (pr_token, "flags")
|
|
|| !STRCMP (pr_token, "scale")
|
|
|| !STRCMP (pr_token, "skin") )
|
|
{ // skip to end of line
|
|
while (QCC_PR_LexMacroName ())
|
|
;
|
|
QCC_PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "flush"))
|
|
{
|
|
QCC_PR_ClearGrabMacros(true);
|
|
while (QCC_PR_LexMacroName ())
|
|
;
|
|
QCC_PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "frame_reset"))
|
|
{ //for compat with qfcc. full reset of all frame macros.
|
|
QCC_PR_ClearGrabMacros(false);
|
|
while (QCC_PR_LexMacroName ())
|
|
;
|
|
QCC_PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "framevalue"))
|
|
{
|
|
QCC_PR_LexMacroName ();
|
|
pr_macrovalue = atoi(pr_token);
|
|
|
|
QCC_PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "framerestore"))
|
|
{
|
|
QCC_PR_LexMacroName ();
|
|
QCC_PR_ExpandMacro();
|
|
pr_macrovalue = (int)pr_immediate._float;
|
|
|
|
QCC_PR_Lex ();
|
|
}
|
|
else if (!STRCMP (pr_token, "modelname"))
|
|
{
|
|
int i;
|
|
QCC_PR_LexMacroName ();
|
|
|
|
if (*pr_framemodelname)
|
|
QCC_PR_MacroFrame(pr_framemodelname, pr_macrovalue, true);
|
|
|
|
if (!QC_strlcpy(pr_framemodelname, pr_token, sizeof(pr_framemodelname)))
|
|
QCC_PR_ParseWarning (WARN_STRINGTOOLONG, "$modelname name too long");
|
|
|
|
i = QCC_PR_FindMacro(pr_framemodelname);
|
|
if (i)
|
|
pr_macrovalue = i;
|
|
else
|
|
i = 0;
|
|
|
|
QCC_PR_Lex ();
|
|
}
|
|
// look for a frame name macro
|
|
else
|
|
QCC_PR_ExpandMacro ();
|
|
}
|
|
|
|
//===========================
|
|
//compiler constants - dmw
|
|
|
|
pbool QCC_PR_UndefineName(const char *name)
|
|
{
|
|
// int a;
|
|
CompilerConstant_t *c;
|
|
c = pHash_Get(&compconstantstable, name);
|
|
if (!c)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_UNDEFNOTDEFINED, "Precompiler constant %s was not defined", name);
|
|
return false;
|
|
}
|
|
|
|
Hash_Remove(&compconstantstable, name);
|
|
return true;
|
|
}
|
|
|
|
CompilerConstant_t *QCC_PR_DefineName(const char *name)
|
|
{
|
|
int i;
|
|
CompilerConstant_t *cnst;
|
|
|
|
if (strlen(name) >= MAXCONSTANTNAMELENGTH || !*name)
|
|
QCC_PR_ParseError(ERR_NAMETOOLONG, "Compiler constant name length is too long or short");
|
|
|
|
cnst = pHash_Get(&compconstantstable, name);
|
|
if (cnst)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "Duplicate definition for Precompiler constant %s", name);
|
|
Hash_Remove(&compconstantstable, name);
|
|
}
|
|
|
|
cnst = qccHunkAlloc(sizeof(CompilerConstant_t));
|
|
|
|
cnst->used = false;
|
|
cnst->numparams = -1;
|
|
cnst->evil = false;
|
|
strcpy(cnst->name, name);
|
|
cnst->namelen = strlen(name);
|
|
cnst->value = cnst->name + strlen(cnst->name);
|
|
for (i = 0; i < MAXCONSTANTPARAMS; i++)
|
|
cnst->params[i][0] = '\0';
|
|
|
|
pHash_Add(&compconstantstable, cnst->name, cnst, qccHunkAlloc(sizeof(bucket_t)));
|
|
|
|
return cnst;
|
|
}
|
|
|
|
void QCC_PR_PreProcessor_Define(pbool append)
|
|
{
|
|
char *d;
|
|
char *dbuf;
|
|
int dbuflen;
|
|
char *s;
|
|
int quote=false;
|
|
pbool preprocessorhack = false;
|
|
CompilerConstant_t *cnst, *oldcnst;
|
|
|
|
QCC_PR_SimpleGetToken ();
|
|
|
|
if (!QCC_PR_SimpleGetToken ())
|
|
QCC_PR_ParseError(ERR_NONAME, "No name defined for compiler constant");
|
|
|
|
oldcnst = pHash_Get(&compconstantstable, pr_token);
|
|
if (oldcnst)
|
|
Hash_Remove(&compconstantstable, oldcnst->name);
|
|
|
|
cnst = QCC_PR_DefineName(pr_token);
|
|
|
|
if (*pr_file_p == '(')
|
|
{
|
|
cnst->numparams = 0;
|
|
pr_file_p++;
|
|
while(qcc_iswhitesameline(*pr_file_p))
|
|
pr_file_p++;
|
|
s = pr_file_p;
|
|
for (;;)
|
|
{
|
|
if (*pr_file_p == ',' || *pr_file_p == ')')
|
|
{
|
|
int nl;
|
|
nl = pr_file_p-s;
|
|
while(qcc_iswhitesameline(s[nl]))
|
|
nl--;
|
|
if (cnst->numparams >= MAXCONSTANTPARAMS)
|
|
QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "May not have more than %i parameters to a macro", MAXCONSTANTPARAMS);
|
|
if (nl >= MAXCONSTANTPARAMLENGTH)
|
|
QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "parameter name is too long (max %i)", MAXCONSTANTPARAMLENGTH);
|
|
if (nl == 3 && s[0] == '.' && s[1] == '.' && s[2] == '.')
|
|
{
|
|
cnst->varg = true;
|
|
if (*pr_file_p != ')')
|
|
QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "varadic argument must be last");
|
|
}
|
|
else
|
|
{
|
|
memcpy(cnst->params[cnst->numparams], s, nl);
|
|
cnst->params[cnst->numparams][nl] = '\0';
|
|
for (nl = 0; nl < cnst->numparams; nl++)
|
|
{
|
|
if (!strcmp(cnst->params[nl], cnst->params[cnst->numparams]))
|
|
QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "duplicate macro paramter name '%s'", cnst->params[nl]);
|
|
}
|
|
cnst->numparams++;
|
|
}
|
|
if (*pr_file_p++ == ')')
|
|
break;
|
|
while(qcc_iswhitesameline(*pr_file_p))
|
|
pr_file_p++;
|
|
s = pr_file_p;
|
|
}
|
|
if(!*pr_file_p++)
|
|
{
|
|
QCC_PR_ParseError(ERR_EXPECTED, "missing ) in macro parameter list");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else cnst->numparams = -1;
|
|
|
|
//disable append mode if they're trying to do something stupid
|
|
if (append)
|
|
{
|
|
if (!oldcnst)
|
|
append = false; //append with no previous define is treated as just a regular define. huzzah.
|
|
else if (cnst->numparams != oldcnst->numparams || cnst->varg != oldcnst->varg)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, "different number of macro arguments in macro append");
|
|
append = false;
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
//arguments need to be specified, if only so that appends with arguments are still vaugely readable.
|
|
//argument names need to match because the expansion is too lame to cope if they're different.
|
|
for (i = 0; i < cnst->numparams; i++)
|
|
{
|
|
if (strcmp(cnst->params[i], oldcnst->params[i]))
|
|
break;
|
|
}
|
|
if (i < cnst->numparams)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, "arguments differ in macro append");
|
|
append = false;
|
|
}
|
|
else
|
|
append = true;
|
|
}
|
|
}
|
|
|
|
s = pr_file_p;
|
|
d = dbuf = NULL;
|
|
dbuflen = 0;
|
|
|
|
if (append)
|
|
{
|
|
//start with the old value
|
|
int olen = strlen(oldcnst->value);
|
|
dbuflen = olen + 128;
|
|
dbuf = qccHunkAlloc(dbuflen);
|
|
memcpy(dbuf, oldcnst->value, olen);
|
|
d = dbuf + olen;
|
|
*d++ = ' ';
|
|
}
|
|
|
|
cnst->fromfile = s_filen;
|
|
cnst->fromline = pr_source_line;
|
|
|
|
while(*s == ' ' || *s == '\t')
|
|
s++;
|
|
while(1)
|
|
{
|
|
if ((d - dbuf) + 2 >= dbuflen)
|
|
{
|
|
int len = d - dbuf;
|
|
dbuflen = (len+128) * 2;
|
|
dbuf = qccHunkAlloc(dbuflen);
|
|
memcpy(dbuf, d - len, len);
|
|
d = dbuf + len;
|
|
}
|
|
|
|
if( *s == '\\' )
|
|
{
|
|
// read over a newline if necessary
|
|
if( s[1] == '\n' || s[1] == '\r' )
|
|
{
|
|
char *exploitcheck;
|
|
s++; //skip the \ char
|
|
if (*s == '\r' && s[1] == '\n')
|
|
s++; //skip the \r. the \n will become part of the macro.
|
|
s++; //skip the \n
|
|
QCC_PR_NewLine(true);
|
|
|
|
/*
|
|
This began as a bug. It is still evil, but its oh so useful.
|
|
|
|
In C,
|
|
#define foobar \
|
|
foo\
|
|
bar\
|
|
moo
|
|
|
|
becomes foobarmoo, not foo\nbar\nmoo
|
|
|
|
#define hacks however, require that it becomes
|
|
foo\nbar\nmoo
|
|
|
|
# cannot be used on the first line of the macro, and then is only valid as the first non-white char of the following lines
|
|
so if present, the preceeding \\\n and following \\\n must become an actual \n instead of being stripped.
|
|
*/
|
|
|
|
for (exploitcheck = s; *exploitcheck && qcc_iswhitesameline(*exploitcheck); exploitcheck++)
|
|
;
|
|
if (*exploitcheck == '#')
|
|
{
|
|
*d++ = '\n';
|
|
if (!cnst->evil)
|
|
QCC_PR_ParseWarning(WARN_EVILPREPROCESSOR, "preprocessor directive within preprocessor macro %s", cnst->name);
|
|
cnst->evil = true;
|
|
preprocessorhack = true;
|
|
}
|
|
else if (preprocessorhack)
|
|
{
|
|
*d++ = '\n';
|
|
preprocessorhack = false;
|
|
}
|
|
}
|
|
}
|
|
else if(*s == '\r' || *s == '\n' || *s == '\0')
|
|
{
|
|
break;
|
|
}
|
|
if (!quote && s[0]=='/'&&s[1]=='/')
|
|
break; //c++ style comments can just be ignored
|
|
if (!quote && s[0]=='/'&&s[1]=='*')
|
|
{ //multi-line c style comments become part of the define itself. this also negates the need for \ at the end of lines.
|
|
//although we don't bother embedding.
|
|
s+=2;
|
|
for(;;)
|
|
{
|
|
if (!s[0])
|
|
{
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, "EOF inside quote in define %s", cnst->name);
|
|
break;
|
|
}
|
|
if (s[0]=='*'&&s[1]=='/')
|
|
{
|
|
s+=2;
|
|
break;
|
|
}
|
|
if (s[0] == '\n')
|
|
pr_source_line++;
|
|
s++;
|
|
}
|
|
continue;
|
|
}
|
|
if (*s == '\"')
|
|
quote=!quote;
|
|
|
|
*d = *s;
|
|
d++;
|
|
s++;
|
|
}
|
|
*d = '\0';
|
|
|
|
cnst->value = dbuf;
|
|
|
|
if (oldcnst && !append)
|
|
{ //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 (strcmp(oldcnst->value, cnst->value))
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, "Alternate precompiler definition of %s (%s -> %s)", pr_token, oldcnst->value, cnst->value);
|
|
else
|
|
QCC_PR_ParseWarning(WARN_IDENTICALPRECOMPILER, "Identical precompiler definition of %s", pr_token);
|
|
}
|
|
|
|
pr_file_p = s;
|
|
}
|
|
|
|
/* *buffer, *bufferlen and *buffermax should be NULL/0 at the start */
|
|
static void QCC_PR_ExpandStrCat(char **buffer, size_t *bufferlen, size_t *buffermax, char *newdata, size_t newlen)
|
|
{
|
|
size_t newmax = *bufferlen + newlen;
|
|
|
|
if (newmax < *bufferlen)//check for overflow
|
|
{
|
|
QCC_PR_ParseWarning(ERR_INTERNAL, "exceeds 4gb");
|
|
return;
|
|
}
|
|
if (newmax > *buffermax)
|
|
{
|
|
char *newbuf;
|
|
if (newmax < 64)
|
|
newmax = 64;
|
|
if (newmax < *bufferlen * 2)
|
|
{
|
|
newmax = *bufferlen * 2;
|
|
if (newmax < *bufferlen) /*overflowed?*/
|
|
{
|
|
QCC_PR_ParseWarning(ERR_INTERNAL, "exceeds 4gb");
|
|
return;
|
|
}
|
|
}
|
|
newbuf = realloc(*buffer, newmax);
|
|
if (!newbuf)
|
|
{
|
|
QCC_PR_ParseWarning(ERR_INTERNAL, "out of memory");
|
|
return; /*OOM*/
|
|
}
|
|
*buffer = newbuf;
|
|
*buffermax = newmax;
|
|
}
|
|
memcpy(*buffer + *bufferlen, newdata, newlen);
|
|
*bufferlen += newlen;
|
|
/*no null terminator, remember to cat one if required*/
|
|
}
|
|
/* *buffer, *bufferlen and *buffermax should be NULL/0 at the start */
|
|
static void QCC_PR_ExpandStrCatMarkup(char **buffer, size_t *bufferlen, size_t *buffermax, char *newdata, size_t newlen)
|
|
{
|
|
size_t newmax = *bufferlen + newlen*2;
|
|
|
|
if (newmax < *bufferlen)//check for overflow
|
|
{
|
|
QCC_PR_ParseWarning(ERR_INTERNAL, "exceeds 4gb");
|
|
return;
|
|
}
|
|
if (newmax > *buffermax)
|
|
{
|
|
char *newbuf;
|
|
if (newmax < 64)
|
|
newmax = 64;
|
|
if (newmax < *bufferlen * 2)
|
|
{
|
|
newmax = *bufferlen * 2;
|
|
if (newmax < *bufferlen) /*overflowed?*/
|
|
{
|
|
QCC_PR_ParseWarning(ERR_INTERNAL, "exceeds 4gb");
|
|
return;
|
|
}
|
|
}
|
|
newbuf = realloc(*buffer, newmax);
|
|
if (!newbuf)
|
|
{
|
|
QCC_PR_ParseWarning(ERR_INTERNAL, "out of memory");
|
|
return; /*OOM*/
|
|
}
|
|
*buffer = newbuf;
|
|
*buffermax = newmax;
|
|
}
|
|
|
|
while (newlen--)
|
|
{
|
|
if (*newdata == '\n')
|
|
{
|
|
(*buffer)[*bufferlen+0] = '\\';
|
|
(*buffer)[*bufferlen+1] = '\n';
|
|
*bufferlen += 2;
|
|
}
|
|
else if (*newdata == '\\')
|
|
{
|
|
(*buffer)[*bufferlen+0] = '\\';
|
|
(*buffer)[*bufferlen+1] = '\\';
|
|
*bufferlen += 2;
|
|
}
|
|
else if (*newdata == '\0')
|
|
{
|
|
(*buffer)[*bufferlen+0] = '\\';
|
|
(*buffer)[*bufferlen+1] = '0';
|
|
*bufferlen += 2;
|
|
}
|
|
else if (*newdata == '\"')
|
|
{
|
|
(*buffer)[*bufferlen+0] = '\\';
|
|
(*buffer)[*bufferlen+1] = '\"';
|
|
*bufferlen += 2;
|
|
}
|
|
else
|
|
{
|
|
(*buffer)[*bufferlen] = *newdata;
|
|
*bufferlen += 1;
|
|
}
|
|
newdata++;
|
|
}
|
|
/*no null terminator, remember to cat one if required*/
|
|
}
|
|
|
|
static const struct tm *QCC_CurrentTime(void)
|
|
{
|
|
//if SOURCE_DATE_EPOCH environment is defined, use that as seconds from epoch (and show utc)
|
|
//this helps give reproducable builds (which is for some debian project, demonstrating that noone is hacking binaries).
|
|
const char *env = getenv("SOURCE_DATE_EPOCH");
|
|
time_t t;
|
|
if (env && *env)
|
|
{
|
|
t = strtoull(env, NULL, 0);
|
|
if (t)
|
|
return gmtime(&t);
|
|
}
|
|
|
|
t = time(NULL);
|
|
return localtime(&t);
|
|
}
|
|
|
|
static char *QCC_PR_PopenMacro(const char *macroname, const char *cmd, char *retbuf, size_t retbufsize)
|
|
{
|
|
char *ret = retbuf;
|
|
char temp[65536], *t = temp;
|
|
#ifdef _WIN32
|
|
FILE *f = _popen(cmd, "rt");
|
|
#else
|
|
FILE *f = popen(cmd, "r");
|
|
#endif
|
|
int len;
|
|
if (!f)
|
|
{
|
|
QCC_PR_ParseWarning(ERR_CONSTANTNOTDEFINED, "%s: Unable to execute \"%s\" for value", macroname, cmd);
|
|
return NULL;
|
|
}
|
|
retbufsize-=3; // '""\0'
|
|
*retbuf++ = '\"';
|
|
for (;;)
|
|
{
|
|
len = fread(temp, 1, sizeof(temp), f);
|
|
if (len <= 0)
|
|
break;
|
|
else for (t=temp; len --> 0 && *t && retbufsize > 1; t++)
|
|
{
|
|
if (*t == '\"') retbuf[1] = '\"';
|
|
else if (*t == '\n') retbuf[1] = 'n';
|
|
else if (*t == '\r') retbuf[1] = 'r';
|
|
else if (*t == '#') retbuf[1] = '#'; //so we don't get preqcc-style expansion in strings.
|
|
else
|
|
{
|
|
*retbuf++ = *t;
|
|
retbufsize--;
|
|
continue;
|
|
}
|
|
retbuf[0] = '\\';
|
|
retbuf+=2;
|
|
retbufsize-=2;
|
|
}
|
|
}
|
|
if (retbuf[-1] == 'n' && retbuf[-2] == '\\')
|
|
retbuf -= 2;
|
|
if (retbuf[-1] == 'r' && retbuf[-2] == '\\')
|
|
retbuf -= 2;
|
|
*retbuf++ = '\"';
|
|
*retbuf++ = 0;
|
|
#ifdef _WIN32
|
|
_pclose(f);
|
|
#else
|
|
pclose(f);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t retbufsize)
|
|
{
|
|
if (constname[0] != '_' || constname[1] != '_')
|
|
return NULL;
|
|
if (!strcmp(constname, "__TIME__"))
|
|
{
|
|
strftime( retbuf, retbufsize, "\"%H:%M\"", QCC_CurrentTime());
|
|
return retbuf;
|
|
}
|
|
if (!strcmp(constname, "__DATE__"))
|
|
{
|
|
strftime( retbuf, retbufsize, "\"%a %d %b %Y\"", QCC_CurrentTime());
|
|
return retbuf;
|
|
}
|
|
if (!strcmp(constname, "__GITURL__"))
|
|
return QCC_PR_PopenMacro(constname, "git remote get-url origin", retbuf, retbufsize); //some git url...
|
|
if (!strcmp(constname, "__GITHASH__"))
|
|
return QCC_PR_PopenMacro(constname, "git log -1 --format=%H", retbuf, retbufsize); //just a hash
|
|
if (!strcmp(constname, "__GITDATE__"))
|
|
return QCC_PR_PopenMacro(constname, "git log -1 --format=%cs", retbuf, retbufsize); //YYYY-MM-DD
|
|
if (!strcmp(constname, "__GITDATETIME__"))
|
|
return QCC_PR_PopenMacro(constname, "git log -1 --format=%ci", retbuf, retbufsize); //YYYY-MM-DD HH:MM:SS +TZ
|
|
if (!strcmp(constname, "__GITDESC__"))
|
|
return QCC_PR_PopenMacro(constname, "git describe", retbuf, retbufsize);
|
|
if (!strcmp(constname, "__RAND__"))
|
|
{
|
|
QC_snprintfz(retbuf, retbufsize, "%i", rand());
|
|
return retbuf;
|
|
}
|
|
#if defined(SVNREVISION) && defined(SVNDATE)
|
|
if (!strcmp(constname, "__QCCREV__"))
|
|
return STRINGIFY(SVNREVISION); //no M or anything, so you can compare revisions properly
|
|
#endif
|
|
if (!strcmp(constname, "__QCCVER__"))
|
|
{
|
|
#if defined(SVNREVISION) && defined(SVNDATE)
|
|
return "\"FTEQCC " STRINGIFY(SVNREVISION) " "STRINGIFY(SVNDATE)"\"";
|
|
#elif defined(SVNREVISION)
|
|
return "\"FTEQCC " STRINGIFY(SVNREVISION) " "__DATE__"\"";
|
|
#else
|
|
return "\""__DATE__"\"";
|
|
#endif
|
|
}
|
|
if (!strcmp(constname, "__FILE__"))
|
|
{
|
|
char *erk;
|
|
QC_snprintfz(retbuf, retbufsize, "\"%s\"", s_filen);
|
|
erk = strchr(retbuf, ':');
|
|
if (erk)
|
|
{
|
|
erk[0] = '\"';
|
|
erk[1] = 0;
|
|
}
|
|
return retbuf;
|
|
}
|
|
if (!strcmp(constname, "__LINE__"))
|
|
{
|
|
QC_snprintfz(retbuf, retbufsize, "%i", pr_source_line);
|
|
return retbuf;
|
|
}
|
|
if (!strcmp(constname, "__LINESTR__"))
|
|
{
|
|
QC_snprintfz(retbuf, retbufsize, "\"%i\"", pr_source_line);
|
|
return retbuf;
|
|
}
|
|
if (!strcmp(constname, "__FUNC__") || !strcmp(constname, "__func__"))
|
|
{
|
|
QC_snprintfz(retbuf, retbufsize, "\"%s\"",pr_scope?pr_scope->name:"<NO FUNCTION>");
|
|
return retbuf;
|
|
}
|
|
if (!strcmp(constname, "__NULL__"))
|
|
{
|
|
return "0i";
|
|
}
|
|
return NULL; //didn't match
|
|
}
|
|
|
|
static pbool QCC_PR_ExpandPreProcessorMacro(CompilerConstant_t *c, char **buffer, size_t *bufferlen, size_t *buffermax)
|
|
{
|
|
int p;
|
|
char *start;
|
|
char *starttok;
|
|
char *argsend;
|
|
int argsendline;
|
|
size_t whitestart;
|
|
char *paramoffset[MAXCONSTANTPARAMS+1];
|
|
int param=0, extraparam=0;
|
|
int plevel=0;
|
|
pbool noargexpand;
|
|
|
|
char *end;
|
|
char retbuf[256];
|
|
|
|
pr_file_p++;
|
|
QCC_PR_LexWhitespace(false);
|
|
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 == ')'))
|
|
{
|
|
if (*pr_file_p == ',' && c->varg && param >= c->numparams)
|
|
extraparam++; //skip extra trailing , arguments if we're varging.
|
|
else
|
|
{
|
|
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++;
|
|
QCC_PR_LexWhitespace(false);
|
|
start = 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 == MAXCONSTANTPARAMS || param > c->numparams)
|
|
QCC_PR_ParseError(ERR_TOOMANYPARAMS, "Too many parameters in macro call");
|
|
}
|
|
} else if (*pr_file_p == ')' )
|
|
plevel--;
|
|
else if(*pr_file_p == '\n')
|
|
QCC_PR_NewLine(false);
|
|
|
|
// see that *pr_file_p = '\0' up there? Must ++ BEFORE checking for !*pr_file_p
|
|
pr_file_p++;
|
|
if (!*pr_file_p)
|
|
QCC_PR_ParseError(ERR_EOF, "EOF on macro call");
|
|
}
|
|
if (param < c->numparams)
|
|
QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Not enough macro parameters");
|
|
paramoffset[param] = start;
|
|
|
|
*buffer = NULL;
|
|
*bufferlen = 0;
|
|
*buffermax = 0;
|
|
|
|
// QCC_PR_LexWhitespace(false);
|
|
argsend = pr_file_p;
|
|
argsendline = pr_source_line;
|
|
pr_file_p = c->value;
|
|
for(;;)
|
|
{
|
|
noargexpand = false;
|
|
whitestart = *bufferlen;
|
|
starttok = pr_file_p;
|
|
/*while(qcc_iswhite(*pr_file_p)) //copy across whitespace
|
|
{
|
|
if (!*pr_file_p)
|
|
break;
|
|
pr_file_p++;
|
|
}*/
|
|
QCC_PR_LexWhitespace(true);
|
|
if (starttok != pr_file_p)
|
|
{
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, starttok, pr_file_p - starttok);
|
|
}
|
|
|
|
if(*pr_file_p == '\"')
|
|
{
|
|
starttok = 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 == '\"')
|
|
pr_file_p++;
|
|
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, starttok, pr_file_p - starttok);
|
|
continue;
|
|
}
|
|
else if (*pr_file_p == '#') //if you ask for #a##b you will be shot. use #a #b instead, or chain macros.
|
|
{
|
|
if (pr_file_p[1] == '#')
|
|
{ //concatinate (strip out whitespace before the token)
|
|
*bufferlen = whitestart;
|
|
pr_file_p+=2;
|
|
noargexpand = true;
|
|
}
|
|
else
|
|
{ //stringify
|
|
pr_file_p++;
|
|
pr_file_p = QCC_COM_Parse2(pr_file_p);
|
|
if (!pr_file_p)
|
|
break;
|
|
|
|
for (p = 0; p < param; p++)
|
|
{
|
|
if (!STRCMP(qcc_token, c->params[p]))
|
|
{
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, "\"", 1);
|
|
QCC_PR_ExpandStrCatMarkup(buffer,bufferlen,buffermax, paramoffset[p], strlen(paramoffset[p]));
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, "\"", 1);
|
|
break;
|
|
}
|
|
}
|
|
if (p == param)
|
|
{
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, "#", 1);
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, qcc_token, strlen(qcc_token));
|
|
if (!c->evil)
|
|
QCC_PR_ParseWarning(0, "'#%s' is not a macro parameter in %s", qcc_token, c->name);
|
|
}
|
|
continue; //already did this one
|
|
}
|
|
}
|
|
|
|
end = qcc_token;
|
|
pr_file_p = QCC_COM_Parse2(pr_file_p);
|
|
if (!pr_file_p)
|
|
break;
|
|
|
|
for (p = 0; p < c->numparams; p++)
|
|
{
|
|
if (!STRCMP(qcc_token, c->params[p]))
|
|
{
|
|
char *argstart, *argend;
|
|
|
|
for (start = pr_file_p; qcc_iswhite(*start); start++)
|
|
;
|
|
if (noargexpand || (start[0] == '#' && start[1] == '#'))
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, paramoffset[p], strlen(paramoffset[p]));
|
|
else
|
|
{
|
|
for (argstart = paramoffset[p]; *argstart; argstart = argend)
|
|
{
|
|
argend = argstart;
|
|
while (qcc_iswhite(*argend))
|
|
argend++;
|
|
if (*argend == '\"')
|
|
{
|
|
do
|
|
{
|
|
argend++;
|
|
} while( (argend[-1] == '\\' || argend[0] != '\"') && *argend && *argend != '\n' );
|
|
if(*argend == '\"')
|
|
argend++;
|
|
end = NULL;
|
|
}
|
|
else
|
|
{
|
|
argend = QCC_COM_Parse2(argend);
|
|
if (!argend)
|
|
break;
|
|
end = QCC_PR_CheckBuiltinCompConst(qcc_token, retbuf, sizeof(retbuf));
|
|
}
|
|
//FIXME: we should be testing all defines instead of just built-in ones.
|
|
if (end)
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, end, strlen(end));
|
|
else
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, argstart, argend-argstart);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (c->varg && !STRCMP(qcc_token, "__VA_ARGS__"))
|
|
{ //c99
|
|
if (param-1 == c->numparams)
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, paramoffset[c->numparams], strlen(paramoffset[c->numparams]));
|
|
else if (noargexpand)
|
|
{
|
|
if(*bufferlen>0 && (*buffer)[*bufferlen-1] == ',')
|
|
*bufferlen-=1;
|
|
}
|
|
}
|
|
else if (c->varg && !STRCMP(qcc_token, "__VA_COUNT__"))
|
|
{ //not c99
|
|
char tmp[64];
|
|
if (param < c->numparams)
|
|
QCC_PR_ParseError(ERR_TOOFEWPARAMS, "__VA_COUNT__ without any variable args");
|
|
QC_snprintfz(tmp, sizeof(tmp), "%i", param-1+extraparam);
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, tmp, strlen(tmp));
|
|
}
|
|
else if (p == c->numparams)
|
|
{
|
|
/*CompilerConstant_t *c2 = pHash_Get(&compconstantstable, qcc_token);
|
|
if (c2 && c2->numparams >= 0 && *pr_file_p == '(')
|
|
{ //oh dear god this is vile bullshit
|
|
char *sub = NULL;
|
|
size_t sublen;
|
|
size_t submax;
|
|
pr_file_p++;
|
|
if (QCC_PR_ExpandPreProcessorMacro(c, &sub,&sublen,&submax))
|
|
{
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, sub, sublen);
|
|
}
|
|
free(sub);
|
|
}
|
|
else*/
|
|
QCC_PR_ExpandStrCat(buffer,bufferlen,buffermax, qcc_token, strlen(qcc_token));
|
|
}
|
|
}
|
|
|
|
for (p = 0; p < param-1; p++)
|
|
paramoffset[p][strlen(paramoffset[p])] = ',';
|
|
paramoffset[p][strlen(paramoffset[p])] = ')';
|
|
|
|
if (c->inside>8)
|
|
return false;
|
|
|
|
pr_file_p = argsend;
|
|
pr_source_line = argsendline;
|
|
|
|
if (flag_debugmacros)
|
|
{
|
|
if (flag_msvcstyle)
|
|
externs->Printf ("%s(%i) : macro %s: %s\n", s_filen, pr_source_line, c->name, pr_file_p);
|
|
else
|
|
externs->Printf ("%s:%i: macro %s: %s\n", s_filen, pr_source_line, c->name, pr_file_p);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int QCC_PR_CheckCompConst(void)
|
|
{
|
|
//FIXME: FOO(SPLAT(FOO(BAR)))
|
|
//the above should expand the inner foo before expanding the outer foo
|
|
|
|
char *initial_file_p = pr_file_p;
|
|
int initial_line = pr_source_line;
|
|
|
|
CompilerConstant_t *c;
|
|
|
|
char *end, *tok;
|
|
char retbuf[256];
|
|
|
|
for (end = pr_file_p; ; end++)
|
|
{
|
|
if (!*end || qcc_iswhite(*end))
|
|
break;
|
|
|
|
if (*end == ')'
|
|
|| *end == '('
|
|
|| *end == '+'
|
|
|| *end == '-'
|
|
|| *end == '*'
|
|
|| *end == '/'
|
|
|| *end == '|'
|
|
|| *end == '&'
|
|
|| *end == '='
|
|
|| *end == '^'
|
|
|| *end == '~'
|
|
|| *end == '['
|
|
|| *end == ']'
|
|
|| *end == '\"'
|
|
|| *end == '{'
|
|
|| *end == '}'
|
|
|| *end == ';'
|
|
|| *end == ':'
|
|
|| *end == ','
|
|
|| *end == '.'
|
|
|| *end == '#')
|
|
break;
|
|
}
|
|
if (!QC_strnlcpy(pr_token, pr_file_p, end-pr_file_p, sizeof(pr_token)))
|
|
return false; //name too long to be a valid macro
|
|
|
|
// externs->Printf("%s\n", pr_token);
|
|
c = pHash_Get(&compconstantstable, pr_token);
|
|
|
|
if (c && (!currentchunk || currentchunk->cnst != c)) //macros don't expand themselves
|
|
{
|
|
pr_file_p = initial_file_p+strlen(c->name);
|
|
while(*pr_file_p == ' ' || *pr_file_p == '\t')
|
|
pr_file_p++;
|
|
if (c->numparams>=0)
|
|
{
|
|
if (*pr_file_p == '(')
|
|
{
|
|
char *buffer = NULL;
|
|
size_t bufferlen, buffermax;
|
|
if (!QCC_PR_ExpandPreProcessorMacro(c, &buffer, &bufferlen, &buffermax))
|
|
{
|
|
free(buffer);
|
|
pr_file_p = initial_file_p;
|
|
pr_source_line = initial_line;
|
|
return false;
|
|
}
|
|
|
|
if (!bufferlen)
|
|
expandedemptymacro = true;
|
|
else
|
|
{
|
|
QCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax, "\0", 1);
|
|
QCC_PR_IncludeChunkEx(buffer, true, NULL, c);
|
|
}
|
|
expandedemptymacro = true;
|
|
free(buffer);
|
|
}
|
|
else
|
|
{
|
|
//QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Macro without argument list");
|
|
//such macros don't get expanded.
|
|
pr_file_p = initial_file_p;
|
|
pr_source_line = initial_line;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (c->inside >= 8)
|
|
{
|
|
pr_file_p = initial_file_p;
|
|
pr_source_line = initial_line;
|
|
return false;
|
|
}
|
|
|
|
if (*c->value)
|
|
QCC_PR_IncludeChunkEx(c->value, false, NULL, c);
|
|
expandedemptymacro = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
tok = QCC_PR_CheckBuiltinCompConst(pr_token, retbuf, sizeof(retbuf));
|
|
if (tok)
|
|
{
|
|
pr_file_p = end;
|
|
QCC_PR_IncludeChunkEx(tok, true, NULL, NULL);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const char *QCC_PR_CheckCompConstString(const char *def)
|
|
{
|
|
const char *s;
|
|
|
|
CompilerConstant_t *c;
|
|
|
|
c = pHash_Get(&compconstantstable, def);
|
|
|
|
if (c)
|
|
{
|
|
s = QCC_PR_CheckCompConstString(c->value);
|
|
return s;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
CompilerConstant_t *QCC_PR_CheckCompConstDefined(const char *def)
|
|
{
|
|
CompilerConstant_t *c = pHash_Get(&compconstantstable, def);
|
|
return c;
|
|
}
|
|
|
|
char *QCC_PR_CheckCompConstTooltip(char *word, char *outstart, char *outend)
|
|
{
|
|
int i;
|
|
CompilerConstant_t *c = QCC_PR_CheckCompConstDefined(word);
|
|
if (c)
|
|
{
|
|
char *out = outstart;
|
|
if (c->numparams >= 0)
|
|
{
|
|
QC_snprintfz(out, outend-out, "#define %s(", c->name);
|
|
out += strlen(out);
|
|
for (i = 0; i < c->numparams-1; i++)
|
|
{
|
|
QC_snprintfz(out, outend-out, "%s,", c->params[i]);
|
|
out += strlen(out);
|
|
}
|
|
if (i < c->numparams)
|
|
{
|
|
QC_snprintfz(out, outend-out, "%s", c->params[i]);
|
|
out += strlen(out);
|
|
}
|
|
QC_snprintfz(out, outend-out, ")");
|
|
}
|
|
else
|
|
QC_snprintfz(out, outend-out, "#define %s", c->name);
|
|
out += strlen(out);
|
|
if (c->value && *c->value)
|
|
QC_snprintfz(out, outend-out, "\n%s", c->value);
|
|
|
|
return outstart;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
==============
|
|
PR_Lex
|
|
|
|
Sets pr_token, pr_token_type, and possibly pr_immediate and pr_immediate_type
|
|
==============
|
|
*/
|
|
void QCC_PR_Lex (void)
|
|
{
|
|
int c;
|
|
|
|
pr_token[0] = 0;
|
|
|
|
if (!pr_file_p)
|
|
{
|
|
if (QCC_PR_UnInclude())
|
|
{
|
|
QCC_PR_Lex();
|
|
return;
|
|
}
|
|
pr_token_type = tt_eof;
|
|
return;
|
|
}
|
|
|
|
pr_token_precomment = NULL;
|
|
QCC_PR_LexComment(&pr_token_precomment);
|
|
// QCC_PR_LexWhitespace (false);
|
|
|
|
pr_token_line_last = pr_token_line;
|
|
pr_token_line = pr_source_line;
|
|
if (currentchunk)
|
|
pr_token_line += currentchunk->currentlinenumber-1;
|
|
|
|
if (!pr_file_p)
|
|
{
|
|
if (QCC_PR_UnInclude())
|
|
{
|
|
QCC_PR_Lex();
|
|
return;
|
|
}
|
|
pr_token_type = tt_eof;
|
|
return;
|
|
}
|
|
|
|
c = *pr_file_p;
|
|
|
|
if (!c)
|
|
{
|
|
if (QCC_PR_UnInclude())
|
|
{
|
|
QCC_PR_Lex();
|
|
return;
|
|
}
|
|
pr_token_type = tt_eof;
|
|
return;
|
|
}
|
|
|
|
// handle quoted strings as a unit
|
|
if (c == '\"' || ((c == 'R' || c == 'Q' || c == 'u' || c == 'U') && pr_file_p[1] == '\"') || (c == 'u' && pr_file_p[1] == '8' && pr_file_p[2] == '\"'))
|
|
{
|
|
QCC_PR_LexString ();
|
|
return;
|
|
}
|
|
|
|
// handle quoted vectors as a unit
|
|
if (c == '\'')
|
|
{
|
|
QCC_PR_LexVector ();
|
|
return;
|
|
}
|
|
|
|
// if the first character is a valid identifier, parse until a non-id
|
|
// character is reached
|
|
if ((c == '%') && flag_qccx && (pr_file_p[1] == '-' || (pr_file_p[1] >= '0' && pr_file_p[1] <= '9')))
|
|
{
|
|
//with qccx, %5 is a denormalized float.
|
|
pr_file_p++;
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
QCC_PR_ParseWarning(WARN_DENORMAL, "denormalized immediate");
|
|
pr_immediate._int = QCC_PR_LexInteger ();
|
|
return;
|
|
}
|
|
if ( c == '0' && pr_file_p[1] == 'x')
|
|
{
|
|
pr_token_type = tt_immediate;
|
|
QCC_PR_LexNumber();
|
|
return;
|
|
}
|
|
if ( (c == '.'&&pr_file_p[1]!='.'&&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;
|
|
QCC_PR_LexNumber ();
|
|
return;
|
|
}
|
|
|
|
if (/*!flag_qccx &&*/ c == '#' && !(pr_file_p[1]==')' || pr_file_p[1]==',' || pr_file_p[1]=='\"' || pr_file_p[1]=='-' || (pr_file_p[1]>='0' && pr_file_p[1] <='9'))) //hash and not number
|
|
{
|
|
pr_file_p++;
|
|
if (!QCC_PR_CheckCompConst())
|
|
{
|
|
if (!QCC_PR_SimpleGetToken())
|
|
strcpy(pr_token, "unknown");
|
|
QCC_PR_ParseError(ERR_CONSTANTNOTDEFINED, "Explicit precompiler usage when not defined %s", pr_token);
|
|
}
|
|
else
|
|
{
|
|
QCC_PR_Lex();
|
|
if (pr_token_type == tt_eof)
|
|
QCC_PR_Lex();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c & 0x80))
|
|
{
|
|
if (flag_hashonly || !QCC_PR_CheckCompConst()) //look for a macro.
|
|
QCC_PR_LexName ();
|
|
else
|
|
{
|
|
//we expanded a macro. we need to read the tokens out of it now though
|
|
QCC_PR_Lex();
|
|
if (pr_token_type == tt_eof)
|
|
{
|
|
if (QCC_PR_UnInclude())
|
|
{
|
|
QCC_PR_Lex();
|
|
return;
|
|
}
|
|
pr_token_type = tt_eof;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (c == '$')
|
|
{
|
|
QCC_PR_LexGrab ();
|
|
return;
|
|
}
|
|
|
|
// parse symbol strings until a non-symbol is found
|
|
QCC_PR_LexPunctuation ();
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
pbool QCC_Temp_Describe(QCC_def_t *def, char *buffer, int buffersize);
|
|
void QCC_PR_ParsePrintDef (int type, QCC_def_t *def)
|
|
{
|
|
if (!qccwarningaction[type])
|
|
return;
|
|
if (def->filen)
|
|
{
|
|
char tybuffer[512];
|
|
char tmbuffer[512];
|
|
char vlbuffer[512];
|
|
char *modifiers;
|
|
if (QCC_Temp_Describe(def, tmbuffer, sizeof(tmbuffer)))
|
|
{
|
|
externs->Printf ("%s:%i: (%s)(%s)\n", def->filen, def->s_line, TypeName(def->type, tybuffer, sizeof(tybuffer)), tmbuffer);
|
|
}
|
|
else
|
|
{
|
|
modifiers = "";
|
|
if (def->constant)
|
|
modifiers = "const ";
|
|
else if (def->isstatic)
|
|
modifiers = "static ";
|
|
|
|
|
|
if (def && def->initialized && def->constant && !def->arraysize)
|
|
{
|
|
const QCC_eval_t *ev = (const QCC_eval_t*)&def->symboldata[0];
|
|
switch(def->type->type)
|
|
{
|
|
case ev_float: QC_snprintfz(vlbuffer, sizeof(vlbuffer), " = %g", ev->_float); break;
|
|
case ev_double: QC_snprintfz(vlbuffer, sizeof(vlbuffer), " = %g", ev->_double); break;
|
|
case ev_integer:QC_snprintfz(vlbuffer, sizeof(vlbuffer), " = %i", ev->_int); break;
|
|
case ev_uint: QC_snprintfz(vlbuffer, sizeof(vlbuffer), " = %u", ev->_uint); break;
|
|
case ev_int64: QC_snprintfz(vlbuffer, sizeof(vlbuffer), " = %"pPRIi64, ev->i64); break;
|
|
case ev_uint64: QC_snprintfz(vlbuffer, sizeof(vlbuffer), " = %"pPRIu64, ev->u64); break;
|
|
default: *vlbuffer = 0; break;
|
|
}
|
|
}
|
|
else
|
|
*vlbuffer = 0;
|
|
if (flag_msvcstyle)
|
|
externs->Printf ("%s%s(%i) : %s%s%s %s%s%s%s%s is defined here\n", col_location, def->filen, def->s_line, col_type, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), col_symbol, def->name, col_type, vlbuffer, col_none);
|
|
else
|
|
externs->Printf ("%s%s:%i: %s%s%s %s%s%s%s%s is defined here\n", col_location, def->filen, def->s_line, col_type, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), col_symbol, def->name, col_type, vlbuffer, col_none);
|
|
}
|
|
}
|
|
}
|
|
void QCC_PR_ParsePrintSRef (int type, QCC_sref_t def)
|
|
{
|
|
QCC_PR_ParsePrintDef(type, def.sym);
|
|
}
|
|
|
|
void *errorscope;
|
|
static void QCC_PR_PrintMacro (qcc_includechunk_t *chunk)
|
|
{
|
|
if (chunk)
|
|
{
|
|
QCC_PR_PrintMacro(chunk->prev);
|
|
if (chunk->cnst)
|
|
{
|
|
#if 1
|
|
externs->Printf ("%s%s:%i: macro %s%s%s is defined here\n", col_location, chunk->cnst->fromfile, chunk->cnst->fromline, col_symbol, chunk->cnst->name, col_none);
|
|
#else
|
|
externs->Printf ("%s:%i: expanding %s\n", chunk->currentfilename, chunk->currentlinenumber, chunk->cnst->name);
|
|
#endif
|
|
if (verbose >= VERBOSE_STANDARD)
|
|
externs->Printf ("%s\n", chunk->datastart);
|
|
}
|
|
else
|
|
externs->Printf ("%s:%i:\n", chunk->currentfilename, chunk->currentlinenumber);
|
|
}
|
|
}
|
|
static void QCC_PR_PrintScope (void)
|
|
{
|
|
QCC_PR_PrintMacro(currentchunk);
|
|
if (pr_scope)
|
|
{
|
|
if (errorscope != pr_scope)
|
|
externs->Printf ("in function %s%s%s (line %i),\n", col_symbol, pr_scope->name, col_none, pr_scope->line);
|
|
errorscope = pr_scope;
|
|
}
|
|
else
|
|
{
|
|
if (errorscope)
|
|
externs->Printf ("at global scope,\n");
|
|
errorscope = NULL;
|
|
}
|
|
}
|
|
void QCC_PR_ResetErrorScope(void)
|
|
{
|
|
errorscope = NULL;
|
|
}
|
|
/*
|
|
============
|
|
PR_ParseError
|
|
|
|
Aborts the current file load
|
|
============
|
|
*/
|
|
#ifndef QCC
|
|
void editbadfile(const char *file, int line);
|
|
#endif
|
|
//will abort.
|
|
NORETURN void VARGS QCC_PR_ParseError (int errortype, const char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start (argptr,error);
|
|
QC_vsnprintf (string,sizeof(string)-1, error,argptr);
|
|
va_end (argptr);
|
|
|
|
#ifndef QCC
|
|
editbadfile(s_filen, pr_source_line);
|
|
#endif
|
|
|
|
QCC_PR_PrintScope();
|
|
if (flag_msvcstyle)
|
|
externs->Printf ("%s%s(%i) : %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string);
|
|
else
|
|
externs->Printf ("%s%s:%i: %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string);
|
|
|
|
longjmp (pr_parse_abort, 1);
|
|
}
|
|
//will abort.
|
|
NORETURN void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, const char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start (argptr,error);
|
|
QC_vsnprintf (string,sizeof(string)-1, error,argptr);
|
|
va_end (argptr);
|
|
|
|
#ifndef QCC
|
|
editbadfile(s_filen, pr_source_line);
|
|
#endif
|
|
QCC_PR_PrintScope();
|
|
if (flag_msvcstyle)
|
|
externs->Printf ("%s%s(%i) : %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string);
|
|
else
|
|
externs->Printf ("%s%s:%i: %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string);
|
|
|
|
QCC_PR_ParsePrintDef(WARN_ERROR, def);
|
|
|
|
longjmp (pr_parse_abort, 1);
|
|
}
|
|
|
|
NORETURN void VARGS QCC_PR_ParseErrorPrintSRef (int errortype, QCC_sref_t def, const char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start (argptr,error);
|
|
QC_vsnprintf (string,sizeof(string)-1, error,argptr);
|
|
va_end (argptr);
|
|
|
|
#ifndef QCC
|
|
editbadfile(s_filen, pr_source_line);
|
|
#endif
|
|
QCC_PR_PrintScope();
|
|
if (flag_msvcstyle)
|
|
externs->Printf ("%s%s(%i) : %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string);
|
|
else
|
|
externs->Printf ("%s%s:%i: %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string);
|
|
|
|
QCC_PR_ParsePrintSRef(WARN_ERROR, def);
|
|
|
|
longjmp (pr_parse_abort, 1);
|
|
}
|
|
|
|
static pbool VARGS QCC_PR_PrintWarning (int type, const char *file, int line, const char *string)
|
|
{
|
|
char *wnam = QCC_NameForWarning(type);
|
|
if (!wnam)
|
|
wnam = "";
|
|
|
|
if (string)
|
|
QCC_PR_PrintScope();
|
|
|
|
if (type >= ERR_PARSEERRORS)
|
|
{
|
|
if (!string)
|
|
;
|
|
else if (!file || !*file)
|
|
externs->Printf (":: %serror%s%s: %s\n", col_error, wnam, col_none, string);
|
|
else if (flag_msvcstyle)
|
|
externs->Printf ("%s%s(%i) : %serror%s%s: %s\n", col_location, file, line, col_error, wnam, col_none, string);
|
|
else
|
|
externs->Printf ("%s%s:%i: %serror%s%s: %s\n", col_location, file, line, col_error, wnam, col_none, string);
|
|
pr_error_count++;
|
|
}
|
|
else if (qccwarningaction[type] == 2)
|
|
{ //-werror
|
|
if (!string)
|
|
;
|
|
else if (!file || !*file)
|
|
externs->Printf (":: %swerror%s%s: %s\n", col_error, wnam, col_none, string);
|
|
else if (flag_msvcstyle)
|
|
externs->Printf ("%s%s(%i) : %swerror%s%s: %s\n", col_location, file, line, col_error, wnam, col_none, string);
|
|
else
|
|
externs->Printf ("%s%s:%i: %swerror%s%s: %s\n", col_location, file, line, col_error, wnam, col_none, string);
|
|
pr_error_count++;
|
|
}
|
|
else
|
|
{
|
|
if (!string)
|
|
;
|
|
else if (!file || !*file)
|
|
externs->Printf (":: %swarning%s%s: %s\n", col_warning, wnam, col_none, string);
|
|
else if (flag_msvcstyle)
|
|
externs->Printf ("%s%s(%i) : %swarning%s%s: %s\n", col_location, file, line, col_warning, wnam, col_none, string);
|
|
else
|
|
externs->Printf ("%s%s:%i: %swarning%s%s: %s\n", col_location, file, line, col_warning, wnam, col_none, string);
|
|
pr_warning_count++;
|
|
}
|
|
return true;
|
|
}
|
|
pbool VARGS QCC_PR_Warning (int type, const char *file, int line, const char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
if (!qccwarningaction[type])
|
|
return false;
|
|
|
|
if (!error)
|
|
return QCC_PR_PrintWarning(type, file, line, NULL);
|
|
|
|
va_start (argptr,error);
|
|
QC_vsnprintf (string,sizeof(string)-1, error,argptr);
|
|
va_end (argptr);
|
|
|
|
return QCC_PR_PrintWarning(type, file, line, string);
|
|
}
|
|
|
|
//can be used for errors, qcc execution will continue.
|
|
pbool VARGS QCC_PR_ParseWarning (int type, const char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
if (!qccwarningaction[type])
|
|
return false;
|
|
|
|
va_start (argptr,error);
|
|
QC_vsnprintf (string,sizeof(string)-1, error,argptr);
|
|
va_end (argptr);
|
|
|
|
return QCC_PR_PrintWarning(type, s_filen, pr_source_line, string);
|
|
}
|
|
|
|
void VARGS QCC_PR_Note (int type, const char *file, int line, const char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
if (!qccwarningaction[type])
|
|
return;
|
|
|
|
va_start (argptr,error);
|
|
QC_vsnprintf (string,sizeof(string)-1, error,argptr);
|
|
va_end (argptr);
|
|
|
|
QCC_PR_PrintScope();
|
|
if (!file)
|
|
externs->Printf ("note: %s\n", string);
|
|
else if (flag_msvcstyle)
|
|
externs->Printf ("%s(%i) : note: %s\n", file, line, string);
|
|
else
|
|
externs->Printf ("%s:%i: note: %s\n", file, line, string);
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
PR_Expect
|
|
|
|
Issues an error if the current token isn't equal to string
|
|
Gets the next token
|
|
=============
|
|
*/
|
|
#ifndef COMMONINLINES
|
|
void QCC_PR_Expect (const char *string)
|
|
{
|
|
if (STRCMP (string, pr_token))
|
|
QCC_PR_ParseError (ERR_EXPECTED, "expected %s%s%s, found %s%s%s", col_location, string, col_none, col_name, pr_token, col_none);
|
|
QCC_PR_Lex ();
|
|
}
|
|
#endif
|
|
|
|
void QCC_PR_LexComment(char **comment)
|
|
{
|
|
char c;
|
|
char *start;
|
|
int nl;
|
|
char *old;
|
|
int oldlen;
|
|
pbool replace = true;
|
|
pbool nextcomment = true;
|
|
|
|
// skip whitespace
|
|
nl = false;
|
|
while(nextcomment)
|
|
{
|
|
nextcomment = false;
|
|
|
|
while ((c = *pr_file_p) && qcc_iswhite(c))
|
|
{
|
|
if (qcc_islineending(c, pr_file_p[1])) //allow new lines, but only if there's whitespace before any tokens, and no double newlines.
|
|
{
|
|
if (nl)
|
|
{
|
|
pr_file_p++;
|
|
QCC_PR_NewLine(false);
|
|
break;
|
|
}
|
|
nl = true;
|
|
}
|
|
else
|
|
{
|
|
pr_file_p++;
|
|
nl = false;
|
|
}
|
|
}
|
|
if (nl)
|
|
break;
|
|
|
|
// parse // comments
|
|
if (c=='/' && pr_file_p[1] == '/')
|
|
{
|
|
pr_file_p += 2;
|
|
while (*pr_file_p == ' ' || *pr_file_p == '\t')
|
|
pr_file_p++;
|
|
start = pr_file_p;
|
|
while (*pr_file_p && *pr_file_p != '\n')
|
|
pr_file_p++;
|
|
|
|
if (*pr_file_p == '\n')
|
|
{
|
|
pr_file_p++;
|
|
QCC_PR_NewLine(false);
|
|
}
|
|
|
|
old = replace?NULL:*comment;
|
|
replace = false;
|
|
oldlen = old?strlen(old)+1:0;
|
|
*comment = qccHunkAlloc(oldlen + (pr_file_p-start)+1);
|
|
if (oldlen)
|
|
{
|
|
memcpy(*comment, old, oldlen-1);
|
|
memcpy(*comment+oldlen-1, "\n", 1);
|
|
}
|
|
memcpy(*comment + oldlen, start, pr_file_p - start);
|
|
oldlen = oldlen+pr_file_p - start;
|
|
while(oldlen > 0 && ((*comment)[oldlen-1] == '\r' || (*comment)[oldlen-1] == '\n' || (*comment)[oldlen-1] == '\t' || (*comment)[oldlen-1] == ' '))
|
|
oldlen--;
|
|
(*comment)[oldlen] = 0;
|
|
nextcomment = true; //include the next // too
|
|
nl = true;
|
|
}
|
|
// parse /* comments
|
|
else if (c=='/' && pr_file_p[1] == '*' && replace)
|
|
{
|
|
pr_file_p+=1;
|
|
start = pr_file_p+1;
|
|
|
|
do
|
|
{
|
|
pr_file_p++;
|
|
if (pr_file_p[0]=='\n')
|
|
{
|
|
QCC_PR_NewLine(true);
|
|
}
|
|
else if (pr_file_p[1] == 0)
|
|
{
|
|
QCC_PR_ParseError(0, "EOF inside comment\n");
|
|
break;
|
|
}
|
|
if (pr_file_p[0] == '/' && pr_file_p[1] == '*')
|
|
QCC_PR_ParseWarning(WARN_NESTEDCOMMENT, "\"/*\" inside comment");
|
|
} while (pr_file_p[0] != '*' || pr_file_p[1] != '/');
|
|
|
|
if (pr_file_p[1] == 0)
|
|
break;
|
|
|
|
old = replace?NULL:*comment;
|
|
replace = false;
|
|
oldlen = old?strlen(old):0;
|
|
*comment = qccHunkAlloc(oldlen + (pr_file_p-start)+1);
|
|
memcpy(*comment, old, oldlen);
|
|
memcpy(*comment + oldlen, start, pr_file_p - start);
|
|
(*comment)[oldlen+pr_file_p - start] = 0;
|
|
|
|
pr_file_p+=2;
|
|
}
|
|
}
|
|
|
|
QCC_PR_LexWhitespace(false);
|
|
}
|
|
|
|
pbool QCC_PR_CheckTokenComment(const char *string, char **comment)
|
|
{
|
|
if (pr_token_type != tt_punct)
|
|
return false;
|
|
|
|
if (STRCMP (string, pr_token))
|
|
return false;
|
|
|
|
if (comment)
|
|
QCC_PR_LexComment(comment);
|
|
|
|
//and then do the rest properly.
|
|
QCC_PR_Lex ();
|
|
return true;
|
|
}
|
|
/*
|
|
=============
|
|
PR_Check
|
|
|
|
Returns true and gets the next token if the current token equals string
|
|
Returns false and does nothing otherwise
|
|
=============
|
|
*/
|
|
#ifndef COMMONINLINES
|
|
pbool QCC_PR_CheckToken (const char *string)
|
|
{
|
|
if (pr_token_type != tt_punct)
|
|
return false;
|
|
|
|
if (STRCMP (string, pr_token))
|
|
return false;
|
|
|
|
QCC_PR_Lex ();
|
|
return true;
|
|
}
|
|
|
|
pbool QCC_PR_PeekToken (const char *string)
|
|
{
|
|
if (pr_token_type != tt_punct)
|
|
return false;
|
|
if (STRCMP (string, pr_token))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
pbool QCC_PR_CheckImmediate (const char *string)
|
|
{
|
|
if (pr_token_type != tt_immediate)
|
|
return false;
|
|
|
|
if (STRCMP (string, pr_token))
|
|
return false;
|
|
|
|
QCC_PR_Lex ();
|
|
return true;
|
|
}
|
|
|
|
pbool QCC_PR_CheckName(const char *string)
|
|
{
|
|
if (pr_token_type != tt_name)
|
|
return false;
|
|
if (flag_caseinsensitive)
|
|
{
|
|
if (stricmp (string, pr_token))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (STRCMP(string, pr_token))
|
|
return false;
|
|
}
|
|
QCC_PR_Lex ();
|
|
return true;
|
|
}
|
|
|
|
pbool QCC_PR_CheckKeyword(int keywordenabled, const char *string)
|
|
{
|
|
if (pr_token[0] == '_' && pr_token[1] == '_')
|
|
{
|
|
//lets just always go insensitive with a leading underscore pair.
|
|
if (stricmp(string, pr_token+2))
|
|
return false;
|
|
QCC_PR_Lex ();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (!keywordenabled)
|
|
return false;
|
|
if (flag_caseinsensitive)
|
|
{
|
|
if (stricmp (string, pr_token))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (STRCMP(string, pr_token))
|
|
return false;
|
|
}
|
|
}
|
|
QCC_PR_Lex ();
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
============
|
|
PR_ParseName
|
|
|
|
Checks to see if the current token is a valid name
|
|
============
|
|
*/
|
|
char *QCC_PR_ParseName (void)
|
|
{
|
|
char ident[MAX_NAME];
|
|
char *ret;
|
|
|
|
if (pr_token_type != tt_name)
|
|
{
|
|
if (pr_token_type == tt_eof)
|
|
QCC_PR_ParseError (ERR_EOF, "unexpected EOF", pr_token);
|
|
else if (strcmp(pr_token, "...")) //seriously? someone used '...' as an intrinsic NAME?
|
|
QCC_PR_ParseError (ERR_NOTANAME, "\"%s%s%s\" - not a name", col_name, pr_token, col_none);
|
|
}
|
|
if (strlen(pr_token) >= MAX_NAME-1)
|
|
QCC_PR_ParseError (ERR_NAMETOOLONG, "name too long");
|
|
strcpy (ident, pr_token);
|
|
QCC_PR_Lex ();
|
|
|
|
ret = qccHunkAlloc(strlen(ident)+1);
|
|
strcpy(ret, ident);
|
|
return ret;
|
|
// return ident;
|
|
}
|
|
|
|
/*
|
|
============
|
|
PR_FindType
|
|
|
|
Returns a preexisting complex type that matches the parm, or allocates
|
|
a new one and copies it out.
|
|
============
|
|
*/
|
|
|
|
//requires EVERYTHING to be the same
|
|
static int typecmp_strict(QCC_type_t *a, QCC_type_t *b)
|
|
{
|
|
int i;
|
|
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->vargs != b->vargs)
|
|
return 1;
|
|
if (a->vargcount != b->vargcount)
|
|
return 1;
|
|
|
|
if (a->size != b->size)
|
|
return 1;
|
|
|
|
if (a->accessors != b->accessors)
|
|
return 1;
|
|
|
|
if (STRCMP(a->name, b->name))
|
|
return 1;
|
|
|
|
if (typecmp_strict(a->aux_type, b->aux_type))
|
|
return 1;
|
|
|
|
i = a->num_parms;
|
|
while(i-- > 0)
|
|
{
|
|
if (STRCMP(a->params[i].paramname, b->params[i].paramname))
|
|
return 1;
|
|
if (typecmp_strict(a->params[i].type, b->params[i].type))
|
|
return 1;
|
|
if (a->params[i].defltvalue.cast || b->params[i].defltvalue.cast)
|
|
{
|
|
if (typecmp_strict(a->params[i].defltvalue.cast, b->params[i].defltvalue.cast) ||
|
|
a->params[i].defltvalue.sym != b->params[i].defltvalue.sym ||
|
|
a->params[i].defltvalue.ofs != b->params[i].defltvalue.ofs)
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//reports if they're functionally equivelent (allows assignments)
|
|
int typecmp(QCC_type_t *a, QCC_type_t *b)
|
|
{
|
|
int i;
|
|
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->vargs != b->vargs)
|
|
return 1;
|
|
if (a->vargcount != b->vargcount)
|
|
return 1;
|
|
|
|
if (a->size != b->size)
|
|
return 1;
|
|
|
|
if ((a->type == ev_entity && a->parentclass) || a->type == ev_struct || a->type == ev_union)
|
|
{
|
|
if (STRCMP(a->name, b->name))
|
|
return 1;
|
|
}
|
|
|
|
if (typecmp(a->aux_type, b->aux_type))
|
|
return 1;
|
|
|
|
i = a->num_parms;
|
|
while(i-- > 0)
|
|
{
|
|
if (a->type != ev_function && STRCMP(a->params[i].paramname, b->params[i].paramname))
|
|
return 1;
|
|
if (typecmp(a->params[i].type, b->params[i].type))
|
|
return 1;
|
|
if ((a->type != ev_function && (a->params[i].defltvalue.cast || b->params[i].defltvalue.cast)) ||
|
|
(a->type == ev_function && (a->params[i].defltvalue.cast && b->params[i].defltvalue.cast)))
|
|
{
|
|
if (typecmp(a->params[i].defltvalue.cast, b->params[i].defltvalue.cast) ||
|
|
a->params[i].defltvalue.sym != b->params[i].defltvalue.sym ||
|
|
a->params[i].defltvalue.ofs != b->params[i].defltvalue.ofs)
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//compares the types, but doesn't complain if there are optional arguments which differ
|
|
int typecmp_lax(QCC_type_t *a, QCC_type_t *b)
|
|
{
|
|
unsigned int minargs = 0;
|
|
unsigned int t;
|
|
|
|
if (a == b)
|
|
return 0;
|
|
if (!a || !b)
|
|
return 1; //different (^ and not both null)
|
|
|
|
if (a->type != b->type)
|
|
{
|
|
if (a->type == ev_accessor && a->parentclass)
|
|
if (!typecmp_lax(a->parentclass, b))
|
|
return 0;
|
|
if (b->type == ev_accessor && b->parentclass)
|
|
if (!typecmp_lax(a, b->parentclass))
|
|
return 0;
|
|
if (a->type != ev_variant && b->type != ev_variant)
|
|
return 1;
|
|
}
|
|
else if (a->size != b->size)
|
|
return 1;
|
|
|
|
if (a->vargcount != b->vargcount)
|
|
return 1;
|
|
|
|
t = a->num_parms;
|
|
minargs = t;
|
|
t = b->num_parms;
|
|
if (minargs > t)
|
|
minargs = t;
|
|
|
|
// if (STRCMP(a->name, b->name)) //This isn't 100% clean.
|
|
// return 1;
|
|
|
|
if (typecmp_lax(a->aux_type, b->aux_type))
|
|
return 1;
|
|
|
|
//variants and args don't make sense, and are considered equivelent(ish).
|
|
if (a->type == ev_variant || b->type == ev_variant)
|
|
return 0;
|
|
|
|
//optional arg types must match, even if they're not specified in one.
|
|
for (t = 0; t < minargs; t++)
|
|
{
|
|
if (a->params[t].type->type != b->params[t].type->type)
|
|
return 1;
|
|
if (a->params[t].out != b->params[t].out)
|
|
return 1;
|
|
//classes/structs/unions are matched on class names rather than the contents of the class
|
|
//it gets too painful otherwise, with recursive definitions.
|
|
if (a->params[t].type->type == ev_entity || a->params[t].type->type == ev_struct || a->params[t].type->type == ev_union)
|
|
{
|
|
if (STRCMP(a->params[t].type->name, b->params[t].type->name))
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if (typecmp_lax(a->params[t].type, b->params[t].type))
|
|
return 1;
|
|
}
|
|
|
|
if ((a->type != ev_function && (a->params[t].defltvalue.cast || b->params[t].defltvalue.cast)) ||
|
|
(a->type == ev_function && (a->params[t].defltvalue.cast && b->params[t].defltvalue.cast)))
|
|
{
|
|
if (typecmp(a->params[t].defltvalue.cast, b->params[t].defltvalue.cast) ||
|
|
a->params[t].defltvalue.sym != b->params[t].defltvalue.sym ||
|
|
a->params[t].defltvalue.ofs != b->params[t].defltvalue.ofs)
|
|
return 1;
|
|
}
|
|
}
|
|
if (a->num_parms > minargs)
|
|
{
|
|
for (t = minargs; t < a->num_parms; t++)
|
|
{
|
|
if (!a->params[t].optional)
|
|
return 1;
|
|
}
|
|
}
|
|
if (b->num_parms > minargs)
|
|
{
|
|
for (t = minargs; t < b->num_parms; t++)
|
|
{
|
|
if (!b->params[t].optional)
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
QCC_type_t *QCC_PR_DuplicateType(QCC_type_t *in, pbool recurse)
|
|
{
|
|
QCC_type_t *out;
|
|
if (!in)
|
|
return NULL;
|
|
|
|
out = QCC_PR_NewType(in->name, in->type, false);
|
|
out->aux_type = recurse?QCC_PR_DuplicateType(in->aux_type, recurse):in->aux_type;
|
|
out->num_parms = in->num_parms;
|
|
out->params = qccHunkAlloc(sizeof(*out->params) * out->num_parms);
|
|
memcpy(out->params, in->params, sizeof(*out->params) * out->num_parms);
|
|
out->accessors = in->accessors;
|
|
out->size = in->size;
|
|
out->num_parms = in->num_parms;
|
|
out->name = in->name;
|
|
out->parentclass = in->parentclass;
|
|
out->vargs = in->vargs;
|
|
out->vargcount = in->vargcount;
|
|
|
|
return out;
|
|
}
|
|
|
|
static void Q_strlcat(char *dest, const char *src, int sizeofdest)
|
|
{
|
|
if (sizeofdest)
|
|
{
|
|
int dlen = strlen(dest);
|
|
int slen = strlen(src)+1;
|
|
memcpy(dest+dlen, src, min((sizeofdest-1)-dlen, slen));
|
|
dest[sizeofdest - 1] = 0;
|
|
}
|
|
}
|
|
|
|
char *TypeName(QCC_type_t *type, char *buffer, int buffersize)
|
|
{
|
|
char *ret;
|
|
/*if (type->typedefed)
|
|
{
|
|
if (buffersize < 0)
|
|
return buffer;
|
|
*buffer = 0;
|
|
Q_strlcat(buffer, type->name, buffersize);
|
|
return buffer;
|
|
}*/
|
|
|
|
if (type->type == ev_void)
|
|
{
|
|
if (buffersize < 0)
|
|
return buffer;
|
|
*buffer = 0;
|
|
Q_strlcat(buffer, "void", buffersize);
|
|
return buffer;
|
|
}
|
|
if (type->type == ev_enum)
|
|
{
|
|
if (buffersize < 0)
|
|
return buffer;
|
|
*buffer = 0;
|
|
Q_strlcat(buffer, "enum ", buffersize);
|
|
Q_strlcat(buffer, type->name, buffersize);
|
|
Q_strlcat(buffer, ":", buffersize);
|
|
TypeName(type->aux_type, buffer+strlen(buffer), buffersize-strlen(buffer));
|
|
return buffer;
|
|
}
|
|
if (type->type == ev_struct)
|
|
{
|
|
if (buffersize < 0)
|
|
return buffer;
|
|
*buffer = 0;
|
|
Q_strlcat(buffer, "struct ", buffersize);
|
|
Q_strlcat(buffer, type->name, buffersize);
|
|
return buffer;
|
|
}
|
|
if (type->type == ev_union)
|
|
{
|
|
if (buffersize < 0)
|
|
return buffer;
|
|
*buffer = 0;
|
|
Q_strlcat(buffer, "union ", buffersize);
|
|
Q_strlcat(buffer, type->name, buffersize);
|
|
return buffer;
|
|
}
|
|
|
|
if (type->type == ev_pointer)
|
|
{
|
|
if (buffersize < 0)
|
|
return buffer;
|
|
TypeName(type->aux_type, buffer, buffersize-2);
|
|
Q_strlcat(buffer, " *", buffersize);
|
|
return buffer;
|
|
}
|
|
|
|
ret = buffer;
|
|
if (type->type == ev_field)
|
|
{
|
|
type = type->aux_type;
|
|
*ret++ = '.';
|
|
}
|
|
*ret = 0;
|
|
|
|
if (type->type == ev_function)
|
|
{
|
|
int args = type->num_parms;
|
|
pbool vargs = type->vargs;
|
|
unsigned int i;
|
|
Q_strlcat(buffer, type->aux_type->name, buffersize);
|
|
Q_strlcat(buffer, "(", buffersize);
|
|
for (i = 0; i < type->num_parms; )
|
|
{
|
|
if (type->params[i].out)
|
|
Q_strlcat(buffer, "inout ", buffersize);
|
|
if (type->params[i].optional)
|
|
Q_strlcat(buffer, "optional ", buffersize);
|
|
args--;
|
|
|
|
Q_strlcat(buffer, type->params[i].type->name, buffersize);
|
|
if (type->params[i].paramname && *type->params[i].paramname)
|
|
{
|
|
Q_strlcat(buffer, " ", buffersize);
|
|
Q_strlcat(buffer, type->params[i].paramname, buffersize);
|
|
}
|
|
|
|
if (type->params[i].defltvalue.cast)
|
|
{
|
|
Q_strlcat(buffer, " = ", buffersize);
|
|
Q_strlcat(buffer, QCC_VarAtOffset(type->params[i].defltvalue), buffersize);
|
|
}
|
|
|
|
if (++i < type->num_parms || vargs)
|
|
Q_strlcat(buffer, ", ", buffersize);
|
|
}
|
|
if (vargs)
|
|
Q_strlcat(buffer, "...", buffersize);
|
|
Q_strlcat(buffer, ")", buffersize);
|
|
}
|
|
else if (type->type == ev_entity && type->parentclass)
|
|
{
|
|
ret = buffer;
|
|
*ret = 0;
|
|
Q_strlcat(buffer, "class ", buffersize);
|
|
Q_strlcat(buffer, type->name, buffersize);
|
|
/* strcat(ret, " {");
|
|
type = type->param;
|
|
while(type)
|
|
{
|
|
strcat(ret, type->name);
|
|
type = type->next;
|
|
|
|
if (type)
|
|
strcat(ret, ", ");
|
|
}
|
|
strcat(ret, "}");
|
|
*/
|
|
}
|
|
else
|
|
Q_strlcat(buffer, type->name, buffersize);
|
|
|
|
return buffer;
|
|
}
|
|
//#define typecmp(a, b) (a && ((a)->type==(b)->type) && !STRCMP((a)->name, (b)->name))
|
|
|
|
static QCC_type_t *QCC_PR_FindType (QCC_type_t *type)
|
|
{
|
|
int t;
|
|
for (t = 0; t < numtypeinfos; t++)
|
|
{
|
|
// check = &qcc_typeinfo[t];
|
|
if (typecmp_strict(&qcc_typeinfo[t], type))
|
|
continue;
|
|
|
|
// c2 = check->next;
|
|
// n2 = type->next;
|
|
// for (i=0 ; n2&&c2 ; i++)
|
|
// {
|
|
// if (!typecmp((c2), (n2)))
|
|
// break;
|
|
// c2=c2->next;
|
|
// n2=n2->next;
|
|
// }
|
|
|
|
// if (n2==NULL&&c2==NULL)
|
|
{
|
|
return &qcc_typeinfo[t];
|
|
}
|
|
}
|
|
|
|
type = QCC_PR_DuplicateType(type, false);
|
|
return type;
|
|
}
|
|
/*
|
|
QCC_type_t *QCC_PR_NextSubType(QCC_type_t *type, QCC_type_t *prev)
|
|
{
|
|
int p;
|
|
if (!prev)
|
|
return type->next;
|
|
|
|
for (p = prev->num_parms; p; p--)
|
|
prev = QCC_PR_NextSubType(prev, NULL);
|
|
if (prev->num_parms)
|
|
|
|
switch(prev->type)
|
|
{
|
|
case ev_function:
|
|
|
|
}
|
|
|
|
return prev->next;
|
|
}
|
|
*/
|
|
|
|
QCC_type_t *QCC_TypeForName(const char *name)
|
|
{
|
|
QCC_type_t *t = pHash_Get(&typedeftable, name);
|
|
while (t && t->scope)
|
|
{
|
|
if (t->scope == pr_scope)
|
|
break; //its okay after all.
|
|
t = pHash_GetNext(&typedeftable, name, t);
|
|
}
|
|
if (t && t->type == ev_typedef)
|
|
return t->aux_type; //just use its real type.
|
|
return t;
|
|
}
|
|
|
|
/*
|
|
============
|
|
PR_SkipToSemicolon
|
|
|
|
For error recovery, also pops out of nested braces
|
|
============
|
|
*/
|
|
void QCC_PR_SkipToSemicolon (void)
|
|
{
|
|
//escape out of any #define
|
|
while (currentchunk && currentchunk->cnst)
|
|
QCC_PR_UnInclude();
|
|
|
|
do
|
|
{
|
|
if (!pr_bracelevel && QCC_PR_CheckToken (";"))
|
|
return;
|
|
QCC_PR_Lex ();
|
|
} while (pr_token_type != tt_eof);
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
PR_ParseType
|
|
|
|
Parses a variable type, including field and functions types
|
|
============
|
|
*/
|
|
#ifdef MAX_EXTRA_PARMS
|
|
char pr_parm_names[MAX_PARMS+MAX_EXTRA_PARMS][MAX_NAME];
|
|
#else
|
|
char pr_parm_names[MAX_PARMS][MAX_NAME];
|
|
#endif
|
|
char *pr_parm_argcount_name;
|
|
|
|
int recursivefunctiontype;
|
|
|
|
QCC_type_t *QCC_PR_MakeThiscall(QCC_type_t *orig, QCC_type_t *thistype)
|
|
{
|
|
QCC_type_t ftype = *orig;
|
|
|
|
ftype.ptrto = NULL;
|
|
ftype.typedefed = false;
|
|
ftype.num_parms++;
|
|
ftype.params = qccHunkAlloc(sizeof(*ftype.params) * ftype.num_parms);
|
|
memcpy(ftype.params+1, orig->params, sizeof(*ftype.params) * orig->num_parms);
|
|
ftype.params[0].paramname = "this";
|
|
ftype.params[0].type = QCC_PR_PointerType(thistype);
|
|
ftype.params[0].isvirtual = true;
|
|
|
|
memmove(&pr_parm_names[1], &pr_parm_names[0], sizeof(*pr_parm_names)*orig->num_parms);
|
|
strcpy(pr_parm_names[0], "this");
|
|
|
|
orig = QCC_PR_FindType (&ftype);
|
|
if (!orig)
|
|
{
|
|
orig = QCC_PR_NewType(ftype.name, ftype.type, false);
|
|
*orig = ftype;
|
|
}
|
|
return orig;
|
|
}
|
|
|
|
//expects a ( to have already been parsed.
|
|
QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype)
|
|
{
|
|
QCC_type_t *ftype, *t;
|
|
char *name;
|
|
int definenames = !recursivefunctiontype;
|
|
int numparms = 0;
|
|
struct QCC_typeparam_s paramlist[MAX_PARMS+MAX_EXTRA_PARMS];
|
|
|
|
recursivefunctiontype++;
|
|
|
|
ftype = QCC_PR_NewType(type_function->name, ev_function, false);
|
|
|
|
ftype->aux_type = returntype; // return type
|
|
ftype->num_parms = 0;
|
|
|
|
if (definenames)
|
|
pr_parm_argcount_name = NULL;
|
|
|
|
if (!QCC_PR_CheckToken (")"))
|
|
{
|
|
do
|
|
{
|
|
pbool foundinout;
|
|
if (ftype->num_parms>=MAX_PARMS+MAX_EXTRA_PARMS)
|
|
QCC_PR_ParseError(ERR_TOOMANYTOTALPARAMETERS, "Too many parameters. Sorry. (limit is %i)\n", MAX_PARMS+MAX_EXTRA_PARMS);
|
|
|
|
if (QCC_PR_CheckToken ("..."))
|
|
{
|
|
t = QCC_PR_ParseType(false, true); //the evil things I do...
|
|
if (!t)
|
|
{
|
|
ftype->vargs = true;
|
|
break;
|
|
}
|
|
else
|
|
{ //its a ... followed by a type... don't bug out...
|
|
t = QCC_PR_FieldType(t);
|
|
t = QCC_PR_FieldType(t);
|
|
t = QCC_PR_FieldType(t);
|
|
paramlist[numparms].type = t;
|
|
foundinout = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foundinout = false;
|
|
paramlist[numparms].optional = false;
|
|
paramlist[numparms].isvirtual = false;
|
|
paramlist[numparms].out = false;
|
|
|
|
while(1)
|
|
{
|
|
if (!paramlist[numparms].optional && QCC_PR_CheckKeyword(keyword_optional, "optional"))
|
|
paramlist[numparms].optional = true;
|
|
else if (!foundinout && QCC_PR_CheckKeyword(keyword_inout, "inout"))
|
|
{
|
|
paramlist[numparms].out = true;
|
|
foundinout = true;
|
|
}
|
|
else if (!foundinout && QCC_PR_CheckKeyword(keyword_inout, "out"))
|
|
{
|
|
paramlist[numparms].out = 2; //not really supported, but parsed for readability.
|
|
foundinout = true;
|
|
}
|
|
else if (!foundinout && QCC_PR_CheckKeyword(keyword_inout, "in"))
|
|
{
|
|
paramlist[numparms].out = false;
|
|
foundinout = true;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
t = QCC_PR_ParseType(false, false);
|
|
}
|
|
paramlist[numparms].defltvalue.cast = NULL;
|
|
paramlist[numparms].ofs = 0;
|
|
paramlist[numparms].arraysize = 0;
|
|
paramlist[numparms].type = t;
|
|
if (!paramlist[numparms].type)
|
|
QCC_PR_ParseError(0, "Expected type\n");
|
|
|
|
while (QCC_PR_CheckToken("*"))
|
|
paramlist[numparms].type = QCC_PointerTypeTo(paramlist[numparms].type);
|
|
|
|
if (paramlist[numparms].type->type == ev_void)
|
|
break; //float(void) has no actual args
|
|
|
|
if (!foundinout && QCC_PR_CheckToken("&"))
|
|
{ //accept c++ syntax, at least on arguments. its not quite the same, but it'll do.
|
|
paramlist[numparms].out = true;
|
|
foundinout = true;
|
|
}
|
|
|
|
// type->name = "FUNC PARAMETER";
|
|
|
|
paramlist[numparms].paramname = "";
|
|
if (STRCMP(pr_token, ",") && STRCMP(pr_token, ")"))
|
|
{
|
|
if (QCC_PR_CheckToken ("..."))
|
|
{
|
|
ftype->vargs = true;
|
|
break;
|
|
}
|
|
newtype = true;
|
|
name = QCC_PR_ParseName ();
|
|
paramlist[numparms].paramname = qccHunkAlloc(strlen(name)+1);
|
|
strcpy(paramlist[numparms].paramname, name);
|
|
if (definenames)
|
|
strcpy (pr_parm_names[numparms], name);
|
|
|
|
if (QCC_PR_CheckToken("["))
|
|
{
|
|
if (QCC_PR_CheckToken("]")) //length omitted. just treat it as a pointer...?
|
|
{
|
|
QCC_PR_ParseError(0, "unsized array argument\n");
|
|
paramlist[numparms].type = QCC_PointerTypeTo(paramlist[numparms].type);
|
|
}
|
|
else
|
|
{ //proper array
|
|
paramlist[numparms].arraysize = QCC_PR_IntConstExpr();
|
|
if (!paramlist[numparms].arraysize)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "cannot cope with 0-sized arrays");
|
|
QCC_PR_Expect("]");
|
|
}
|
|
}
|
|
}
|
|
else if (definenames)
|
|
strcpy (pr_parm_names[numparms], "");
|
|
|
|
if (QCC_PR_CheckToken("="))
|
|
{
|
|
paramlist[numparms].defltvalue = QCC_PR_ParseDefaultInitialiser(paramlist[numparms].type);
|
|
QCC_FreeTemp(paramlist[numparms].defltvalue);
|
|
}
|
|
numparms++;
|
|
} while (QCC_PR_CheckToken (","));
|
|
|
|
if (ftype->vargs)
|
|
{
|
|
if (!QCC_PR_CheckToken (")"))
|
|
{
|
|
name = QCC_PR_ParseName();
|
|
if (definenames)
|
|
{
|
|
pr_parm_argcount_name = qccHunkAlloc(strlen(name)+1);
|
|
strcpy(pr_parm_argcount_name, name);
|
|
}
|
|
ftype->vargcount = true;
|
|
QCC_PR_Expect (")");
|
|
}
|
|
}
|
|
else
|
|
QCC_PR_Expect (")");
|
|
}
|
|
ftype->num_parms = numparms;
|
|
ftype->params = qccHunkAlloc(sizeof(*ftype->params) * numparms);
|
|
memcpy(ftype->params, paramlist, sizeof(*ftype->params) * numparms);
|
|
recursivefunctiontype--;
|
|
|
|
if (returntype->size > 3 && !autoprototype) //fixme: handle properly, without breaking __out
|
|
QCC_PR_ParseWarning(WARN_UNDESIRABLECONVENTION, "Unable to handle functions returning structures larger than a vector. Will truncate.");
|
|
|
|
if (newtype)
|
|
return ftype;
|
|
return QCC_PR_FindType (ftype);
|
|
}
|
|
QCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype)
|
|
{
|
|
QCC_type_t *ftype, *nptype;
|
|
char *name;
|
|
int definenames = !recursivefunctiontype;
|
|
int numparms = 0;
|
|
struct QCC_typeparam_s paramlist[MAX_PARMS+MAX_EXTRA_PARMS];
|
|
|
|
recursivefunctiontype++;
|
|
|
|
ftype = QCC_PR_NewType(type_function->name, ev_function, false);
|
|
|
|
ftype->aux_type = returntype; // return type
|
|
ftype->num_parms = 0;
|
|
|
|
pr_parm_argcount_name = NULL;
|
|
|
|
if (!QCC_PR_CheckToken (")"))
|
|
{
|
|
do
|
|
{
|
|
if (numparms>=MAX_PARMS+MAX_EXTRA_PARMS)
|
|
QCC_PR_ParseError(ERR_TOOMANYTOTALPARAMETERS, "Too many parameters. Sorry. (limit is %i)\n", MAX_PARMS+MAX_EXTRA_PARMS);
|
|
|
|
if (QCC_PR_CheckToken ("..."))
|
|
{
|
|
ftype->vargs = true;
|
|
break;
|
|
}
|
|
|
|
if (QCC_PR_CheckName("arg"))
|
|
{
|
|
name = "";
|
|
nptype = QCC_PR_NewType("Variant", ev_variant, false);
|
|
}
|
|
else if (QCC_PR_CheckName("vect")) //this can only be of vector sizes, so...
|
|
{
|
|
name = "";
|
|
nptype = QCC_PR_NewType("Vector", ev_vector, false);
|
|
}
|
|
else
|
|
{
|
|
name = QCC_PR_ParseName();
|
|
QCC_PR_Expect(":");
|
|
nptype = QCC_PR_ParseType(true, false);
|
|
}
|
|
|
|
if (!nptype)
|
|
QCC_PR_ParseError(0, "Expected type\n");
|
|
if (nptype->type == ev_void)
|
|
break;
|
|
// type->name = "FUNC PARAMETER";
|
|
|
|
paramlist[numparms].out = false;
|
|
paramlist[numparms].optional = false;
|
|
paramlist[numparms].isvirtual = false;
|
|
paramlist[numparms].ofs = 0;
|
|
paramlist[numparms].arraysize = 0;
|
|
paramlist[numparms].type = nptype;
|
|
|
|
if (!*name)
|
|
paramlist[numparms].paramname = "";
|
|
else
|
|
{
|
|
paramlist[numparms].paramname = qccHunkAlloc(strlen(name)+1);
|
|
strcpy(paramlist[numparms].paramname, name);
|
|
}
|
|
if (definenames)
|
|
QC_snprintfz(pr_parm_names[numparms], MAX_NAME, "%s", name);
|
|
|
|
numparms++;
|
|
} while (QCC_PR_CheckToken (";"));
|
|
QCC_PR_Expect (")");
|
|
}
|
|
ftype->num_parms = numparms;
|
|
ftype->params = qccHunkAlloc(sizeof(*ftype->params) * numparms);
|
|
memcpy(ftype->params, paramlist, sizeof(*ftype->params) * numparms);
|
|
recursivefunctiontype--;
|
|
if (newtype)
|
|
return ftype;
|
|
return QCC_PR_FindType (ftype);
|
|
}
|
|
QCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto)
|
|
{
|
|
QCC_type_t *ptype;
|
|
char name[128];
|
|
if (pointsto->ptrto)
|
|
return pointsto->ptrto;
|
|
QC_snprintfz(name, sizeof(name), "%s*", pointsto->name);
|
|
ptype = QCC_PR_NewType(strcpy(qccHunkAlloc(strlen(name)+1), name), ev_pointer, false);
|
|
ptype->aux_type = pointsto;
|
|
return pointsto->ptrto = QCC_PR_FindType (ptype);
|
|
}
|
|
QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto)
|
|
{
|
|
QCC_type_t *ptype;
|
|
char name[128];
|
|
if (pointsto->fldto)
|
|
return pointsto->fldto;
|
|
QC_snprintfz(name, sizeof(name), ".%s", pointsto->name);
|
|
ptype = QCC_PR_NewType(strcpy(qccHunkAlloc(strlen(name)+1), name), ev_field, false);
|
|
ptype->aux_type = pointsto;
|
|
ptype->size = ptype->aux_type->size;
|
|
return pointsto->fldto = QCC_PR_FindType (ptype);
|
|
}
|
|
QCC_type_t *QCC_PR_GenFunctionType (QCC_type_t *rettype, struct QCC_typeparam_s *args, int numargs)
|
|
{
|
|
int i;
|
|
struct QCC_typeparam_s *p;
|
|
QCC_type_t *ftype;
|
|
ftype = QCC_PR_NewType("$func", ev_function, false);
|
|
ftype->aux_type = rettype;
|
|
ftype->num_parms = numargs;
|
|
ftype->params = p = qccHunkAlloc(sizeof(*ftype->params)*numargs);
|
|
ftype->vargs = false;
|
|
ftype->vargcount = false;
|
|
for (i = 0; i < numargs; i++, p++)
|
|
{
|
|
if (args[i].paramname)
|
|
{
|
|
p->paramname = qccHunkAlloc(strlen(args[i].paramname)+1);
|
|
strcpy(p->paramname, args[i].paramname);
|
|
}
|
|
else
|
|
p->paramname = "";
|
|
p->type = args[i].type;
|
|
p->out = args[i].out;
|
|
p->optional = args[i].optional;
|
|
p->isvirtual = args[i].isvirtual;
|
|
p->ofs = args[i].ofs;
|
|
p->arraysize = args[i].arraysize;
|
|
p->defltvalue.cast = NULL;
|
|
}
|
|
return QCC_PR_FindType (ftype);
|
|
}
|
|
|
|
|
|
struct accessor_s *QCC_PR_ParseAccessorMember(QCC_type_t *classtype, pbool isinline, pbool setnotget)
|
|
{
|
|
struct accessor_s *acc, *pacc;
|
|
char *fieldtypename;
|
|
QCC_type_t *fieldtype;
|
|
QCC_type_t *indextype;
|
|
QCC_sref_t def;
|
|
QCC_type_t *functype;
|
|
QCC_type_t *parenttype;
|
|
struct QCC_typeparam_s arg[3];
|
|
int args;
|
|
char *indexname;
|
|
pbool isref;
|
|
char *accessorname;
|
|
|
|
if (QCC_PR_CheckToken("&"))
|
|
isref = 2;
|
|
else
|
|
isref = QCC_PR_CheckToken("*");
|
|
|
|
fieldtypename = QCC_PR_ParseName();
|
|
fieldtype = QCC_TypeForName(fieldtypename);
|
|
if (!fieldtype)
|
|
QCC_PR_ParseError(ERR_NOTATYPE, "Invalid type: %s", fieldtypename);
|
|
while(QCC_PR_CheckToken("*"))
|
|
fieldtype = QCC_PR_PointerType(fieldtype);
|
|
|
|
if (pr_token_type != tt_punct)
|
|
accessorname = QCC_PR_ParseName();
|
|
else
|
|
accessorname = "";
|
|
|
|
indextype = NULL;
|
|
indexname = "index";
|
|
if (QCC_PR_CheckToken("["))
|
|
{
|
|
fieldtypename = QCC_PR_ParseName();
|
|
indextype = QCC_TypeForName(fieldtypename);
|
|
|
|
if (!QCC_PR_CheckToken("]"))
|
|
{
|
|
indexname = QCC_PR_ParseName();
|
|
QCC_PR_Expect("]");
|
|
}
|
|
}
|
|
|
|
QCC_PR_Expect("=");
|
|
|
|
args = 0;
|
|
memset(arg, 0, sizeof(arg));
|
|
strcpy (pr_parm_names[args], "this");
|
|
arg[args].paramname = "this";
|
|
if (isref == 2)
|
|
{
|
|
arg[args].type = classtype;
|
|
arg[args].out = 1; //inout
|
|
}
|
|
else if (isref)
|
|
arg[args].type = QCC_PointerTypeTo(classtype);
|
|
else
|
|
arg[args].type = classtype;
|
|
args++;
|
|
if (indextype)
|
|
{
|
|
strcpy (pr_parm_names[args], indexname);
|
|
arg[args].paramname = indexname;
|
|
arg[args++].type = indextype;
|
|
}
|
|
if (setnotget)
|
|
{
|
|
strcpy (pr_parm_names[args], "value");
|
|
arg[args].paramname = "value";
|
|
arg[args++].type = fieldtype;
|
|
}
|
|
functype = QCC_PR_GenFunctionType(setnotget?type_void:fieldtype, arg, args);
|
|
|
|
if (pr_token_type != tt_name)
|
|
{
|
|
QCC_function_t *f;
|
|
char funcname[256];
|
|
QC_snprintfz(funcname, sizeof(funcname), "%s::%s_%s", classtype->name, setnotget?"set":"get", accessorname);
|
|
|
|
def = QCC_PR_GetSRef(functype, funcname, NULL, true, 0, GDF_CONST | (isinline?GDF_INLINE:0));
|
|
|
|
if (autoprototype)
|
|
{
|
|
if (QCC_PR_CheckToken("["))
|
|
{
|
|
while (!QCC_PR_CheckToken("]"))
|
|
{
|
|
if (pr_token_type == tt_eof)
|
|
break;
|
|
QCC_PR_Lex();
|
|
}
|
|
}
|
|
QCC_PR_Expect("{");
|
|
|
|
{
|
|
int blev = 1;
|
|
//balance out the { and }
|
|
while(blev)
|
|
{
|
|
if (pr_token_type == tt_eof)
|
|
break;
|
|
if (QCC_PR_CheckToken("{"))
|
|
blev++;
|
|
else if (QCC_PR_CheckToken("}"))
|
|
blev--;
|
|
else
|
|
QCC_PR_Lex(); //ignore it.
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pr_classtype = ((classtype->type==ev_entity)?classtype:NULL);
|
|
f = QCC_PR_ParseImmediateStatements (def.sym, functype, false);
|
|
pr_classtype = NULL;
|
|
pr_scope = NULL;
|
|
def.sym->symboldata[def.ofs].function = f - functions;
|
|
f->def = def.sym;
|
|
def.sym->initialized = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const char *funcname = QCC_PR_ParseName();
|
|
def = QCC_PR_GetSRef(functype, funcname, NULL, true, 0, GDF_CONST|(isinline?GDF_INLINE:0));
|
|
if (!def.cast)
|
|
QCC_Error(ERR_NOFUNC, "%s::set_%s: %s was not defined", classtype->name, accessorname, funcname);
|
|
}
|
|
if (!def.cast || !def.sym || def.sym->temp)
|
|
QCC_Error(ERR_NOFUNC, "%s::%s_%s function invalid", classtype->name, setnotget?"set":"get", accessorname);
|
|
|
|
for (acc = classtype->accessors; acc; acc = acc->next)
|
|
if (!strcmp(acc->fieldname, accessorname))
|
|
break;
|
|
if (!acc)
|
|
{
|
|
acc = qccHunkAlloc(sizeof(*acc));
|
|
acc->fieldname = accessorname;
|
|
acc->next = classtype->accessors;
|
|
acc->type = fieldtype;
|
|
acc->indexertype = indextype;
|
|
classtype->accessors = acc;
|
|
}
|
|
|
|
if (acc->getset_func[setnotget].cast && (
|
|
acc->getset_func[setnotget].sym != def.sym ||
|
|
acc->getset_func[setnotget].cast != def.cast ||
|
|
acc->getset_func[setnotget].ofs != def.ofs))
|
|
QCC_Error(ERR_TOOMANYINITIALISERS, "%s::%s_%s already declared", classtype->name, setnotget?"set":"get", accessorname);
|
|
acc->getset_func[setnotget] = def;
|
|
acc->getset_isref[setnotget] = isref;
|
|
QCC_FreeTemp(def);
|
|
|
|
for (parenttype = classtype->parentclass; parenttype; parenttype = parenttype->parentclass)
|
|
{
|
|
if (!parenttype->accessors)
|
|
continue;
|
|
for (pacc = parenttype->accessors; pacc; pacc = pacc->next)
|
|
{
|
|
if (!strcmp(acc->fieldname, pacc->fieldname))
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "%s::%s shadows parent %s", classtype->name, acc->fieldname?acc->fieldname:"<anon>", parenttype->name);
|
|
}
|
|
}
|
|
|
|
return acc;
|
|
}
|
|
|
|
extern char *basictypenames[];
|
|
extern QCC_type_t **basictypes[];
|
|
pbool type_inlinefunction;
|
|
/*newtype=true: creates a new type always
|
|
silentfail=true: function is permitted to return NULL if it was not given a type, otherwise never returns NULL
|
|
*/
|
|
QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
|
|
{
|
|
QCC_type_t *newparm;
|
|
QCC_type_t *newt;
|
|
QCC_type_t *type;
|
|
char *name;
|
|
etype_t structtype;
|
|
|
|
type_inlinefunction = false; //doesn't really matter so long as its not from an inline function type
|
|
|
|
// int ofs;
|
|
|
|
if (QCC_PR_CheckKeyword(keyword_const, "const"))
|
|
{
|
|
QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "ignoring unsupported const keyword");
|
|
silentfail = false; //FIXME
|
|
}
|
|
|
|
if (QCC_PR_PeekToken ("...") ) //this is getting stupid
|
|
{
|
|
QCC_PR_LexWhitespace (false);
|
|
if (*pr_file_p == '(')
|
|
{ //work around gmqcc's "(...(" being misinterpreted as a cast syntax error, instead abort here so it can be treated as an intrinsic (with args) instead.
|
|
if (silentfail)
|
|
return NULL;
|
|
QCC_PR_ParseError (ERR_NOTATYPE, "\"%s\" is not a type", pr_token);
|
|
}
|
|
QCC_PR_Lex ();
|
|
|
|
type = QCC_PR_NewType("FIELD_TYPE", ev_field, false);
|
|
type->aux_type = QCC_PR_ParseType (false, false);
|
|
type->size = type->aux_type->size;
|
|
|
|
newt = QCC_PR_FindType (type);
|
|
type = QCC_PR_NewType("FIELD_TYPE", ev_field, false);
|
|
type->aux_type = newt;
|
|
type->size = type->aux_type->size;
|
|
|
|
newt = QCC_PR_FindType (type);
|
|
type = QCC_PR_NewType("FIELD_TYPE", ev_field, false);
|
|
type->aux_type = newt;
|
|
type->size = type->aux_type->size;
|
|
|
|
if (newtype)
|
|
return type;
|
|
return QCC_PR_FindType (type);
|
|
}
|
|
if (QCC_PR_CheckToken ("..")) //so we don't end up with the user specifying '. .vector blah' (hexen2 added the .. token for array ranges)
|
|
{
|
|
newt = QCC_PR_NewType("FIELD_TYPE", ev_field, false);
|
|
newt->aux_type = QCC_PR_ParseType (false, false);
|
|
|
|
newt->size = newt->aux_type->size;
|
|
|
|
newt = QCC_PR_FindType (newt);
|
|
|
|
type = QCC_PR_NewType("FIELD_TYPE", ev_field, false);
|
|
type->aux_type = newt;
|
|
|
|
type->size = type->aux_type->size;
|
|
|
|
if (newtype)
|
|
return type;
|
|
return QCC_PR_FindType (type);
|
|
}
|
|
if (QCC_PR_CheckToken ("."))
|
|
{
|
|
//.float *foo; is annoying.
|
|
//technically it is a pointer to a .float
|
|
//most people will want a .(float*) foo;
|
|
//so .*float will give you that.
|
|
//however, we can't cope parsing that with regular types, so we support that ONLY when . was already specified.
|
|
//this is pretty much an evil syntax hack.
|
|
pbool ptr = QCC_PR_CheckToken ("*");
|
|
type = QCC_PR_ParseType(false, false);
|
|
if (!type)
|
|
QCC_PR_ParseError(0, "Expected type\n");
|
|
if (ptr)
|
|
type = QCC_PointerTypeTo(type);
|
|
|
|
name = qccHunkAlloc(strlen(type->name)+2);
|
|
*name = '.';
|
|
strcpy(name+1, type->name);
|
|
|
|
newt = QCC_PR_NewType(name, ev_field, false);
|
|
newt->aux_type = type;
|
|
newt->size = newt->aux_type->size;
|
|
|
|
if (newtype)
|
|
return newt;
|
|
return QCC_PR_FindType (newt);
|
|
}
|
|
|
|
name = pr_token;
|
|
if (pr_token_type != tt_name)
|
|
{
|
|
if (silentfail)
|
|
return NULL;
|
|
QCC_PR_ParseError (ERR_NOTATYPE, "\"%s\" is not a type", name);
|
|
}
|
|
// name = QCC_PR_CheckCompConstString(name);
|
|
|
|
//accessors
|
|
if (QCC_PR_CheckKeyword (keyword_accessor, "accessor"))
|
|
{
|
|
char parentname[256];
|
|
char *accessorname;
|
|
char *funcname;
|
|
|
|
newt = NULL;
|
|
|
|
funcname = QCC_PR_ParseName();
|
|
accessorname = qccHunkAlloc(strlen(funcname)+1);
|
|
strcpy(accessorname, funcname);
|
|
|
|
/* Look to see if this type is already defined */
|
|
newt = QCC_TypeForName(accessorname);
|
|
if (newt && newt->type != ev_accessor)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Type %s cannot be redefined as an accessor", accessorname);
|
|
|
|
if (QCC_PR_CheckToken(":"))
|
|
{
|
|
type = QCC_PR_ParseType(false, false);
|
|
if (type)
|
|
TypeName(type, parentname, sizeof(parentname));
|
|
else
|
|
strcpy(parentname, "??");
|
|
|
|
if (!type || type->type == ev_struct || type->type == ev_union)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Accessor %s cannot be based upon %s", accessorname, parentname);
|
|
}
|
|
else
|
|
type = NULL;
|
|
|
|
if (!newt)
|
|
{
|
|
newt = QCC_PR_NewType(accessorname, ev_accessor, true);
|
|
newt->size=type->size;
|
|
}
|
|
if (!newt->parentclass)
|
|
{
|
|
newt->parentclass = type;
|
|
if (!newt->parentclass || newt->parentclass->type == ev_struct || newt->parentclass->type == ev_union || newt->size != newt->parentclass->size)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Accessor %s cannot be based upon %s", accessorname, parentname);
|
|
}
|
|
else if (type != newt->parentclass)
|
|
{
|
|
char bufe[256];
|
|
char bufn[256];
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Accessor %s basic type mismatch (%s, expected %s)", accessorname,
|
|
TypeName(type, bufn, sizeof(bufn)), TypeName(newt->parentclass, bufe, sizeof(bufe)));
|
|
}
|
|
|
|
if (QCC_PR_CheckToken("{"))
|
|
{
|
|
pbool setnotget;
|
|
pbool isinline;
|
|
|
|
newt->filen = s_filen;
|
|
newt->line = pr_source_line;
|
|
|
|
do
|
|
{
|
|
isinline = QCC_PR_CheckName("inline");
|
|
|
|
if (QCC_PR_CheckName("set"))
|
|
setnotget = true;
|
|
else if (QCC_PR_CheckName("get"))
|
|
setnotget = false;
|
|
else
|
|
break;
|
|
|
|
QCC_PR_ParseAccessorMember(newt, isinline, setnotget);
|
|
} while (QCC_PR_CheckToken(",") || QCC_PR_CheckToken(";"));
|
|
QCC_PR_Expect("}");
|
|
}
|
|
|
|
if (newtype)
|
|
newt = QCC_PR_DuplicateType(newt, false);
|
|
return newt;
|
|
}
|
|
|
|
if (flag_qcfuncs && QCC_PR_CheckKeyword (keyword_class, "class"))
|
|
{
|
|
// int parms;
|
|
QCC_type_t *fieldtype;
|
|
char membername[2048];
|
|
char *classname;
|
|
int forwarddeclaration;
|
|
int numparms = 0;
|
|
struct QCC_typeparam_s *parms = NULL;
|
|
char *parmname;
|
|
int arraysize;
|
|
pbool redeclaration;
|
|
int basicindex;
|
|
QCC_def_t *d;
|
|
QCC_type_t *pc;
|
|
QCC_type_t *basetype;
|
|
pbool found = false;
|
|
int assumevirtual = 0; //0=erk, 1=yes, -1=no
|
|
|
|
parmname = QCC_PR_ParseName();
|
|
classname = qccHunkAlloc(strlen(parmname)+1);
|
|
strcpy(classname, parmname);
|
|
|
|
newt = 0;
|
|
|
|
if (QCC_PR_CheckToken(":"))
|
|
{
|
|
char *parentname = QCC_PR_ParseName();
|
|
fieldtype = QCC_TypeForName(parentname);
|
|
if (!fieldtype)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Parent class %s was not yet defined", parentname);
|
|
|
|
//FIXME: we should allow inheriting from 'object' too.
|
|
if (fieldtype->type != ev_entity)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Parent type %s is not a class/entity", parentname);
|
|
forwarddeclaration = false;
|
|
|
|
QCC_PR_Expect("{");
|
|
}
|
|
else
|
|
{
|
|
fieldtype = type_entity;
|
|
forwarddeclaration = !QCC_PR_CheckToken("{");
|
|
}
|
|
|
|
newt = QCC_TypeForName(classname);
|
|
if (newt && newt->num_parms != 0)
|
|
redeclaration = true;
|
|
else
|
|
redeclaration = false;
|
|
|
|
if (!newt)
|
|
{
|
|
newt = QCC_PR_NewType(classname, fieldtype->type, true);
|
|
newt->size=type_entity->size;
|
|
}
|
|
|
|
type = NULL;
|
|
|
|
if (forwarddeclaration)
|
|
return newt;
|
|
|
|
if (pr_scope)
|
|
QCC_PR_ParseError(ERR_REDECLARATION, "Declaration of class %s%s%s within function", col_type,classname,col_none);
|
|
if (redeclaration && fieldtype != newt->parentclass)
|
|
QCC_PR_ParseError(ERR_REDECLARATION, "Parent class changed on redeclaration of %s%s%s", col_type,classname,col_none);
|
|
newt->parentclass = fieldtype;
|
|
newt->filen = s_filen;
|
|
newt->line = pr_source_line;
|
|
|
|
if (QCC_PR_CheckToken(","))
|
|
QCC_PR_ParseError(ERR_NOTANAME, "member missing name");
|
|
while (!QCC_PR_CheckToken("}"))
|
|
{
|
|
unsigned int gdf_flags;
|
|
|
|
pbool wasvirt = false;
|
|
pbool wasnonvirt = false;
|
|
pbool wasstatic = false;
|
|
pbool isconst = false;
|
|
pbool isvar = false;
|
|
pbool isignored = false;
|
|
pbool isinline = false;
|
|
pbool isget = false;
|
|
pbool isset = false;
|
|
// pbool ispublic = false;
|
|
// pbool isprivate = false;
|
|
// pbool isprotected = false;
|
|
pbool firstkeyword = true;
|
|
|
|
while(1)
|
|
{
|
|
if (QCC_PR_CheckKeyword(1, "nonvirtual"))
|
|
{
|
|
if (firstkeyword && QCC_PR_CheckToken(":"))
|
|
{ //fteqcc-specific
|
|
assumevirtual = -1;
|
|
continue;
|
|
}
|
|
wasnonvirt = true;
|
|
}
|
|
else if (QCC_PR_CheckKeyword(1, "static"))
|
|
wasstatic = true;
|
|
else if (QCC_PR_CheckKeyword(1, "const"))
|
|
isconst = wasstatic = true;
|
|
else if (QCC_PR_CheckKeyword(1, "var"))
|
|
isvar = true;
|
|
else if (QCC_PR_CheckKeyword(1, "virtual"))
|
|
{
|
|
if (firstkeyword && QCC_PR_CheckToken(":"))
|
|
{ //fteqcc-specific
|
|
assumevirtual = 1;
|
|
continue;
|
|
}
|
|
wasvirt = true;
|
|
}
|
|
else if (QCC_PR_CheckKeyword(1, "ignore"))
|
|
isignored = true;
|
|
else if (QCC_PR_CheckKeyword(1, "strip"))
|
|
isignored = true;
|
|
else if (QCC_PR_CheckKeyword(1, "inline"))
|
|
isinline = true;
|
|
else if (QCC_PR_CheckKeyword(1, "get"))
|
|
isget = true;
|
|
else if (QCC_PR_CheckKeyword(1, "set"))
|
|
isset = true;
|
|
else if (firstkeyword && QCC_PR_CheckKeyword(1, "public"))
|
|
{
|
|
/*ispublic = true;*/
|
|
QCC_PR_Expect(":");
|
|
continue;
|
|
}
|
|
else if (firstkeyword && QCC_PR_CheckKeyword(1, "private"))
|
|
{
|
|
QCC_PR_Expect(":");
|
|
/*isprivate = true; */
|
|
continue;
|
|
}
|
|
else if (firstkeyword && QCC_PR_CheckKeyword(1, "protected"))
|
|
{
|
|
QCC_PR_Expect(":");
|
|
/*isprotected = true;*/ QCC_PR_Expect(":");
|
|
continue;
|
|
}
|
|
else
|
|
break;
|
|
firstkeyword = false;
|
|
}
|
|
if (isget || isset)
|
|
{
|
|
if (wasvirt)
|
|
QCC_PR_ParseWarning(ERR_INTERNAL, "virtual accessors are not supported at this time");
|
|
if (wasstatic)
|
|
QCC_PR_ParseError(ERR_INTERNAL, "static accessors are not supported");
|
|
QCC_PR_ParseAccessorMember(newt, isinline, isset);
|
|
QCC_PR_CheckToken(";");
|
|
continue;
|
|
}
|
|
|
|
basetype = QCC_PR_ParseType(false, false);
|
|
|
|
if (!basetype)
|
|
QCC_PR_ParseError(ERR_INTERNAL, "In class %s, expected type, found %s", classname, pr_token);
|
|
|
|
if (basetype->type == ev_struct || basetype->type == ev_union) //we wouldn't be able to handle it.
|
|
QCC_PR_ParseError(ERR_INTERNAL, "Struct or union in class %s", classname);
|
|
|
|
|
|
for (;basetype;)
|
|
{
|
|
pbool havebody = false;
|
|
pbool isnull = false;
|
|
pbool isstatic = wasstatic;
|
|
pbool isvirt = wasvirt;
|
|
pbool isnonvirt = wasnonvirt;
|
|
|
|
if (flag_qcfuncs && basetype->type == ev_function && basetype->aux_type == newt && QCC_PR_PeekToken(";"))
|
|
{ //C++ style constructor (ie: missing return type followed by class name)
|
|
//swap the class out for the appropriate function type...
|
|
newparm = QCC_PR_GenFunctionType(type_void, basetype->params, basetype->num_parms);
|
|
parmname = classname;
|
|
}
|
|
else if (!flag_qcfuncs && basetype == newt && QCC_PR_CheckToken("("))
|
|
{
|
|
newparm = QCC_PR_ParseFunctionType(false, type_void);
|
|
parmname = classname;
|
|
}
|
|
else
|
|
{
|
|
parmname = QCC_PR_ParseName();
|
|
if (QCC_PR_CheckToken("["))
|
|
{
|
|
arraysize = QCC_PR_IntConstExpr();
|
|
QCC_PR_Expect("]");
|
|
}
|
|
else
|
|
arraysize = 0;
|
|
|
|
if (QCC_PR_CheckToken("("))
|
|
{
|
|
//int fnc(), fld; is valid.
|
|
newparm = QCC_PR_ParseFunctionType(false, basetype);
|
|
}
|
|
else
|
|
newparm = basetype;
|
|
}
|
|
|
|
if (newparm->type == ev_function)
|
|
{
|
|
if (!strcmp(classname, parmname))
|
|
{
|
|
if (isstatic)
|
|
QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "Constructor %s::%s should not be static.", classname, pr_token);
|
|
if (!isvirt)
|
|
isnonvirt = true;//silently promote constructors to static
|
|
if (newparm->aux_type->type != ev_void)
|
|
QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "Constructor %s::%s does not return void.", classname, pr_token);
|
|
if (newparm->num_parms != 0 || newparm->vargs)
|
|
QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "Constructor arguments are not supported at this time %s::%s. Use named fields instead.", classname, pr_token);
|
|
}
|
|
else if (!isvirt && !isnonvirt && !isstatic)
|
|
{
|
|
if (assumevirtual == 1)
|
|
isvirt = true;
|
|
else if (assumevirtual == -1)
|
|
isnonvirt = true;
|
|
else
|
|
{
|
|
QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "%s::%s was not qualified. Assuming non-virtual.", classname, parmname);
|
|
isnonvirt = true;
|
|
}
|
|
}
|
|
else if (isvirt+isnonvirt+isstatic != 1)
|
|
QCC_PR_ParseError(ERR_INTERNAL, "Multiple conflicting qualifiers on %s::%s.", classname, pr_token);
|
|
|
|
if (isstatic)
|
|
{ //static and non-virtual functions differ only in that static should not have a 'this' available. however there's no hiding 'self' for calling in to other functions.
|
|
isstatic = false;
|
|
isnonvirt = true;
|
|
// QCC_PR_ParseError(ERR_INTERNAL, "%s::%s static member functions are not supported at this time.", classname, parmname);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isvirt||isnonvirt)
|
|
QCC_Error(ERR_INTERNAL, "virtual keyword on member that is not a function");
|
|
if (isinline)
|
|
QCC_Error(ERR_INTERNAL, "inline keyword on member that is not a function");
|
|
}
|
|
|
|
if (QCC_PR_CheckToken("="))
|
|
havebody = true;
|
|
else if (newparm->type == ev_function && QCC_PR_PeekToken("{"))
|
|
havebody = true;
|
|
if (isinline && (!havebody || isvirt))
|
|
QCC_Error(ERR_INTERNAL, "inline keyword on function prototype or virtual function");
|
|
|
|
gdf_flags = 0;
|
|
if ((newparm->type == ev_function && !arraysize && !isvar) || isconst)
|
|
gdf_flags = GDF_CONST;
|
|
|
|
if (havebody)
|
|
{
|
|
QCC_def_t *def;
|
|
if (pr_scope)
|
|
QCC_Error(ERR_INTERNAL, "Nested function declaration");
|
|
|
|
isnull = (QCC_PR_CheckImmediate("0") || QCC_PR_CheckImmediate("0i"));
|
|
QC_snprintfz(membername, sizeof(membername), "%s::%s", classname, parmname);
|
|
if (isnull)
|
|
{
|
|
if (isignored)
|
|
def = NULL;
|
|
else
|
|
{
|
|
def = QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags);
|
|
def->symboldata[def->ofs].function = 0;
|
|
def->initialized = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isignored)
|
|
def = NULL;
|
|
else
|
|
def = QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags);
|
|
|
|
if (newparm->type != ev_function && !isstatic)
|
|
QCC_Error(ERR_INTERNAL, "Can only initialise member functions");
|
|
else
|
|
{
|
|
if (autoprototype || isignored)
|
|
{
|
|
if (QCC_PR_CheckToken("["))
|
|
{
|
|
while (!QCC_PR_CheckToken("]"))
|
|
{
|
|
if (pr_token_type == tt_eof)
|
|
break;
|
|
QCC_PR_Lex();
|
|
}
|
|
}
|
|
QCC_PR_Expect("{");
|
|
|
|
{
|
|
int blev = 1;
|
|
//balance out the { and }
|
|
while(blev)
|
|
{
|
|
if (pr_token_type == tt_eof)
|
|
break;
|
|
if (QCC_PR_CheckToken("{"))
|
|
blev++;
|
|
else if (QCC_PR_CheckToken("}"))
|
|
blev--;
|
|
else
|
|
QCC_PR_Lex(); //ignore it.
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pr_classtype = newt;
|
|
QCC_PR_ParseInitializerDef(def, 0);
|
|
pr_classtype = NULL;
|
|
/*
|
|
f = QCC_PR_ParseImmediateStatements (def, newparm);
|
|
pr_classtype = NULL;
|
|
pr_scope = NULL;
|
|
def->symboldata[def->ofs].function = f - functions;
|
|
f->def = def;
|
|
def->initialized = 1;*/
|
|
}
|
|
}
|
|
}
|
|
if (def)
|
|
QCC_FreeDef(def);
|
|
|
|
if (!isvirt && !isignored)
|
|
{
|
|
QCC_def_t *fdef;
|
|
QCC_type_t *pc;
|
|
unsigned int i;
|
|
|
|
for (pc = newt->parentclass; pc; pc = pc->parentclass)
|
|
{
|
|
for (i = 0; i < pc->num_parms; i++)
|
|
{
|
|
if (!strcmp(pc->params[i].paramname, parmname))
|
|
{
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "%s::%s is virtual inside parent class '%s'. Did you forget the 'virtual' keyword?", newt->name, parmname, pc->name);
|
|
break;
|
|
}
|
|
}
|
|
if (i < pc->num_parms)
|
|
break;
|
|
}
|
|
if (!pc)
|
|
{
|
|
fdef = QCC_PR_GetDef(NULL, parmname, NULL, false, 0, GDF_CONST);
|
|
if (fdef && fdef->type->type == ev_field)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "%s::%s is virtual inside parent class 'entity'. Did you forget the 'virtual' keyword?", newt->name, parmname);
|
|
QCC_PR_ParsePrintDef(0, fdef);
|
|
}
|
|
else if (fdef)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "%s::%s shadows a global", newt->name, parmname);
|
|
QCC_PR_ParsePrintDef(0, fdef);
|
|
}
|
|
if (fdef)
|
|
QCC_FreeDef(fdef);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!QCC_PR_CheckToken(","))
|
|
{
|
|
QCC_PR_Expect(";");
|
|
basetype = NULL;
|
|
}
|
|
|
|
if (isignored) //member doesn't really exist
|
|
continue;
|
|
|
|
//static members are technically just funny-named globals, and do not generate fields.
|
|
if (isnonvirt || isstatic || (newparm->type == ev_function && !arraysize))
|
|
{
|
|
QC_snprintfz(membername, sizeof(membername), "%s::%s", classname, parmname);
|
|
QCC_FreeDef(QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags));
|
|
|
|
if (isnonvirt || isstatic)
|
|
continue;
|
|
}
|
|
|
|
|
|
fieldtype = QCC_PR_NewType(parmname, ev_field, false);
|
|
fieldtype->aux_type = newparm;
|
|
fieldtype->size = newparm->size;
|
|
|
|
parms = realloc(parms, sizeof(*parms) * (numparms+1));
|
|
parms[numparms].ofs = 0;
|
|
parms[numparms].out = false;
|
|
parms[numparms].optional = false;
|
|
parms[numparms].isvirtual = isvirt;
|
|
parms[numparms].paramname = parmname;
|
|
parms[numparms].arraysize = arraysize;
|
|
parms[numparms].type = newparm;
|
|
parms[numparms].defltvalue.cast = NULL;
|
|
|
|
basicindex = 0;
|
|
found = false;
|
|
for(pc = newt; pc && !found; pc = pc->parentclass)
|
|
{
|
|
struct QCC_typeparam_s *pp;
|
|
int numpc;
|
|
int i;
|
|
if (pc == newt)
|
|
{
|
|
pp = parms;
|
|
numpc = numparms;
|
|
}
|
|
else
|
|
{
|
|
pp = pc->params;
|
|
numpc = pc->num_parms;
|
|
}
|
|
for (i = 0; i < numpc; i++)
|
|
{
|
|
if (pp[i].type->type == newparm->type)
|
|
{
|
|
if (!strcmp(pp[i].paramname, parmname))
|
|
{
|
|
if (typecmp(pp[i].type, newparm))
|
|
{
|
|
char bufc[256];
|
|
char bufp[256];
|
|
TypeName(pp[i].type, bufp, sizeof(bufp));
|
|
TypeName(newparm, bufc, sizeof(bufc));
|
|
QCC_PR_ParseError(0, "%s defined as %s in %s, but %s in %s\n", parmname, bufc, newt->name, bufp, pc->name);
|
|
}
|
|
basicindex = pp[i].ofs;
|
|
found = true;
|
|
break;
|
|
}
|
|
if ((unsigned int)basicindex < pp[i].ofs+pp[i].type->size*(pp[i].arraysize?pp[i].arraysize:1)) //if we found one with the index
|
|
basicindex = pp[i].ofs+pp[i].type->size*(pp[i].arraysize?pp[i].arraysize:1); //make sure we don't union it.
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* iterate over every parent-class and see if our method already exists in conflicting nonvirtual type form */
|
|
if (isvirt)
|
|
{
|
|
for(pc = newt; pc && !found; pc = pc->parentclass)
|
|
{
|
|
struct QCC_typeparam_s *pp;
|
|
int numpc;
|
|
int i;
|
|
found = false;
|
|
|
|
if (pc == newt)
|
|
continue;
|
|
|
|
pp = parms;
|
|
numpc = numparms;
|
|
|
|
/* iterate over all of the virtual methods */
|
|
for (i = 0; i < numpc; i++)
|
|
{
|
|
if (pp[i].type->type == newparm->type)
|
|
{
|
|
/* if we found it, abandon this loop - we still have to check the other classes however */
|
|
if (!strcmp(pp[i].paramname, parmname))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* we didn't find it as a field... so check if it exists as a nonvirtual method */
|
|
if (found == false)
|
|
{
|
|
QC_snprintfz(membername, sizeof(membername), "%s::%s", pc->name, parmname);
|
|
|
|
/* if we found it, game over */
|
|
if (QCC_PR_GetDef(NULL, membername, NULL, false, 0, 0))
|
|
QCC_PR_ParseError(0, "%s defined as virtual in %s, but nonvirtual in %s\n", parmname, newt->name, pc->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
parms[numparms].ofs = basicindex; //ulp, its new
|
|
numparms++;
|
|
|
|
if (found)
|
|
continue;
|
|
|
|
if (!*basictypes[newparm->type])
|
|
QCC_PR_ParseError(0, "members of type %s are not supported (%s::%s)\n", basictypenames[newparm->type], classname, parmname);
|
|
|
|
//make sure the union is okay
|
|
d = QCC_PR_GetDef(NULL, parmname, NULL, 0, 0, GDF_CONST);
|
|
if (d)
|
|
basicindex = 0;
|
|
else
|
|
{ //don't go all weird with unioning generic fields
|
|
QC_snprintfz(membername, sizeof(membername), "::*%s", basictypenames[newparm->type]);
|
|
d = QCC_PR_GetDef(NULL, membername, NULL, 0, 0, GDF_CONST);
|
|
if (!d)
|
|
{
|
|
d = QCC_PR_GetDef(QCC_PR_FieldType(*basictypes[newparm->type]), membername, NULL, 2, 0, GDF_CONST|GDF_POSTINIT|GDF_USED);
|
|
// for (i = 0; (unsigned int)i < newparm->size*(arraysize?arraysize:1); i++)
|
|
// d->symboldata[i]._int = pr.size_fields+i;
|
|
// pr.size_fields += i;
|
|
|
|
d->used = true;
|
|
d->referenced = true; //always referenced, so you can inherit safely.
|
|
}
|
|
if (d->arraysize < basicindex+(arraysize?arraysize:1))
|
|
{
|
|
if (d->symboldata)
|
|
QCC_PR_ParseError(ERR_INTERNAL, "array members are kinda limited, sorry. try rearranging them or adding padding for alignment\n"); //FIXME: add relocs to cope with this all of a type can then be contiguous and thus allow arrays.
|
|
else
|
|
{
|
|
int newsize = basicindex+(arraysize?arraysize:1);
|
|
if (d->type->type == ev_union || d->type->type == ev_struct)
|
|
d->arraysize = newsize;
|
|
else while(d->arraysize < newsize)
|
|
{
|
|
QC_snprintfz(membername, sizeof(membername), "::%s[%i]", basictypenames[newparm->type], d->arraysize/d->type->size);
|
|
QCC_PR_DummyDef(d->type, membername, d->scope, 0, d, d->arraysize, true, GDF_CONST);
|
|
d->arraysize+=d->type->size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
QCC_FreeDef(d);
|
|
|
|
//and make sure we can do member::__fname
|
|
//actually, that seems pointless.
|
|
QC_snprintfz(membername, sizeof(membername), "%s::"MEMBERFIELDNAME, classname, parmname);
|
|
// externs->Printf("define %s -> %s\n", membername, d->name);
|
|
d = QCC_PR_DummyDef(fieldtype, membername, pr_scope, arraysize, d, basicindex, true, (isnull?0:GDF_CONST)|(opt_classfields?GDF_STRIP:0));
|
|
d->referenced = true; //always referenced, so you can inherit safely.
|
|
}
|
|
}
|
|
|
|
if (redeclaration)
|
|
{
|
|
int i;
|
|
redeclaration = newt->num_parms != numparms;
|
|
|
|
for (i = 0; i < numparms && (unsigned int)i < newt->num_parms; i++)
|
|
{
|
|
if (newt->params[i].arraysize != parms[i].arraysize || typecmp(newt->params[i].type, parms[i].type) || strcmp(newt->params[i].paramname, parms[i].paramname))
|
|
{
|
|
QCC_PR_ParseError(ERR_REDECLARATION, "Incompatible redeclaration of class %s. %s differs.", classname, parms[i].paramname);
|
|
break;
|
|
}
|
|
}
|
|
if (newt->num_parms != numparms)
|
|
QCC_PR_ParseError(ERR_REDECLARATION, "Incompatible redeclaration of class %s.", classname);
|
|
}
|
|
else
|
|
{
|
|
newt->num_parms = numparms;
|
|
newt->params = qccHunkAlloc(sizeof(*type->params) * numparms);
|
|
memcpy(newt->params, parms, sizeof(*type->params) * numparms);
|
|
}
|
|
free(parms);
|
|
|
|
{
|
|
QCC_def_t *d;
|
|
//if there's a constructor, make sure the spawnfunc_ function is defined so that its available to maps.
|
|
QC_snprintfz(membername, sizeof(membername), "spawnfunc_%s", classname);
|
|
d = QCC_PR_GetDef(type_function, membername, NULL, true, 0, GDF_CONST);
|
|
d->funccalled = true;
|
|
d->referenced = true;
|
|
QCC_FreeDef(d);
|
|
}
|
|
|
|
QCC_PR_Expect(";");
|
|
return NULL;
|
|
}
|
|
|
|
if (QCC_PR_CheckKeyword(keyword_enum, "enum"))
|
|
{
|
|
return QCC_PR_ParseEnum(false);
|
|
}
|
|
if (QCC_PR_CheckKeyword(keyword_enumflags, "enumflags"))
|
|
{
|
|
return QCC_PR_ParseEnum(true);
|
|
}
|
|
|
|
structtype = ev_void;
|
|
if (QCC_PR_CheckKeyword (keyword_union, "union"))
|
|
structtype = ev_union;
|
|
else if (QCC_PR_CheckKeyword (keyword_struct, "struct"))
|
|
structtype = ev_struct; //defaults to public
|
|
// else if (QCC_PR_CheckKeyword (keyword_class, "class"))
|
|
// structtype = ev_struct; //defaults to private. no other difference.
|
|
if (structtype != ev_void)
|
|
{
|
|
struct QCC_typeparam_s *parms = NULL, *oldparm;
|
|
int numparms = 0;
|
|
int ofs;
|
|
unsigned int arraysize;
|
|
char *parmname;
|
|
|
|
pbool isnonvirt = false;
|
|
pbool isstatic = false;
|
|
pbool isvirt = false;
|
|
pbool definedsomething = false;
|
|
|
|
if (QCC_PR_CheckToken("{"))
|
|
{
|
|
//nameless struct
|
|
newt = QCC_PR_NewType(structtype==ev_union?"<union>":"<struct>", structtype, false);
|
|
}
|
|
else
|
|
{
|
|
QCC_type_t *parenttype;
|
|
char *tname = QCC_PR_ParseName();
|
|
|
|
if (structtype == ev_struct && QCC_PR_CheckToken(":"))
|
|
{
|
|
char *parentname = QCC_PR_ParseName();
|
|
parenttype = QCC_TypeForName(parentname);
|
|
if (!parenttype)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Parent type %s was not yet defined", parentname);
|
|
if (parenttype->type != ev_struct)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Parent type %s is not a struct", parentname);
|
|
}
|
|
else
|
|
parenttype = NULL;
|
|
|
|
newt = QCC_TypeForName(tname);
|
|
if (!newt)
|
|
{
|
|
newt = QCC_PR_NewType(tname, ev_struct, true);
|
|
newt->parentclass = parenttype;
|
|
}
|
|
else if (!newt->size && !newt->parentclass)
|
|
newt->parentclass = parenttype;
|
|
else if (parenttype && newt->parentclass != parenttype)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Redeclaration of struct with different parent type", tname, parenttype->name);
|
|
|
|
if (newt->size)
|
|
{
|
|
if (QCC_PR_CheckToken("{"))
|
|
QCC_PR_ParseError(ERR_NOTANAME, "%s %s is already defined", structtype==ev_union?"union":"struct", newt->name);
|
|
return newt;
|
|
}
|
|
|
|
//struct declaration only, not definition.
|
|
if (parenttype)
|
|
QCC_PR_Expect("{");
|
|
else if (!QCC_PR_CheckToken("{"))
|
|
return newt;
|
|
}
|
|
if (newt->parentclass)
|
|
newt->size = newt->parentclass->size;
|
|
else
|
|
newt->size=0;
|
|
|
|
type = NULL;
|
|
|
|
newparm = NULL;
|
|
for (;;)
|
|
{
|
|
//in qc, functions are assignable references like anything else, so no modifiers means the qc will need to assign to it somewhere.
|
|
//virtual functions are still references, but we initialise them somewhere
|
|
//nonvirtual functions and static functions are kinda the same thing
|
|
QCC_sref_t defaultval;
|
|
if (QCC_PR_CheckToken("}"))
|
|
{
|
|
if (newparm)
|
|
QCC_PR_ParseError(ERR_EXPECTED, "missing semi-colon");
|
|
break;
|
|
}
|
|
else if (QCC_PR_CheckToken(";"))
|
|
{
|
|
newparm = NULL;
|
|
continue;
|
|
}
|
|
else if (QCC_PR_CheckToken(","))
|
|
{ //same as last type, unless initial/after-semicolon
|
|
if (!newparm)
|
|
QCC_PR_ParseError(ERR_EXPECTED, "element missing type");
|
|
}
|
|
else
|
|
{ //new type!
|
|
if (newparm)
|
|
QCC_PR_ParseError(ERR_EXPECTED, "missing semi-colon"); //allow a missing semi-colon on functions, for mixed-style functions.
|
|
|
|
//reset these...
|
|
isnonvirt = false;
|
|
isstatic = false;
|
|
isvirt = false;
|
|
|
|
//parse field modifiers
|
|
if (QCC_PR_CheckKeyword(1, "public"))
|
|
{
|
|
/*ispublic = true*/;
|
|
QCC_PR_Expect(":");
|
|
continue;
|
|
}
|
|
else if (QCC_PR_CheckKeyword(1, "private"))
|
|
{
|
|
/*isprivate = true*/;
|
|
QCC_PR_Expect(":");
|
|
continue;
|
|
}
|
|
else if (QCC_PR_CheckKeyword(1, "protected"))
|
|
{
|
|
/*isprotected = true*/;
|
|
QCC_PR_Expect(":");
|
|
continue;
|
|
}
|
|
|
|
//if (QCC_PR_CheckKeyword(1, "nonvirtual"))
|
|
// isnonvirt = true;
|
|
//else
|
|
if (QCC_PR_CheckKeyword(1, "static"))
|
|
isstatic = true;
|
|
else if (QCC_PR_CheckKeyword(1, "virtual"))
|
|
isvirt = true;
|
|
// else if (QCC_PR_CheckKeyword(1, "ignore"))
|
|
// isignored = true;
|
|
// else if (QCC_PR_CheckKeyword(1, "strip"))
|
|
// isignored = true;
|
|
|
|
//now parse the actual type.
|
|
newparm = QCC_PR_ParseType(false, false);
|
|
definedsomething = false;
|
|
}
|
|
type = newparm;
|
|
|
|
while (QCC_PR_CheckToken("*"))
|
|
type = QCC_PointerTypeTo(type);
|
|
|
|
arraysize = 0;
|
|
if (QCC_PR_CheckToken(";"))
|
|
{ //annonymous structs do weird scope stuff.
|
|
if (!definedsomething && (type->type != ev_struct && type->type != ev_union))
|
|
{
|
|
if (flag_qcfuncs && type->type == ev_function && type->aux_type == newt && QCC_PR_PeekToken(";"))
|
|
QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "constructors are not supported in structs at this time", newparm->name);
|
|
else
|
|
QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "declaration does not declare anything (%s)", newparm->name);
|
|
}
|
|
newparm = NULL;
|
|
parmname = "";
|
|
}
|
|
else
|
|
{
|
|
parmname = qccHunkAlloc(strlen(pr_token)+1);
|
|
strcpy(parmname, pr_token);
|
|
QCC_PR_Lex();
|
|
definedsomething = true;
|
|
if (QCC_PR_CheckToken("["))
|
|
{
|
|
arraysize=QCC_PR_IntConstExpr();
|
|
if (!arraysize)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "cannot cope with 0-sized arrays");
|
|
QCC_PR_Expect("]");
|
|
}
|
|
while (QCC_PR_CheckToken("["))
|
|
{
|
|
int nsize=QCC_PR_IntConstExpr();
|
|
if (!nsize)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "cannot cope with 0-sized arrays");
|
|
QCC_PR_Expect("]");
|
|
arraysize *= nsize;
|
|
QCC_PR_ParseWarning(WARN_IGNOREDKEYWORD, "multi-dimensional arrays are not supported. flattening to single array.");
|
|
}
|
|
|
|
if (QCC_PR_CheckToken("("))
|
|
type = QCC_PR_ParseFunctionType(false, type);
|
|
}
|
|
|
|
if (type == newt || ((type->type == ev_struct || type->type == ev_union) && !type->size))
|
|
{
|
|
QCC_PR_ParseWarning(ERR_NOTANAME, "type %s not fully defined yet", type->name);
|
|
continue;
|
|
}
|
|
|
|
if (QCC_PR_CheckToken(":"))
|
|
{
|
|
QCC_PR_IntConstExpr();
|
|
QCC_PR_ParseWarning(WARN_IGNOREDKEYWORD, "bitfields are not supported");
|
|
}
|
|
|
|
if ((isnonvirt || isvirt) && type->type != ev_function)
|
|
QCC_PR_ParseWarning(ERR_INTERNAL, "[non]virtual members must be functions", type->name);
|
|
|
|
//static members are technically just funny-named globals, and do not generate fields.
|
|
if (isnonvirt || isstatic || isvirt)
|
|
{ //either way its a regular global. the difference being static has no implicit this/self argument.
|
|
QCC_def_t *d;
|
|
char membername[2048];
|
|
if (!isstatic)
|
|
type = QCC_PR_MakeThiscall(type, newt);
|
|
|
|
QC_snprintfz(membername, sizeof(membername), "%s::%s", newt->name, parmname);
|
|
d = QCC_PR_GetDef(type, membername, NULL, true, 0, (type->type==ev_function)?GDF_CONST:0);
|
|
if (QCC_PR_CheckToken("=") || (type->type == ev_function && QCC_PR_PeekToken("{")))
|
|
{
|
|
//FIXME: methods cannot be compiled yet, as none of the fields are not actually defined yet.
|
|
pr_classtype = newt;
|
|
QCC_PR_ParseInitializerDef(d, 0);
|
|
pr_classtype = NULL;
|
|
}
|
|
QCC_FreeDef(d);
|
|
if (!QCC_PR_PeekToken(","))
|
|
newparm = NULL;
|
|
|
|
if (isvirt)
|
|
{
|
|
defaultval.ofs = 0;
|
|
defaultval.cast = d->type;
|
|
defaultval.sym = d;
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
defaultval.cast = NULL;
|
|
if (QCC_PR_CheckToken("="))
|
|
{
|
|
defaultval = QCC_PR_ParseDefaultInitialiser(type);
|
|
QCC_PR_ParseWarning(ERR_INTERNAL, "TODO: pre-initialised struct members are not implemented yet", type->name);
|
|
}
|
|
}
|
|
|
|
parms = realloc(parms, sizeof(*parms) * (numparms+4));
|
|
oldparm = QCC_PR_FindStructMember(newt, parmname, &ofs);
|
|
if (oldparm && oldparm->arraysize == arraysize && !typecmp_lax(oldparm->type, type))
|
|
{
|
|
if (!isvirt)
|
|
continue;
|
|
}
|
|
else if (structtype == ev_union)
|
|
{
|
|
ofs = 0;
|
|
if (type->size*(arraysize?arraysize:1) > newt->size)
|
|
newt->size = type->size*(arraysize?arraysize:1);
|
|
}
|
|
else
|
|
{
|
|
ofs = newt->size;
|
|
newt->size += type->size*(arraysize?arraysize:1);
|
|
}
|
|
|
|
parms[numparms].ofs = ofs;
|
|
parms[numparms].arraysize = arraysize;
|
|
parms[numparms].out = false;
|
|
parms[numparms].optional = false;
|
|
parms[numparms].isvirtual = isvirt;
|
|
parms[numparms].paramname = parmname;
|
|
parms[numparms].type = type;
|
|
parms[numparms].defltvalue = defaultval;
|
|
numparms++;
|
|
|
|
/*if (type->type == ev_vector && arraysize == 0)
|
|
{ //add in vec_x/y/z members too.
|
|
int c;
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
parms[numparms].ofs = ofs + c;
|
|
parms[numparms].arraysize = arraysize;
|
|
parms[numparms].out = false;
|
|
parms[numparms].optional = true;
|
|
parms[numparms].isvirtual = isvirt;
|
|
parms[numparms].paramname = qccHunkAlloc(strlen(parmname)+3);
|
|
sprintf(parms[numparms].paramname, "%s_%c", parmname, 'x'+c);
|
|
parms[numparms].type = type_float;
|
|
parms[numparms].defltvalue = nullsref;
|
|
numparms++;
|
|
}
|
|
}*/
|
|
}
|
|
if (!numparms)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "%s %s has no members", structtype==ev_union?"union":"struct", newt->name);
|
|
|
|
newt->num_parms = numparms;
|
|
newt->params = qccHunkAlloc(sizeof(*type->params) * numparms);
|
|
memcpy(newt->params, parms, sizeof(*type->params) * numparms);
|
|
free(parms);
|
|
|
|
return newt;
|
|
}
|
|
|
|
type = QCC_TypeForName(name);
|
|
if (!type)
|
|
{
|
|
if (!*name)
|
|
{
|
|
QCC_PR_ParseError(ERR_NOTANAME, "type missing name");
|
|
return NULL;
|
|
}
|
|
|
|
//some reacc types...
|
|
if (flag_acc && !stricmp("Void", name))
|
|
type = type_void;
|
|
else if (flag_acc && !stricmp("Real", name))
|
|
type = type_float;
|
|
else if (flag_acc && !stricmp("Vector", name))
|
|
type = type_vector;
|
|
else if (flag_acc && !stricmp("Object", name))
|
|
type = type_entity;
|
|
else if (flag_acc && !stricmp("String", name))
|
|
type = type_string;
|
|
else if (flag_acc && !stricmp("PFunc", name))
|
|
type = type_function;
|
|
else
|
|
{
|
|
//try and handle C's types, which have weird and obtuse combinations (like long long, long int, short int).
|
|
pbool isokay = false;
|
|
pbool issigned = false;
|
|
pbool isunsigned = false;
|
|
pbool islong = false;
|
|
pbool isfloat = false;
|
|
int bits = 0;
|
|
|
|
while(true)
|
|
{
|
|
if (!isunsigned && !issigned && QCC_PR_CheckKeyword(keyword_signed, "signed"))
|
|
issigned = isokay = true;
|
|
else if (!issigned && !isunsigned && QCC_PR_CheckKeyword(keyword_unsigned, "unsigned"))
|
|
isunsigned = isokay = true;
|
|
else if (!bits && QCC_PR_CheckKeyword(keyword_long, "long"))
|
|
{
|
|
if (islong)
|
|
bits = 128;
|
|
islong = isokay = true;
|
|
}
|
|
else if ((!bits || bits==16) && (QCC_PR_CheckKeyword(keyword_int, "int") || QCC_PR_CheckKeyword(keyword_integer, "integer")))
|
|
{ //long int, short int, etc are allowed
|
|
if (!bits)
|
|
bits = 32;
|
|
isokay = true;
|
|
}
|
|
else if (!bits && QCC_PR_CheckKeyword(keyword_short, "short"))
|
|
bits = 16, isokay = true;
|
|
else if (!bits && QCC_PR_CheckKeyword(keyword_char, "char"))
|
|
bits = 8, isokay = true;
|
|
else if (!bits && !issigned && !isunsigned && QCC_PR_CheckKeyword(true, "_Bool")) //c99
|
|
{
|
|
if (keyword_int)
|
|
type = type_bint;
|
|
else
|
|
type = type_bfloat;
|
|
goto wasctype;
|
|
}
|
|
|
|
else if (!bits && !islong && QCC_PR_CheckKeyword(keyword_float, "float"))
|
|
bits = 32, isfloat = isokay = true;
|
|
else if ((!bits||islong) && QCC_PR_CheckKeyword(keyword_double, "double"))
|
|
bits = islong?128:64, islong=false, isfloat = isokay = true;
|
|
else
|
|
break;
|
|
}
|
|
if (isokay)
|
|
{
|
|
if (!bits)
|
|
bits = islong?64:32; //<signed|unsigned|long> [int]
|
|
if (isfloat)
|
|
{
|
|
if (isunsigned)
|
|
QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "ignoring unsupported unsigned keyword, type will be signed");
|
|
if (bits > 64)
|
|
type = type_double, QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "long doubles are not supported, using double"); //permitted
|
|
else if (bits == 64)
|
|
type = type_double;
|
|
else
|
|
type = type_float;
|
|
}
|
|
else
|
|
{
|
|
if (bits > 64)
|
|
type = (isunsigned?type_uint64:type_int64), QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "long longs are not supported, using long"); //permitted
|
|
else if (bits == 64)
|
|
type = (isunsigned?type_uint64:type_int64);
|
|
else if (bits == 16)
|
|
type = (isunsigned?type_uint:type_integer), QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "shorts are not supported, using int"); //permitted
|
|
else if (bits == 8)
|
|
type = (isunsigned?type_uint:type_integer), QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "chars are not supported, using int"); //permitted
|
|
else
|
|
type = (isunsigned?type_uint:type_integer);
|
|
}
|
|
goto wasctype;
|
|
}
|
|
|
|
|
|
if (silentfail)
|
|
return NULL;
|
|
|
|
QCC_PR_ParseError (ERR_NOTATYPE, "\"%s\" is not a type", name);
|
|
type = type_float; // shut up compiler warning
|
|
}
|
|
}
|
|
QCC_PR_Lex ();
|
|
wasctype:
|
|
|
|
while (QCC_PR_CheckToken("*"))
|
|
{
|
|
if (QCC_PR_CheckKeyword(keyword_const, "const"))
|
|
QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "ignoring unsupported const keyword");
|
|
type = QCC_PointerTypeTo(type);
|
|
}
|
|
|
|
if (flag_qcfuncs && QCC_PR_CheckToken ("(")) //this is followed by parameters. Must be a function.
|
|
{
|
|
type_inlinefunction = true;
|
|
type = QCC_PR_ParseFunctionType(newtype, type);
|
|
}
|
|
else
|
|
{
|
|
if (newtype)
|
|
{
|
|
type = QCC_PR_DuplicateType(type, false);
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|