9cd988a836
Fixed up cl_indepphysics. Sending is fully independent, bar sync points. Fixed so #if 0 works in qc code. Fixed up error conditions in qclib when features are not supported. The webpage generator will now refcount properly. Fixed error conditions when using glsl shaders. If MULTITHREAD is defined, r_loadlit will not light inside a separate thread. We now generate VBOs for bsp objects. Shaders/rtlights don't use them yet. Fixed up MVD/multiview playback a bit. It now looks like it works! (cl_hightrack will no longer track the same person in all views). Fixed error conditions when attempting to download versioned csprogs. Reduced the number of places that a q3-style marked up string is expanded. I think there are a couple places left still though. Approximated ezquake colour codes. Memory mapped read file access in win32, where we can. Not sure about this. Lets see how things pan out. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3195 fc73d0e0-1445-4013-8a0c-d673dee63da5
3498 lines
72 KiB
C
3498 lines
72 KiB
C
#ifndef MINIMAL
|
|
|
|
#include "qcc.h"
|
|
#ifdef QCC
|
|
#define print printf
|
|
#endif
|
|
#include "time.h"
|
|
|
|
#define MEMBERFIELDNAME "__m%s"
|
|
|
|
#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc
|
|
|
|
void QCC_PR_ConditionCompilation(void);
|
|
pbool QCC_PR_UndefineName(char *name);
|
|
char *QCC_PR_CheakCompConstString(char *def);
|
|
CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def);
|
|
pbool QCC_Include(char *filename);
|
|
|
|
char *compilingfile;
|
|
|
|
int pr_source_line;
|
|
|
|
char *pr_file_p;
|
|
char *pr_line_start; // start of current source line
|
|
|
|
int pr_bracelevel;
|
|
|
|
char pr_token[8192];
|
|
token_type_t pr_token_type;
|
|
QCC_type_t *pr_immediate_type;
|
|
QCC_eval_t pr_immediate;
|
|
|
|
char pr_immediate_string[8192];
|
|
|
|
int pr_error_count;
|
|
int pr_warning_count;
|
|
|
|
|
|
CompilerConstant_t *CompilerConstant;
|
|
int numCompilerConstants;
|
|
extern pbool expandedemptymacro;
|
|
|
|
|
|
|
|
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;// = {ev_void/*, &def_void*/};
|
|
QCC_type_t *type_string;// = {ev_string/*, &def_string*/};
|
|
QCC_type_t *type_float;// = {ev_float/*, &def_float*/};
|
|
QCC_type_t *type_vector;// = {ev_vector/*, &def_vector*/};
|
|
QCC_type_t *type_entity;// = {ev_entity/*, &def_entity*/};
|
|
QCC_type_t *type_field;// = {ev_field/*, &def_field*/};
|
|
QCC_type_t *type_function;// = {ev_function/*, &def_function*/,NULL,&type_void};
|
|
// type_function is a void() function used for state defs
|
|
QCC_type_t *type_pointer;// = {ev_pointer/*, &def_pointer*/};
|
|
QCC_type_t *type_integer;// = {ev_integer/*, &def_integer*/};
|
|
QCC_type_t *type_variant;// = {ev_integer/*, &def_integer*/};
|
|
|
|
QCC_type_t *type_floatfield;// = {ev_field/*, &def_field*/, NULL, &type_float};
|
|
|
|
#ifdef QCCONLY
|
|
const unsigned int type_size[12] = {1, //void
|
|
sizeof(string_t)/4, //string
|
|
1, //float
|
|
3, //vector
|
|
1, //entity
|
|
1, //field
|
|
sizeof(func_t)/4,//function
|
|
sizeof(void *)/4,//pointer
|
|
1, //integer
|
|
1, //fixme: how big should a variant be?
|
|
0, //ev_struct. variable sized.
|
|
0 //ev_union. variable sized.
|
|
};
|
|
#endif
|
|
|
|
/*QCC_def_t def_void = {type_void, "temp"};
|
|
QCC_def_t def_string = {type_string, "temp"};
|
|
QCC_def_t def_float = {type_float, "temp"};
|
|
QCC_def_t def_vector = {type_vector, "temp"};
|
|
QCC_def_t def_entity = {type_entity, "temp"};
|
|
QCC_def_t def_field = {type_field, "temp"};
|
|
QCC_def_t def_function = {type_function, "temp"};
|
|
QCC_def_t def_pointer = {type_pointer, "temp"};
|
|
QCC_def_t def_integer = {type_integer, "temp"};
|
|
*/
|
|
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 (void);
|
|
|
|
|
|
|
|
|
|
//for compiler constants and file includes.
|
|
|
|
typedef struct qcc_includechunk_s {
|
|
struct qcc_includechunk_s *prev;
|
|
char *filename;
|
|
char *currentdatapoint;
|
|
int currentlinenumber;
|
|
CompilerConstant_t *cnst;
|
|
} qcc_includechunk_t;
|
|
qcc_includechunk_t *currentchunk;
|
|
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->currentlinenumber = pr_source_line;
|
|
chunk->cnst = cnst;
|
|
if( cnst )
|
|
{
|
|
cnst->inside++;
|
|
}
|
|
|
|
if (duplicate)
|
|
{
|
|
pr_file_p = qccHunkAlloc(strlen(data)+1);
|
|
strcpy(pr_file_p, data);
|
|
}
|
|
else
|
|
pr_file_p = data;
|
|
}
|
|
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;
|
|
|
|
currentchunk = currentchunk->prev;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
PR_PrintNextLine
|
|
==============
|
|
*/
|
|
void QCC_PR_PrintNextLine (void)
|
|
{
|
|
char *t;
|
|
|
|
printf ("%3i:",pr_source_line);
|
|
for (t=pr_line_start ; *t && *t != '\n' ; t++)
|
|
printf ("%c",*t);
|
|
printf ("\n");
|
|
}
|
|
|
|
extern char qccmsourcedir[];
|
|
//also meant to include it.
|
|
void QCC_FindBestInclude(char *newfile, char *currentfile, char *rootpath)
|
|
{
|
|
char fullname[10248];
|
|
char *stripfrom;
|
|
int doubledots;
|
|
|
|
char *end = fullname;
|
|
|
|
if (!*newfile)
|
|
return;
|
|
|
|
doubledots = 0;
|
|
while(!strncmp(newfile, "../", 3) || !strncmp(newfile, "..\\", 3))
|
|
{
|
|
newfile+=3;
|
|
doubledots++;
|
|
}
|
|
|
|
currentfile += strlen(rootpath); //could this be bad?
|
|
|
|
for(stripfrom = currentfile+strlen(currentfile)-1; stripfrom>currentfile; stripfrom--)
|
|
{
|
|
if (*stripfrom == '/' || *stripfrom == '\\')
|
|
{
|
|
if (doubledots>0)
|
|
doubledots--;
|
|
else
|
|
{
|
|
stripfrom++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
strcpy(end, rootpath); end = end+strlen(end);
|
|
if (*fullname && end[-1] != '/')
|
|
{
|
|
strcpy(end, "/");
|
|
end = end+strlen(end);
|
|
}
|
|
strncpy(end, currentfile, stripfrom - currentfile); end += stripfrom - currentfile; *end = '\0';
|
|
strcpy(end, newfile);
|
|
|
|
QCC_Include(fullname);
|
|
}
|
|
|
|
pbool defaultstatic;
|
|
int ForcedCRC;
|
|
int QCC_PR_LexInteger (void);
|
|
void QCC_AddFile (char *filename);
|
|
void QCC_PR_LexString (void);
|
|
pbool QCC_PR_SimpleGetToken (void);
|
|
|
|
int ParsePrecompilerIf(void)
|
|
{
|
|
CompilerConstant_t *c;
|
|
int eval;
|
|
char *start = pr_file_p;
|
|
if (!QCC_PR_SimpleGetToken())
|
|
{
|
|
if (*pr_file_p == '(')
|
|
{
|
|
eval = ParsePrecompilerIf();
|
|
while (*pr_file_p == ' ' || *pr_file_p == '\t')
|
|
pr_file_p++;
|
|
if (*pr_file_p != ')')
|
|
QCC_PR_ParseError(ERR_EXPECTED, "unclosed bracket condition\n");
|
|
}
|
|
else
|
|
QCC_PR_ParseError(ERR_EXPECTED, "expected bracket or constant\n");
|
|
}
|
|
else if (!strcmp(pr_token, "defined"))
|
|
{
|
|
while (*pr_file_p == ' ' || *pr_file_p == '\t')
|
|
pr_file_p++;
|
|
if (*pr_file_p != '(')
|
|
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
|
|
{
|
|
c = QCC_PR_CheckCompConstDefined(pr_token);
|
|
if (!c)
|
|
eval = atoi(pr_token);
|
|
else
|
|
eval = atoi(c->value);
|
|
}
|
|
|
|
QCC_PR_SimpleGetToken();
|
|
if (!strcmp(pr_token, "||"))
|
|
eval = ParsePrecompilerIf()||eval;
|
|
else if (!strcmp(pr_token, "&&"))
|
|
eval = ParsePrecompilerIf()&&eval;
|
|
else if (!strcmp(pr_token, "<="))
|
|
eval = eval <= ParsePrecompilerIf();
|
|
else if (!strcmp(pr_token, ">="))
|
|
eval = eval >= ParsePrecompilerIf();
|
|
else if (!strcmp(pr_token, "<"))
|
|
eval = eval < ParsePrecompilerIf();
|
|
else if (!strcmp(pr_token, ">"))
|
|
eval = eval > ParsePrecompilerIf();
|
|
else if (!strcmp(pr_token, "!="))
|
|
eval = eval != ParsePrecompilerIf();
|
|
|
|
return eval;
|
|
}
|
|
/*
|
|
==============
|
|
QCC_PR_Precompiler
|
|
==============
|
|
|
|
Runs precompiler stage
|
|
*/
|
|
pbool QCC_PR_Precompiler(void)
|
|
{
|
|
char msg[1024];
|
|
int ifmode;
|
|
int a;
|
|
static int ifs = 0;
|
|
int level; //#if level
|
|
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_ConditionCompilation();
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
else if (!strncmp(directive, "undef", 5))
|
|
{
|
|
pr_file_p = directive+5;
|
|
while(*pr_file_p <= ' ')
|
|
pr_file_p++;
|
|
|
|
QCC_PR_SimpleGetToken ();
|
|
QCC_PR_UndefineName(pr_token);
|
|
|
|
// QCC_PR_ConditionCompilation();
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
else if (!strncmp(directive, "if", 2))
|
|
{
|
|
int originalline = pr_source_line;
|
|
pr_file_p = directive+2;
|
|
if (!strncmp(pr_file_p, "def ", 4))
|
|
{
|
|
ifmode = 0;
|
|
pr_file_p+=4;
|
|
}
|
|
else if (!strncmp(pr_file_p, "ndef ", 5))
|
|
{
|
|
ifmode = 1;
|
|
pr_file_p+=5;
|
|
}
|
|
else
|
|
{
|
|
ifmode = 2;
|
|
pr_file_p+=0;
|
|
//QCC_PR_ParseError("bad \"#if\" type");
|
|
}
|
|
|
|
if (ifmode == 2)
|
|
{
|
|
eval = ParsePrecompilerIf();
|
|
|
|
if(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
QCC_PR_ParseError (ERR_NOENDIF, "junk on the end of #if line");
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
level = 1;
|
|
|
|
if (eval)
|
|
ifs+=1;
|
|
else
|
|
{
|
|
while (1)
|
|
{
|
|
while(*pr_file_p && (*pr_file_p==' ' || *pr_file_p == '\t'))
|
|
pr_file_p++;
|
|
|
|
if (!*pr_file_p)
|
|
{
|
|
pr_source_line = originalline;
|
|
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 (!strncmp(pr_file_p, "else", 4) && level == 1)
|
|
{
|
|
ifs+=1;
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
if (level <= 0)
|
|
break;
|
|
pr_file_p++; //next line
|
|
pr_source_line++;
|
|
}
|
|
}
|
|
}
|
|
else if (!strncmp(directive, "else", 4))
|
|
{
|
|
int originalline = pr_source_line;
|
|
|
|
ifs -= 1;
|
|
level = 1;
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
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 (!strncmp(pr_file_p, "else", 4) && level == 1)
|
|
{
|
|
ifs+=1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
if (level <= 0)
|
|
break;
|
|
pr_file_p++; //go off the end
|
|
pr_source_line++;
|
|
}
|
|
}
|
|
else if (!strncmp(directive, "endif", 5))
|
|
{
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
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 < 1023 && 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, yes, I KNOW we are going to register an error, and not properly leave this function tree, but...
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
|
|
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] != '\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++;
|
|
}
|
|
|
|
QCC_PR_ParseWarning(WARN_PRECOMPILERMESSAGE, "#warning: %s", msg);
|
|
}
|
|
else if (!strncmp(directive, "message", 7))
|
|
{
|
|
pr_file_p = directive+7;
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
|
|
printf("#message: %s\n", msg);
|
|
}
|
|
else if (!strncmp(directive, "copyright", 9))
|
|
{
|
|
pr_file_p = directive+9;
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
|
|
if (strlen(msg) >= sizeof(QCC_copyright))
|
|
QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n");
|
|
strncpy(QCC_copyright, msg, sizeof(QCC_copyright)-1);
|
|
}
|
|
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 < 1023 && 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++;
|
|
}
|
|
|
|
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();
|
|
|
|
pr_file_p++;
|
|
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
else if (!strncmp(directive, "includelist", 11))
|
|
{
|
|
pr_file_p=directive+11;
|
|
|
|
while(*pr_file_p <= ' ')
|
|
pr_file_p++;
|
|
|
|
while(1)
|
|
{
|
|
QCC_PR_LexWhitespace();
|
|
if (!QCC_PR_SimpleGetToken())
|
|
{
|
|
if (!*pr_file_p)
|
|
QCC_Error(ERR_EOF, "eof in includelist");
|
|
else
|
|
{
|
|
pr_file_p++;
|
|
pr_source_line++;
|
|
}
|
|
continue;
|
|
}
|
|
if (!strcmp(pr_token, "#endlist"))
|
|
break;
|
|
|
|
QCC_FindBestInclude(pr_token, compilingfile, qccmsourcedir);
|
|
|
|
if (*pr_file_p == '\r')
|
|
pr_file_p++;
|
|
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
else if (!strncmp(directive, "include", 7))
|
|
{
|
|
char sm;
|
|
|
|
pr_file_p=directive+7;
|
|
|
|
while(*pr_file_p <= ' ')
|
|
pr_file_p++;
|
|
|
|
msg[0] = '\0';
|
|
if (*pr_file_p == '\"')
|
|
sm = '\"';
|
|
else if (*pr_file_p == '<')
|
|
sm = '>';
|
|
else
|
|
{
|
|
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 boundy\n");
|
|
break;
|
|
}
|
|
msg[a++] = *pr_file_p;
|
|
pr_file_p++;
|
|
}
|
|
msg[a] = 0;
|
|
|
|
QCC_FindBestInclude(msg, compilingfile, qccmsourcedir);
|
|
|
|
pr_file_p++;
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0' && *pr_file_p <= ' ')
|
|
pr_file_p++;
|
|
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
else if (!strncmp(directive, "datafile", 8))
|
|
{
|
|
pr_file_p=directive+8;
|
|
|
|
while(*pr_file_p <= ' ')
|
|
pr_file_p++;
|
|
|
|
QCC_PR_LexString();
|
|
printf("Including datafile: %s\n", pr_token);
|
|
QCC_AddFile(pr_token);
|
|
|
|
pr_file_p++;
|
|
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
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))
|
|
{
|
|
extern char destfile[1024];
|
|
pr_file_p=directive+6;
|
|
|
|
while(*pr_file_p <= ' ')
|
|
pr_file_p++;
|
|
|
|
QCC_PR_LexString();
|
|
strcpy(destfile, pr_token);
|
|
printf("Outputfile: %s\n", destfile);
|
|
|
|
pr_file_p++;
|
|
|
|
for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++)
|
|
msg[a] = pr_file_p[a];
|
|
|
|
msg[a-1] = '\0';
|
|
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
{
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
else if (!strncmp(directive, "pragma", 6))
|
|
{
|
|
pr_file_p=directive+6;
|
|
while(*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;
|
|
continue;
|
|
}
|
|
msg[a++] = *pr_file_p;
|
|
}
|
|
|
|
msg[a] = '\0';
|
|
{
|
|
char *end;
|
|
for (end = msg + a-1; end>=msg && *end <= ' '; end--)
|
|
*end = '\0';
|
|
}
|
|
|
|
if (!*qcc_token)
|
|
{
|
|
strcpy(qcc_token, msg);
|
|
msg[0] = '\0';
|
|
}
|
|
|
|
{
|
|
char *end;
|
|
for (end = msg + a-1; end>=msg && *end <= ' '; end--)
|
|
*end = '\0';
|
|
}
|
|
|
|
if (!QC_strcasecmp(qcc_token, "DONT_COMPILE_THIS_FILE"))
|
|
{
|
|
while (*pr_file_p)
|
|
{
|
|
while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line
|
|
pr_file_p++;
|
|
|
|
if (*pr_file_p == '\n')
|
|
{
|
|
pr_file_p++;
|
|
QCC_PR_NewLine(false);
|
|
}
|
|
}
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "COPYRIGHT"))
|
|
{
|
|
if (strlen(msg) >= sizeof(QCC_copyright))
|
|
QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n");
|
|
strncpy(QCC_copyright, msg, sizeof(QCC_copyright)-1);
|
|
}
|
|
else if (!strncmp(qcc_token, "compress", 8))
|
|
{
|
|
extern pbool compressoutput;
|
|
compressoutput = atoi(msg);
|
|
}
|
|
else if (!strncmp(qcc_token, "forcecrc", 8))
|
|
{
|
|
ForcedCRC = atoi(msg);
|
|
}
|
|
else if (!strncmp(qcc_token, "defaultstatic", 13))
|
|
{
|
|
defaultstatic = atoi(msg);
|
|
}
|
|
else if (!strncmp(qcc_token, "sourcefile", 10))
|
|
{
|
|
#define MAXSOURCEFILESLIST 8
|
|
extern char sourcefileslist[MAXSOURCEFILESLIST][1024];
|
|
extern int currentsourcefile;
|
|
extern int numsourcefiles;
|
|
|
|
int i;
|
|
|
|
QCC_COM_Parse(msg);
|
|
|
|
for (i = 0; i < numsourcefiles; i++)
|
|
{
|
|
if (!strcmp(sourcefileslist[i], qcc_token))
|
|
break;
|
|
}
|
|
if (i == numsourcefiles)
|
|
strcpy(sourcefileslist[numsourcefiles++], qcc_token);
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "TARGET"))
|
|
{
|
|
if (qcc_targetformat == QCF_HEXEN2 && numstatements)
|
|
QCC_PR_ParseWarning(WARN_BADTARGET, "Cannot switch from hexen2 target \'%s\'. Ignored.", msg);
|
|
else if (!QC_strcasecmp(msg, "H2") || !QC_strcasecmp(msg, "HEXEN2"))
|
|
{
|
|
if (numstatements)
|
|
QCC_PR_ParseWarning(WARN_BADTARGET, "Cannot switch from hexen2 target \'%s\'. Ignored.", msg);
|
|
else
|
|
qcc_targetformat = QCF_HEXEN2;
|
|
}
|
|
else if (!QC_strcasecmp(msg, "KK7"))
|
|
qcc_targetformat = QCF_KK7;
|
|
else if (!QC_strcasecmp(msg, "DP") || !QC_strcasecmp(msg, "DARKPLACES"))
|
|
qcc_targetformat = QCF_DARKPLACES;
|
|
else if (!QC_strcasecmp(msg, "FTEDEBUG"))
|
|
qcc_targetformat = QCF_FTEDEBUG;
|
|
else if (!QC_strcasecmp(msg, "FTE"))
|
|
qcc_targetformat = QCF_FTE;
|
|
else if (!QC_strcasecmp(msg, "STANDARD") || !QC_strcasecmp(msg, "ID"))
|
|
qcc_targetformat = QCF_STANDARD;
|
|
else if (!QC_strcasecmp(msg, "DEBUG"))
|
|
qcc_targetformat = QCF_FTEDEBUG;
|
|
else
|
|
QCC_PR_ParseWarning(WARN_BADTARGET, "Unknown target \'%s\'. Ignored.", msg);
|
|
}
|
|
else if (!QC_strcasecmp(qcc_token, "PROGS_SRC"))
|
|
{ //doesn't make sence, 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.
|
|
extern char destfile[1024];
|
|
#ifndef QCCONLY
|
|
extern char qccmfilename[1024];
|
|
int p;
|
|
char *s, *s2;
|
|
#endif
|
|
QCC_COM_Parse(msg);
|
|
|
|
#ifndef QCCONLY
|
|
p=0;
|
|
s2 = qcc_token;
|
|
if (!strncmp(s2, "./", 2))
|
|
s2+=2;
|
|
else
|
|
{
|
|
while(!strncmp(s2, "../", 3))
|
|
{
|
|
s2+=3;
|
|
p++;
|
|
}
|
|
}
|
|
strcpy(qccmfilename, qccmsourcedir);
|
|
for (s=qccmfilename+strlen(qccmfilename);p && s>=qccmfilename; s--)
|
|
{
|
|
if (*s == '/' || *s == '\\')
|
|
{
|
|
*(s+1) = '\0';
|
|
p--;
|
|
}
|
|
}
|
|
sprintf(destfile, "%s", s2);
|
|
|
|
while (p>0)
|
|
{
|
|
memmove(destfile+3, destfile, strlen(destfile)+1);
|
|
destfile[0] = '.';
|
|
destfile[1] = '.';
|
|
destfile[2] = '/';
|
|
p--;
|
|
}
|
|
#else
|
|
|
|
strcpy(destfile, qcc_token);
|
|
#endif
|
|
printf("Outputfile: %s\n", destfile);
|
|
}
|
|
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)
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "warning id not recognised");
|
|
else
|
|
{
|
|
int f;
|
|
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;
|
|
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 not recognised");
|
|
|
|
}
|
|
}
|
|
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 = 0;
|
|
else if (!stricmp(qcc_token, "disable") || !stricmp(qcc_token, "off"))
|
|
st = 1;
|
|
else if (!stricmp(qcc_token, "toggle"))
|
|
st = 2;
|
|
else
|
|
{
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "warning state not recognised");
|
|
st = -1;
|
|
}
|
|
if (st>=0)
|
|
{
|
|
int wn;
|
|
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 == 2) //toggle
|
|
qccwarningdisabled[wn] = true - qccwarningdisabled[wn];
|
|
else
|
|
qccwarningdisabled[wn] = st;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
QCC_PR_ParseWarning(WARN_BADPRAGMA, "Unknown pragma \'%s\'", qcc_token);
|
|
}
|
|
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
|
|
==============
|
|
*/
|
|
#if 0
|
|
void QCC_PR_LexString (void)
|
|
{
|
|
int c;
|
|
int len;
|
|
char tmpbuf[2048];
|
|
|
|
char *text;
|
|
char *oldf;
|
|
int oldline;
|
|
|
|
bool fromfile = true;
|
|
|
|
len = 0;
|
|
|
|
text = pr_file_p;
|
|
do
|
|
{
|
|
QCC_COM_Parse(text);
|
|
// print("Next token is \"%s\"\n", com_token);
|
|
if (*text == '\"')
|
|
{
|
|
text++;
|
|
if (fromfile) pr_file_p++;
|
|
}
|
|
do
|
|
{
|
|
c = *text++;
|
|
if (fromfile) pr_file_p++;
|
|
if (!c)
|
|
QCC_PR_ParseError ("EOF inside quote");
|
|
if (c=='\n')
|
|
QCC_PR_ParseError ("newline inside quote");
|
|
if (c=='\\')
|
|
{ // escape char
|
|
c = *text++;
|
|
if (fromfile) pr_file_p++;
|
|
if (!c)
|
|
QCC_PR_ParseError ("EOF inside quote");
|
|
if (c == 'n')
|
|
c = '\n';
|
|
else if (c == '"')
|
|
c = '"';
|
|
else if (c == '\\')
|
|
c = '\\';
|
|
else
|
|
QCC_PR_ParseError ("Unknown escape char");
|
|
}
|
|
else if (c=='\"')
|
|
{
|
|
if (fromfile) pr_file_p++;
|
|
break;
|
|
}
|
|
tmpbuf[len] = c;
|
|
len++;
|
|
} while (1);
|
|
tmpbuf[len] = 0;
|
|
// if (fromfile) pr_file_p++;
|
|
|
|
pr_immediate_type=NULL;
|
|
oldline=pr_source_line;
|
|
oldf=pr_file_p;
|
|
QCC_PR_Lex();
|
|
if (pr_immediate_type == &type_string)
|
|
{
|
|
// print("Appending \"%s\" to \"%s\"\n", pr_immediate_string, tmpbuf);
|
|
strcat(tmpbuf, pr_immediate_string);
|
|
len+=strlen(pr_immediate_string);
|
|
}
|
|
else
|
|
{
|
|
pr_source_line = oldline;
|
|
pr_file_p = oldf-1;
|
|
QCC_PR_LexWhitespace();
|
|
if (*pr_file_p != '\"') //annother string
|
|
break;
|
|
}
|
|
|
|
QCC_PR_LexWhitespace();
|
|
text = pr_file_p;
|
|
|
|
} while (1);
|
|
|
|
strcpy(pr_token, tmpbuf);
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = &type_string;
|
|
strcpy (pr_immediate_string, pr_token);
|
|
|
|
// print("Found \"%s\"\n", pr_immediate_string);
|
|
}
|
|
#else
|
|
void QCC_PR_LexString (void)
|
|
{
|
|
int c;
|
|
int len;
|
|
char *end, *cnst;
|
|
|
|
int texttype=0;
|
|
|
|
len = 0;
|
|
pr_file_p++;
|
|
do
|
|
{
|
|
c = *pr_file_p++;
|
|
if (!c)
|
|
QCC_PR_ParseError (ERR_EOF, "EOF inside quote");
|
|
if (c=='\n')
|
|
QCC_PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, "newline inside quote");
|
|
if (c=='\\')
|
|
{ // escape char
|
|
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 == '"')
|
|
c = '"';
|
|
else if (c == 't')
|
|
c = '\t';
|
|
else if (c == 'a')
|
|
c = '\a';
|
|
else if (c == 'v')
|
|
c = '\v';
|
|
else if (c == 'f')
|
|
c = '\f';
|
|
else if (c == 's' || c == 'b')
|
|
{
|
|
texttype ^= 128;
|
|
continue;
|
|
}
|
|
else if (c == '[')
|
|
c = 16;
|
|
else if (c == ']')
|
|
c = 17;
|
|
else if (c == '{')
|
|
{
|
|
int d;
|
|
c = 0;
|
|
while ((d = *pr_file_p++) != '}')
|
|
{
|
|
c = c * 10 + d - '0';
|
|
if (d < '0' || d > '9' || c > 255)
|
|
QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code");
|
|
}
|
|
}
|
|
else if (c == '<')
|
|
c = 29;
|
|
else if (c == '-')
|
|
c = 30;
|
|
else if (c == '>')
|
|
c = 31;
|
|
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')
|
|
c = 18 + 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);
|
|
}
|
|
else if (c=='\"')
|
|
{
|
|
if (len >= sizeof(pr_immediate_string)-1)
|
|
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_immediate_string)-1);
|
|
|
|
while(*pr_file_p && *pr_file_p <= ' ')
|
|
{
|
|
if (*pr_file_p == '\n')
|
|
{
|
|
pr_file_p++;
|
|
QCC_PR_NewLine(false);
|
|
}
|
|
else
|
|
pr_file_p++;
|
|
}
|
|
if (*pr_file_p == '\"') //have annother go
|
|
{
|
|
pr_file_p++;
|
|
continue;
|
|
}
|
|
pr_token[len] = 0;
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_string;
|
|
strcpy (pr_immediate_string, pr_token);
|
|
return;
|
|
}
|
|
else if (c == '#')
|
|
{
|
|
for (end = pr_file_p; ; end++)
|
|
{
|
|
if (*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_CheakCompConstString(pr_file_p);
|
|
if (cnst==pr_file_p)
|
|
cnst=NULL;
|
|
*end = c;
|
|
c = '#'; //undo
|
|
if (cnst)
|
|
{
|
|
QCC_PR_ParseWarning(WARN_MACROINSTRING, "Macro expansion in string");
|
|
|
|
if (len+strlen(cnst) >= sizeof(pr_token)-1)
|
|
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1);
|
|
|
|
strcpy(pr_token+len, cnst);
|
|
len+=strlen(cnst);
|
|
pr_file_p = end;
|
|
continue;
|
|
}
|
|
}
|
|
else if (c == 0x7C && flag_acc) //reacc support... reacc is strange.
|
|
c = '\n';
|
|
else
|
|
c |= texttype;
|
|
|
|
pr_token[len] = c;
|
|
len++;
|
|
if (len >= sizeof(pr_token)-1)
|
|
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1);
|
|
} while (1);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
==============
|
|
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_token[0] = '0';
|
|
pr_token[1] = 'x';
|
|
len = 2;
|
|
c = *(pr_file_p+=2);
|
|
}
|
|
do
|
|
{
|
|
pr_token[len] = c;
|
|
len++;
|
|
pr_file_p++;
|
|
c = *pr_file_p;
|
|
} while ((c >= '0' && c<= '9') || c == '.' || (c>='a' && c <= 'f'));
|
|
pr_token[len] = 0;
|
|
return atoi (pr_token);
|
|
}
|
|
|
|
void QCC_PR_LexNumber (void)
|
|
{
|
|
int num=0;
|
|
int base=10;
|
|
int c;
|
|
int sign=1;
|
|
if (*pr_file_p == '-')
|
|
{
|
|
sign=-1;
|
|
pr_file_p++;
|
|
}
|
|
if (pr_file_p[1] == 'x')
|
|
{
|
|
pr_file_p+=2;
|
|
base = 16;
|
|
}
|
|
|
|
while((c = *pr_file_p))
|
|
{
|
|
if (c >= '0' && c <= '9')
|
|
{
|
|
num*=base;
|
|
num += c-'0';
|
|
}
|
|
else if (c >= 'a' && c <= 'f')
|
|
{
|
|
num*=base;
|
|
num += c -'a'+10;
|
|
}
|
|
else if (c >= 'A' && c <= 'F')
|
|
{
|
|
num*=base;
|
|
num += c -'A'+10;
|
|
}
|
|
else if (c == '.')
|
|
{
|
|
pr_file_p++;
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = (float)num;
|
|
num = 1;
|
|
while(1)
|
|
{
|
|
c = *pr_file_p;
|
|
if (c >= '0' && c <= '9')
|
|
{
|
|
num*=base;
|
|
pr_immediate._float += (c-'0')/(float)(num);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
pr_file_p++;
|
|
}
|
|
pr_immediate._float *= sign;
|
|
return;
|
|
}
|
|
else if (c == 'i')
|
|
{
|
|
pr_file_p++;
|
|
pr_immediate_type = type_integer;
|
|
pr_immediate._int = num*sign;
|
|
return;
|
|
}
|
|
else break;
|
|
pr_file_p++;
|
|
}
|
|
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = (float)(num*sign);
|
|
}
|
|
|
|
|
|
float 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...
|
|
pr_token[len] = 0;
|
|
return (float)atof (pr_token);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexVector
|
|
|
|
Parses a single quoted vector
|
|
==============
|
|
*/
|
|
void QCC_PR_LexVector (void)
|
|
{
|
|
int i;
|
|
|
|
pr_file_p++;
|
|
|
|
if (*pr_file_p == '\\')
|
|
{//extended character constant
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
pr_file_p++;
|
|
switch(*pr_file_p)
|
|
{
|
|
case 'n':
|
|
pr_immediate._float = '\n';
|
|
break;
|
|
case 'r':
|
|
pr_immediate._float = '\r';
|
|
break;
|
|
case 't':
|
|
pr_immediate._float = '\t';
|
|
break;
|
|
case '\'':
|
|
pr_immediate._float = '\'';
|
|
break;
|
|
case '\"':
|
|
pr_immediate._float = '\"';
|
|
break;
|
|
case '\\':
|
|
pr_immediate._float = '\\';
|
|
break;
|
|
default:
|
|
QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad character constant");
|
|
}
|
|
if (*pr_file_p != '\'')
|
|
QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad character constant");
|
|
pr_file_p++;
|
|
return;
|
|
}
|
|
if (pr_file_p[1] == '\'')
|
|
{//character constant
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = pr_file_p[0];
|
|
pr_file_p+=2;
|
|
return;
|
|
}
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_vector;
|
|
QCC_PR_LexWhitespace ();
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
pr_immediate.vector[i] = QCC_PR_LexFloat ();
|
|
QCC_PR_LexWhitespace ();
|
|
|
|
if (*pr_file_p == '\'' && i == 1)
|
|
{
|
|
if (i < 2)
|
|
QCC_PR_ParseWarning (WARN_FTE_SPECIFIC, "Bad 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
|
|
==============
|
|
*/
|
|
void QCC_PR_LexName (void)
|
|
{
|
|
int c;
|
|
int len;
|
|
|
|
len = 0;
|
|
c = *pr_file_p;
|
|
do
|
|
{
|
|
pr_token[len] = c;
|
|
len++;
|
|
pr_file_p++;
|
|
c = *pr_file_p;
|
|
} while ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
|
|
|| (c >= '0' && c <= '9'));
|
|
|
|
pr_token[len] = 0;
|
|
pr_token_type = tt_name;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexPunctuation
|
|
==============
|
|
*/
|
|
void QCC_PR_LexPunctuation (void)
|
|
{
|
|
int i;
|
|
int len;
|
|
char *p;
|
|
|
|
pr_token_type = tt_punct;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
QCC_PR_ParseError (ERR_UNKNOWNPUCTUATION, "Unknown punctuation");
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
PR_LexWhitespace
|
|
==============
|
|
*/
|
|
void QCC_PR_LexWhitespace (void)
|
|
{
|
|
int c;
|
|
|
|
while (1)
|
|
{
|
|
// skip whitespace
|
|
while ( (c = *pr_file_p) <= ' ')
|
|
{
|
|
if (c=='\n')
|
|
{
|
|
pr_file_p++;
|
|
QCC_PR_NewLine (false);
|
|
if (!pr_file_p)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (c == 0)
|
|
return; // end of file
|
|
pr_file_p++;
|
|
}
|
|
}
|
|
|
|
// skip // comments
|
|
if (c=='/' && pr_file_p[1] == '/')
|
|
{
|
|
while (*pr_file_p && *pr_file_p != '\n')
|
|
pr_file_p++;
|
|
|
|
if (*pr_file_p == '\n')
|
|
pr_file_p++; //don't break on eof.
|
|
QCC_PR_NewLine(false);
|
|
continue;
|
|
}
|
|
|
|
// skip /* */ comments
|
|
if (c=='/' && pr_file_p[1] == '*')
|
|
{
|
|
do
|
|
{
|
|
pr_file_p++;
|
|
if (pr_file_p[0]=='\n')
|
|
{
|
|
pr_file_p++;
|
|
QCC_PR_NewLine(true);
|
|
}
|
|
if (pr_file_p[1] == 0)
|
|
{
|
|
pr_file_p++;
|
|
return;
|
|
}
|
|
} while (pr_file_p[-1] != '*' || pr_file_p[0] != '/');
|
|
pr_file_p++;
|
|
continue;
|
|
}
|
|
|
|
break; // a real character has been found
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
#define MAX_FRAMES 8192
|
|
char pr_framemodelname[64];
|
|
char pr_framemacros[MAX_FRAMES][64];
|
|
int pr_framemacrovalue[MAX_FRAMES];
|
|
int pr_nummacros, pr_oldmacros;
|
|
int pr_macrovalue;
|
|
int pr_savedmacro;
|
|
|
|
void QCC_PR_ClearGrabMacros (void)
|
|
{
|
|
pr_oldmacros = pr_nummacros;
|
|
// pr_nummacros = 0;
|
|
pr_macrovalue = 0;
|
|
pr_savedmacro = -1;
|
|
}
|
|
|
|
int QCC_PR_FindMacro (char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i=pr_nummacros-1 ; i>=0 ; i--)
|
|
{
|
|
if (!STRCMP (name, pr_framemacros[i]))
|
|
{
|
|
return pr_framemacrovalue[i];
|
|
}
|
|
}
|
|
for (i=pr_nummacros-1 ; i>=0 ; i--)
|
|
{
|
|
if (!stricmp (name, pr_framemacros[i]))
|
|
{
|
|
QCC_PR_ParseWarning(WARN_CASEINSENSATIVEFRAMEMACRO, "Case insensative frame macro");
|
|
return pr_framemacrovalue[i];
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
|
|
sprintf (pr_token,"%d", i);
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = (float)i;
|
|
}
|
|
|
|
// 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) <= ' ')
|
|
{
|
|
if (c=='\n' || c == 0)
|
|
return false;
|
|
pr_file_p++;
|
|
}
|
|
if (pr_file_p[0] == '/')
|
|
{
|
|
if (pr_file_p[1] == '/')
|
|
{ //comment alert
|
|
while(*pr_file_p && *pr_file_p != '\n')
|
|
pr_file_p++;
|
|
return false;
|
|
}
|
|
if (pr_file_p[1] == '*')
|
|
return false;
|
|
}
|
|
|
|
i = 0;
|
|
while ( (c = *pr_file_p) > ' ' && c != ',' && c != ';' && c != ')' && c != '(' && c != ']')
|
|
{
|
|
pr_token[i] = c;
|
|
i++;
|
|
pr_file_p++;
|
|
}
|
|
pr_token[i] = 0;
|
|
return i!=0;
|
|
}
|
|
|
|
pbool QCC_PR_LexMacroName(void)
|
|
{
|
|
int c;
|
|
int i;
|
|
|
|
pr_token[0] = 0;
|
|
|
|
// skip whitespace
|
|
while ( (c = *pr_file_p) <= ' ')
|
|
{
|
|
if (c=='\n' || c == 0)
|
|
return false;
|
|
pr_file_p++;
|
|
}
|
|
if (pr_file_p[0] == '/')
|
|
{
|
|
if (pr_file_p[1] == '/')
|
|
{ //comment alert
|
|
while(*pr_file_p && *pr_file_p != '\n')
|
|
pr_file_p++;
|
|
return false;
|
|
}
|
|
if (pr_file_p[1] == '*')
|
|
return false;
|
|
}
|
|
|
|
i = 0;
|
|
while ( (c = *pr_file_p) > ' ' && c != '\n' && c != ',' && c != ';' && c != ')' && c != '(' && c != ']' && !(pr_file_p[0] == '.' && pr_file_p[1] == '.'))
|
|
{
|
|
pr_token[i] = c;
|
|
i++;
|
|
pr_file_p++;
|
|
}
|
|
pr_token[i] = 0;
|
|
return i!=0;
|
|
}
|
|
|
|
void QCC_PR_MacroFrame(char *name, int value)
|
|
{
|
|
int i;
|
|
for (i=pr_nummacros-1 ; i>=0 ; i--)
|
|
{
|
|
if (!STRCMP (name, pr_framemacros[i]))
|
|
{
|
|
pr_framemacrovalue[i] = value;
|
|
if (i>=pr_oldmacros)
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEMACRO, "Duplicate macro defined (%s)", pr_token);
|
|
//else it's from an old file, and shouldn't be mentioned.
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (strlen(name)+1 > sizeof(pr_framemacros[0]))
|
|
QCC_PR_ParseWarning(ERR_TOOMANYFRAMEMACROS, "Name for frame macro %s is too long", name);
|
|
else
|
|
{
|
|
strcpy (pr_framemacros[pr_nummacros], name);
|
|
pr_framemacrovalue[pr_nummacros] = value;
|
|
pr_nummacros++;
|
|
if (pr_nummacros >= MAX_FRAMES)
|
|
QCC_PR_ParseError(ERR_TOOMANYFRAMEMACROS, "Too many frame macros defined");
|
|
}
|
|
}
|
|
|
|
void QCC_PR_ParseFrame (void)
|
|
{
|
|
while (QCC_PR_LexMacroName ())
|
|
{
|
|
QCC_PR_MacroFrame(pr_token, pr_macrovalue++);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PR_LexGrab
|
|
|
|
Deals with counting sequence numbers and replacing frame macros
|
|
==============
|
|
*/
|
|
void QCC_PR_LexGrab (void)
|
|
{
|
|
pr_file_p++; // skip the $
|
|
// if (!QCC_PR_SimpleGetToken ())
|
|
// QCC_PR_ParseError ("hanging $");
|
|
if (*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();
|
|
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);
|
|
|
|
strncpy(pr_framemodelname, pr_token, sizeof(pr_framemodelname)-1);
|
|
pr_framemodelname[sizeof(pr_framemodelname)-1] = '\0';
|
|
|
|
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(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;
|
|
/*
|
|
a = c-CompilerConstant;
|
|
// for (a = 0; a < numCompilerConstants; a++)
|
|
{
|
|
// if (!STRCMP(name, CompilerConstant[a].name))
|
|
{
|
|
memmove(&CompilerConstant[a], &CompilerConstant[a+1], sizeof(CompilerConstant_t) * (numCompilerConstants-a));
|
|
numCompilerConstants--;
|
|
|
|
|
|
|
|
|
|
if (!STRCMP(name, "OP_NODUP"))
|
|
qccop_noduplicatestrings = false;
|
|
|
|
if (!STRCMP(name, "OP_COMP_ALL")) //group
|
|
{
|
|
QCC_PR_UndefineName("OP_COMP_STATEMENTS");
|
|
QCC_PR_UndefineName("OP_COMP_DEFS");
|
|
QCC_PR_UndefineName("OP_COMP_FIELDS");
|
|
QCC_PR_UndefineName("OP_COMP_FUNCTIONS");
|
|
QCC_PR_UndefineName("OP_COMP_STRINGS");
|
|
QCC_PR_UndefineName("OP_COMP_GLOBALS");
|
|
QCC_PR_UndefineName("OP_COMP_LINES");
|
|
QCC_PR_UndefineName("OP_COMP_TYPES");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
// return false;
|
|
*/
|
|
}
|
|
|
|
CompilerConstant_t *QCC_PR_DefineName(char *name)
|
|
{
|
|
int i;
|
|
CompilerConstant_t *cnst;
|
|
|
|
// if (numCompilerConstants >= MAX_CONSTANTS)
|
|
// QCC_PR_ParseError("Too many compiler constants - %i >= %i", numCompilerConstants, MAX_CONSTANTS);
|
|
|
|
if (strlen(name) >= MAXCONSTANTLENGTH || !*name)
|
|
QCC_PR_ParseError(ERR_CONSTANTTOOLONG, "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 = 0;
|
|
strcpy(cnst->name, name);
|
|
cnst->namelen = strlen(name);
|
|
*cnst->value = '\0';
|
|
for (i = 0; i < MAXCONSTANTPARAMS; i++)
|
|
cnst->params[i][0] = '\0';
|
|
|
|
pHash_Add(&compconstantstable, cnst->name, cnst, qccHunkAlloc(sizeof(bucket_t)));
|
|
|
|
if (!STRCMP(name, "OP_NODUP"))
|
|
opt_noduplicatestrings = true;
|
|
|
|
|
|
if (!STRCMP(name, "OP_TIME")) //group - optimize for a fast compiler
|
|
{
|
|
QCC_PR_UndefineName("OP_SIZE");
|
|
QCC_PR_UndefineName("OP_SPEED");
|
|
|
|
QCC_PR_UndefineName("OP_NODUP");
|
|
QCC_PR_UndefineName("OP_COMP_ALL");
|
|
}
|
|
|
|
if (!STRCMP(name, "OP_SPEED")) //group - optimize run speed
|
|
{
|
|
QCC_PR_UndefineName("OP_SIZE");
|
|
QCC_PR_UndefineName("OP_TIME");
|
|
|
|
// QCC_PR_UndefineName("OP_NODUP");
|
|
QCC_PR_UndefineName("OP_COMP_ALL");
|
|
}
|
|
|
|
if (!STRCMP(name, "OP_SIZE")) //group - produce small output.
|
|
{
|
|
QCC_PR_UndefineName("OP_SPEED");
|
|
QCC_PR_UndefineName("OP_TIME");
|
|
|
|
QCC_PR_DefineName("OP_NODUP");
|
|
QCC_PR_DefineName("OP_COMP_ALL");
|
|
}
|
|
|
|
if (!STRCMP(name, "OP_COMP_ALL")) //group - compress the output
|
|
{
|
|
QCC_PR_DefineName("OP_COMP_STATEMENTS");
|
|
QCC_PR_DefineName("OP_COMP_DEFS");
|
|
QCC_PR_DefineName("OP_COMP_FIELDS");
|
|
QCC_PR_DefineName("OP_COMP_FUNCTIONS");
|
|
QCC_PR_DefineName("OP_COMP_STRINGS");
|
|
QCC_PR_DefineName("OP_COMP_GLOBALS");
|
|
QCC_PR_DefineName("OP_COMP_LINES");
|
|
QCC_PR_DefineName("OP_COMP_TYPES");
|
|
}
|
|
|
|
|
|
|
|
return cnst;
|
|
}
|
|
|
|
void QCC_PR_Undefine(void)
|
|
{
|
|
QCC_PR_SimpleGetToken ();
|
|
|
|
QCC_PR_UndefineName(pr_token);
|
|
// QCC_PR_ParseError("%s was not defined.", pr_token);
|
|
}
|
|
|
|
void QCC_PR_ConditionCompilation(void)
|
|
{
|
|
char *oldval;
|
|
char *d;
|
|
char *s;
|
|
int quote=false;
|
|
CompilerConstant_t *cnst;
|
|
|
|
QCC_PR_SimpleGetToken ();
|
|
|
|
if (!QCC_PR_SimpleGetToken ())
|
|
QCC_PR_ParseError(ERR_NONAME, "No name defined for compiler constant");
|
|
|
|
cnst = pHash_Get(&compconstantstable, pr_token);
|
|
if (cnst)
|
|
{
|
|
oldval = cnst->value;
|
|
Hash_Remove(&compconstantstable, pr_token);
|
|
}
|
|
else
|
|
oldval = NULL;
|
|
|
|
cnst = QCC_PR_DefineName(pr_token);
|
|
|
|
if (*pr_file_p == '(')
|
|
{
|
|
s = pr_file_p+1;
|
|
while(*pr_file_p++)
|
|
{
|
|
if (*pr_file_p == ',')
|
|
{
|
|
if (cnst->numparams >= MAXCONSTANTPARAMS)
|
|
QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "May not have more than %i parameters to a macro", MAXCONSTANTPARAMS);
|
|
strncpy(cnst->params[cnst->numparams], s, pr_file_p-s);
|
|
cnst->params[cnst->numparams][pr_file_p-s] = '\0';
|
|
cnst->numparams++;
|
|
pr_file_p++;
|
|
s = pr_file_p;
|
|
}
|
|
if (*pr_file_p == ')')
|
|
{
|
|
if (cnst->numparams >= MAXCONSTANTPARAMS)
|
|
QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "May not have more than %i parameters to a macro", MAXCONSTANTPARAMS);
|
|
strncpy(cnst->params[cnst->numparams], s, pr_file_p-s);
|
|
cnst->params[cnst->numparams][pr_file_p-s] = '\0';
|
|
cnst->numparams++;
|
|
pr_file_p++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else cnst->numparams = -1;
|
|
|
|
s = pr_file_p;
|
|
d = cnst->value;
|
|
while(*s == ' ' || *s == '\t')
|
|
s++;
|
|
while(1)
|
|
{
|
|
if( *s == '\\' )
|
|
{
|
|
// read over a newline if necessary
|
|
if( s[1] == '\n' || s[1] == '\r' )
|
|
{
|
|
s++;
|
|
QCC_PR_NewLine(false);
|
|
*d++ = *s++;
|
|
if( s[-1] == '\r' && s[0] == '\n' )
|
|
{
|
|
*d++ = *s++;
|
|
}
|
|
}
|
|
}
|
|
else if(*s == '\r' || *s == '\n' || *s == '\0')
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!quote && s[0]=='/'&&(s[1]=='/'||s[1]=='*'))
|
|
break;
|
|
if (*s == '\"')
|
|
quote=!quote;
|
|
|
|
*d = *s;
|
|
d++;
|
|
s++;
|
|
}
|
|
*d = '\0';
|
|
d--;
|
|
while(*d<= ' ' && d >= cnst->value)
|
|
*d-- = '\0';
|
|
if (strlen(cnst->value) >= sizeof(cnst->value)) //this is too late.
|
|
QCC_PR_ParseError(ERR_CONSTANTTOOLONG, "Macro %s too long (%i not %i)", cnst->name, strlen(cnst->value), sizeof(cnst->value));
|
|
|
|
if (oldval)
|
|
{ //we always warn if it was already defined
|
|
//we use different warning codes so that -Wno-mundane can be used to ignore identical redefinitions.
|
|
if (strcmp(oldval, cnst->value))
|
|
QCC_PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, "Alternate precompiler definition of %s", pr_token);
|
|
else
|
|
QCC_PR_ParseWarning(WARN_IDENTICALPRECOMPILER, "Identical precompiler definition of %s", pr_token);
|
|
}
|
|
|
|
pr_file_p = s;
|
|
}
|
|
|
|
int QCC_PR_CheakCompConst(void)
|
|
{
|
|
char *oldpr_file_p = pr_file_p;
|
|
int whitestart;
|
|
|
|
CompilerConstant_t *c;
|
|
|
|
char *end;
|
|
for (end = pr_file_p; ; end++)
|
|
{
|
|
if (*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;
|
|
}
|
|
strncpy(pr_token, pr_file_p, end-pr_file_p);
|
|
pr_token[end-pr_file_p]='\0';
|
|
|
|
// printf("%s\n", pr_token);
|
|
c = pHash_Get(&compconstantstable, pr_token);
|
|
|
|
if (c && !c->inside)
|
|
{
|
|
pr_file_p = oldpr_file_p+strlen(c->name);
|
|
while(*pr_file_p == ' ' || *pr_file_p == '\t')
|
|
pr_file_p++;
|
|
if (c->numparams>=0)
|
|
{
|
|
if (*pr_file_p == '(')
|
|
{
|
|
int p;
|
|
char *start;
|
|
char buffer[1024];
|
|
char *paramoffset[MAXCONSTANTPARAMS+1];
|
|
int param=0;
|
|
int plevel=0;
|
|
|
|
pr_file_p++;
|
|
while(*pr_file_p == ' ' || *pr_file_p == '\t')
|
|
pr_file_p++;
|
|
start = pr_file_p;
|
|
while(1)
|
|
{
|
|
// handle strings correctly by ignoring them
|
|
if (*pr_file_p == '\"')
|
|
{
|
|
do {
|
|
pr_file_p++;
|
|
} while( (pr_file_p[-1] == '\\' || pr_file_p[0] != '\"') && *pr_file_p && *pr_file_p != '\n' );
|
|
}
|
|
if (*pr_file_p == '(')
|
|
plevel++;
|
|
else if (!plevel && (*pr_file_p == ',' || *pr_file_p == ')'))
|
|
{
|
|
paramoffset[param++] = start;
|
|
start = pr_file_p+1;
|
|
if (*pr_file_p == ')')
|
|
{
|
|
*pr_file_p = '\0';
|
|
pr_file_p++;
|
|
break;
|
|
}
|
|
*pr_file_p = '\0';
|
|
pr_file_p++;
|
|
while(*pr_file_p == ' ' || *pr_file_p == '\t')
|
|
{
|
|
pr_file_p++;
|
|
start++;
|
|
}
|
|
// move back by one char because we move forward by one at the end of the loop
|
|
pr_file_p--;
|
|
if (param == MAXCONSTANTPARAMS)
|
|
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 = '\0';
|
|
|
|
oldpr_file_p = pr_file_p;
|
|
pr_file_p = c->value;
|
|
for(;;)
|
|
{
|
|
whitestart = p = strlen(buffer);
|
|
while(*pr_file_p <= ' ') //copy across whitespace
|
|
{
|
|
if (!*pr_file_p)
|
|
break;
|
|
buffer[p++] = *pr_file_p++;
|
|
}
|
|
buffer[p] = 0;
|
|
|
|
if(*pr_file_p == '\"')
|
|
{
|
|
do
|
|
{
|
|
buffer[p++] = *pr_file_p;
|
|
++pr_file_p;
|
|
} while( (pr_file_p[-1] == '\\' || pr_file_p[0] != '\"') && *pr_file_p && *pr_file_p != '\n' );
|
|
buffer[p++] = *pr_file_p; // copy the end-quote too
|
|
buffer[p] = 0;
|
|
++pr_file_p; // and skip it
|
|
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 (srip out whitespace)
|
|
buffer[whitestart] = '\0';
|
|
pr_file_p+=2;
|
|
}
|
|
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]))
|
|
{
|
|
strcat(buffer, "\"");
|
|
strcat(buffer, paramoffset[p]);
|
|
strcat(buffer, "\"");
|
|
break;
|
|
}
|
|
}
|
|
if (p == param)
|
|
{
|
|
strcat(buffer, "#");
|
|
strcat(buffer, qcc_token);
|
|
//QCC_PR_ParseWarning(0, "Stringification ignored");
|
|
}
|
|
continue; //already did this one
|
|
}
|
|
}
|
|
|
|
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]))
|
|
{
|
|
strcat(buffer, paramoffset[p]);
|
|
break;
|
|
}
|
|
}
|
|
if (p == param)
|
|
strcat(buffer, qcc_token);
|
|
}
|
|
|
|
for (p = 0; p < param-1; p++)
|
|
paramoffset[p][strlen(paramoffset[p])] = ',';
|
|
paramoffset[p][strlen(paramoffset[p])] = ')';
|
|
|
|
pr_file_p = oldpr_file_p;
|
|
if (!*buffer)
|
|
expandedemptymacro = true;
|
|
QCC_PR_IncludeChunkEx(buffer, true, NULL, c);
|
|
}
|
|
else
|
|
QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Macro without opening brace");
|
|
}
|
|
else
|
|
{
|
|
if (!*c->value)
|
|
expandedemptymacro = true;
|
|
QCC_PR_IncludeChunkEx(c->value, false, NULL, c);
|
|
}
|
|
|
|
QCC_PR_Lex();
|
|
return true;
|
|
}
|
|
|
|
if (!strncmp(pr_file_p, "__TIME__", 8))
|
|
{
|
|
static char retbuf[128];
|
|
|
|
time_t long_time;
|
|
time( &long_time );
|
|
strftime( retbuf, sizeof(retbuf),
|
|
"\"%H:%M\"", localtime( &long_time ));
|
|
|
|
pr_file_p = retbuf;
|
|
QCC_PR_Lex(); //translate the macro's value
|
|
pr_file_p = oldpr_file_p+8;
|
|
|
|
return true;
|
|
}
|
|
if (!strncmp(pr_file_p, "__DATE__", 8))
|
|
{
|
|
static char retbuf[128];
|
|
|
|
time_t long_time;
|
|
time( &long_time );
|
|
strftime( retbuf, sizeof(retbuf),
|
|
"\"%a %d %b %Y\"", localtime( &long_time ));
|
|
|
|
pr_file_p = retbuf;
|
|
QCC_PR_Lex(); //translate the macro's value
|
|
pr_file_p = oldpr_file_p+8;
|
|
|
|
return true;
|
|
}
|
|
if (!strncmp(pr_file_p, "__FILE__", 8))
|
|
{
|
|
static char retbuf[256];
|
|
sprintf(retbuf, "\"%s\"", strings + s_file);
|
|
pr_file_p = retbuf;
|
|
QCC_PR_Lex(); //translate the macro's value
|
|
pr_file_p = oldpr_file_p+8;
|
|
|
|
return true;
|
|
}
|
|
if (!strncmp(pr_file_p, "__LINE__", 8))
|
|
{
|
|
static char retbuf[256];
|
|
sprintf(retbuf, "\"%i\"", pr_source_line);
|
|
pr_file_p = retbuf;
|
|
QCC_PR_Lex(); //translate the macro's value
|
|
pr_file_p = oldpr_file_p+8;
|
|
return true;
|
|
}
|
|
if (!strncmp(pr_file_p, "__FUNC__", 8))
|
|
{
|
|
static char retbuf[256];
|
|
sprintf(retbuf, "\"%s\"",pr_scope->name);
|
|
pr_file_p = retbuf;
|
|
QCC_PR_Lex(); //translate the macro's value
|
|
pr_file_p = oldpr_file_p+8;
|
|
return true;
|
|
}
|
|
if (!strncmp(pr_file_p, "__NULL__", 8))
|
|
{
|
|
static char retbuf[256];
|
|
sprintf(retbuf, "~0");
|
|
pr_file_p = retbuf;
|
|
QCC_PR_Lex(); //translate the macro's value
|
|
pr_file_p = oldpr_file_p+8;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
char *QCC_PR_CheakCompConstString(char *def)
|
|
{
|
|
char *s;
|
|
|
|
CompilerConstant_t *c;
|
|
|
|
c = pHash_Get(&compconstantstable, def);
|
|
|
|
if (c)
|
|
{
|
|
s = QCC_PR_CheakCompConstString(c->value);
|
|
return s;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def)
|
|
{
|
|
CompilerConstant_t *c = pHash_Get(&compconstantstable, def);
|
|
return c;
|
|
/*int a;
|
|
for (a = 0; a < numCompilerConstants; a++)
|
|
{
|
|
if (!strncmp(def, CompilerConstant[a].name, CompilerConstant[a].namelen+1))
|
|
return &CompilerConstant[a];
|
|
}
|
|
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;
|
|
}
|
|
|
|
QCC_PR_LexWhitespace ();
|
|
|
|
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 == '\"')
|
|
{
|
|
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 == '~' || c == '%') //let's see which one we make into an operator first... possibly both...
|
|
{
|
|
pr_file_p++;
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_integer;
|
|
pr_immediate._int = 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] >='0' && pr_file_p[1] <= '9') || (c >= '0' && c <= '9') || ( c=='-' && pr_file_p[1]>='0' && pr_file_p[1] <='9') )
|
|
{
|
|
pr_token_type = tt_immediate;
|
|
pr_immediate_type = type_float;
|
|
pr_immediate._float = QCC_PR_LexFloat ();
|
|
|
|
// 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'))) //hash and not number
|
|
{
|
|
pr_file_p++;
|
|
if (!QCC_PR_CheakCompConst())
|
|
{
|
|
if (!QCC_PR_SimpleGetToken())
|
|
strcpy(pr_token, "unknown");
|
|
QCC_PR_ParseError(ERR_CONSTANTNOTDEFINED, "Explicit precompiler usage when not defined %s", pr_token);
|
|
}
|
|
else
|
|
if (pr_token_type == tt_eof)
|
|
QCC_PR_Lex();
|
|
|
|
return;
|
|
}
|
|
|
|
if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' )
|
|
{
|
|
if (flag_hashonly || !QCC_PR_CheakCompConst()) //look for a macro.
|
|
QCC_PR_LexName ();
|
|
else
|
|
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 ();
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
|
|
void QCC_PR_ParsePrintDef (int type, QCC_def_t *def)
|
|
{
|
|
if (qccwarningdisabled[type])
|
|
return;
|
|
if (def->s_file)
|
|
printf ("%s:%i: %s is defined here\n", strings + def->s_file, def->s_line, def->name);
|
|
}
|
|
void *errorscope;
|
|
void QCC_PR_PrintScope (void)
|
|
{
|
|
if (pr_scope)
|
|
{
|
|
if (errorscope != pr_scope)
|
|
printf ("in function %s (line %i),\n", pr_scope->name, pr_scope->s_line);
|
|
errorscope = pr_scope;
|
|
}
|
|
else
|
|
{
|
|
if (errorscope)
|
|
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(char *file, int line);
|
|
#endif
|
|
void VARGS QCC_PR_ParseError (int errortype, 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(strings+s_file, pr_source_line);
|
|
#endif
|
|
|
|
QCC_PR_PrintScope();
|
|
printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string);
|
|
|
|
longjmp (pr_parse_abort, 1);
|
|
}
|
|
void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, 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(strings+s_file, pr_source_line);
|
|
#endif
|
|
QCC_PR_PrintScope();
|
|
printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string);
|
|
|
|
QCC_PR_ParsePrintDef(WARN_ERROR, def);
|
|
|
|
longjmp (pr_parse_abort, 1);
|
|
}
|
|
void VARGS QCC_PR_ParseWarning (int type, char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
if (type < ERR_PARSEERRORS && qccwarningdisabled[type])
|
|
return;
|
|
|
|
va_start (argptr,error);
|
|
QC_vsnprintf (string,sizeof(string)-1, error,argptr);
|
|
va_end (argptr);
|
|
|
|
QCC_PR_PrintScope();
|
|
if (type >= ERR_PARSEERRORS)
|
|
{
|
|
printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string);
|
|
pr_error_count++;
|
|
}
|
|
else
|
|
{
|
|
printf ("%s:%i: warning: %s\n", strings + s_file, pr_source_line, string);
|
|
pr_warning_count++;
|
|
}
|
|
}
|
|
|
|
void VARGS QCC_PR_Warning (int type, char *file, int line, char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
if (qccwarningdisabled[type])
|
|
return;
|
|
|
|
va_start (argptr,error);
|
|
QC_vsnprintf (string,sizeof(string)-1, error,argptr);
|
|
va_end (argptr);
|
|
|
|
QCC_PR_PrintScope();
|
|
if (file)
|
|
printf ("%s:%i: warning: %s\n", file, line, string);
|
|
else
|
|
printf ("warning: %s\n", string);
|
|
pr_warning_count++;
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
PR_Expect
|
|
|
|
Issues an error if the current token isn't equal to string
|
|
Gets the next token
|
|
=============
|
|
*/
|
|
#ifndef COMMONINLINES
|
|
void QCC_PR_Expect (char *string)
|
|
{
|
|
if (STRCMP (string, pr_token))
|
|
QCC_PR_ParseError (ERR_EXPECTED, "expected %s, found %s",string, pr_token);
|
|
QCC_PR_Lex ();
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
=============
|
|
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 (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_CheckImmediate (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(char *string)
|
|
{
|
|
if (pr_token_type != tt_name)
|
|
return false;
|
|
if (flag_caseinsensative)
|
|
{
|
|
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, char *string)
|
|
{
|
|
if (!keywordenabled)
|
|
return false;
|
|
if (flag_caseinsensative)
|
|
{
|
|
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)
|
|
{
|
|
static char ident[MAX_NAME];
|
|
char *ret;
|
|
|
|
if (pr_token_type != tt_name)
|
|
QCC_PR_ParseError (ERR_NOTANAME, "\"%s\" - not a name", pr_token);
|
|
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.
|
|
============
|
|
*/
|
|
|
|
//0 if same
|
|
QCC_type_t *QCC_PR_NewType (char *name, int basictype);
|
|
int typecmp(QCC_type_t *a, QCC_type_t *b)
|
|
{
|
|
if (a == b)
|
|
return 0;
|
|
if (!a || !b)
|
|
return 1; //different (^ and not both null)
|
|
|
|
if (a->type != b->type)
|
|
return 1;
|
|
if (a->num_parms != b->num_parms)
|
|
return 1;
|
|
|
|
if (a->size != b->size)
|
|
return 1;
|
|
// if (STRCMP(a->name, b->name)) //This isn't 100% clean.
|
|
// return 1;
|
|
|
|
if (typecmp(a->aux_type, b->aux_type))
|
|
return 1;
|
|
|
|
if (a->param || b->param)
|
|
{
|
|
a = a->param;
|
|
b = b->param;
|
|
|
|
while(a || b)
|
|
{
|
|
if (typecmp(a, b))
|
|
return 1;
|
|
|
|
a=a->next;
|
|
b=b->next;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
QCC_type_t *QCC_PR_DuplicateType(QCC_type_t *in)
|
|
{
|
|
QCC_type_t *out, *op, *ip;
|
|
if (!in)
|
|
return NULL;
|
|
|
|
out = QCC_PR_NewType(in->name, in->type);
|
|
out->aux_type = QCC_PR_DuplicateType(in->aux_type);
|
|
out->param = QCC_PR_DuplicateType(in->param);
|
|
ip = in->param;
|
|
op = NULL;
|
|
while(ip)
|
|
{
|
|
if (!op)
|
|
out->param = op = QCC_PR_DuplicateType(ip);
|
|
else
|
|
op = (op->next = QCC_PR_DuplicateType(ip));
|
|
ip = ip->next;
|
|
}
|
|
out->size = in->size;
|
|
out->num_parms = in->num_parms;
|
|
out->ofs = in->ofs;
|
|
out->name = in->name;
|
|
out->parentclass = in->parentclass;
|
|
|
|
return out;
|
|
}
|
|
|
|
char *TypeName(QCC_type_t *type)
|
|
{
|
|
static char buffer[2][512];
|
|
static int op;
|
|
char *ret;
|
|
|
|
|
|
op++;
|
|
ret = buffer[op&1];
|
|
if (type->type == ev_field)
|
|
{
|
|
type = type->aux_type;
|
|
*ret++ = '.';
|
|
}
|
|
*ret = 0;
|
|
|
|
if (type->type == ev_function)
|
|
{
|
|
strcat(ret, type->aux_type->name);
|
|
strcat(ret, " (");
|
|
type = type->param;
|
|
while(type)
|
|
{
|
|
strcat(ret, type->name);
|
|
type = type->next;
|
|
|
|
if (type)
|
|
strcat(ret, ", ");
|
|
}
|
|
strcat(ret, ")");
|
|
}
|
|
else if (type->type == ev_entity && type->parentclass)
|
|
{
|
|
ret = buffer[op&1];
|
|
*ret = 0;
|
|
strcat(ret, "class ");
|
|
strcat(ret, type->name);
|
|
/* strcat(ret, " {");
|
|
type = type->param;
|
|
while(type)
|
|
{
|
|
strcat(ret, type->name);
|
|
type = type->next;
|
|
|
|
if (type)
|
|
strcat(ret, ", ");
|
|
}
|
|
strcat(ret, "}");
|
|
*/
|
|
}
|
|
else
|
|
strcpy(ret, type->name);
|
|
|
|
return buffer[op&1];
|
|
}
|
|
//#define typecmp(a, b) (a && ((a)->type==(b)->type) && !STRCMP((a)->name, (b)->name))
|
|
|
|
QCC_type_t *QCC_PR_FindType (QCC_type_t *type)
|
|
{
|
|
int t;
|
|
for (t = 0; t < numtypeinfos; t++)
|
|
{
|
|
// check = &qcc_typeinfo[t];
|
|
if (typecmp(&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];
|
|
}
|
|
}
|
|
QCC_Error(ERR_INTERNAL, "Error with type");
|
|
|
|
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(char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < numtypeinfos; i++)
|
|
{
|
|
if (!STRCMP(qcc_typeinfo[i].name, name))
|
|
{
|
|
return &qcc_typeinfo[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
============
|
|
PR_SkipToSemicolon
|
|
|
|
For error recovery, also pops out of nested braces
|
|
============
|
|
*/
|
|
void QCC_PR_SkipToSemicolon (void)
|
|
{
|
|
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
|
|
|
|
pbool recursivefunctiontype;
|
|
|
|
QCC_type_t *QCC_PR_NewType (char *name, int basictype);
|
|
//expects a ( to have already been parsed.
|
|
QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype)
|
|
{
|
|
QCC_type_t *ftype, *ptype, *nptype;
|
|
char *name;
|
|
int definenames = !recursivefunctiontype;
|
|
|
|
recursivefunctiontype++;
|
|
|
|
ftype = QCC_PR_NewType(type_function->name, ev_function);
|
|
|
|
ftype->aux_type = returntype; // return type
|
|
ftype->num_parms = 0;
|
|
ptype = NULL;
|
|
|
|
|
|
if (!QCC_PR_CheckToken (")"))
|
|
{
|
|
if (QCC_PR_CheckToken ("..."))
|
|
ftype->num_parms = -1; // variable args
|
|
else
|
|
do
|
|
{
|
|
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 ("..."))
|
|
{
|
|
ftype->num_parms = (ftype->num_parms * -1) - 1;
|
|
break;
|
|
}
|
|
|
|
nptype = QCC_PR_ParseType(true);
|
|
|
|
if (nptype->type == ev_void)
|
|
break;
|
|
if (!ptype)
|
|
{
|
|
ptype = nptype;
|
|
ftype->param = ptype;
|
|
}
|
|
else
|
|
{
|
|
ptype->next = nptype;
|
|
ptype = ptype->next;
|
|
}
|
|
// type->name = "FUNC PARAMETER";
|
|
|
|
|
|
if (STRCMP(pr_token, ",") && STRCMP(pr_token, ")"))
|
|
{
|
|
name = QCC_PR_ParseName ();
|
|
if (definenames)
|
|
strcpy (pr_parm_names[ftype->num_parms], name);
|
|
}
|
|
else if (definenames)
|
|
strcpy (pr_parm_names[ftype->num_parms], "");
|
|
ftype->num_parms++;
|
|
} while (QCC_PR_CheckToken (","));
|
|
|
|
QCC_PR_Expect (")");
|
|
}
|
|
recursivefunctiontype--;
|
|
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, *ptype, *nptype;
|
|
char *name;
|
|
char argname[64];
|
|
int definenames = !recursivefunctiontype;
|
|
|
|
recursivefunctiontype++;
|
|
|
|
ftype = QCC_PR_NewType(type_function->name, ev_function);
|
|
|
|
ftype->aux_type = returntype; // return type
|
|
ftype->num_parms = 0;
|
|
ptype = NULL;
|
|
|
|
|
|
if (!QCC_PR_CheckToken (")"))
|
|
{
|
|
if (QCC_PR_CheckToken ("..."))
|
|
ftype->num_parms = -1; // variable args
|
|
else
|
|
do
|
|
{
|
|
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 ("..."))
|
|
{
|
|
ftype->num_parms = (ftype->num_parms * -1) - 1;
|
|
break;
|
|
}
|
|
|
|
if (QCC_PR_CheckName("arg"))
|
|
{
|
|
sprintf(argname, "arg%i", ftype->num_parms);
|
|
name = argname;
|
|
nptype = QCC_PR_NewType("Variant", ev_variant);
|
|
}
|
|
else if (QCC_PR_CheckName("vect")) //this can only be of vector sizes, so...
|
|
{
|
|
sprintf(argname, "arg%i", ftype->num_parms);
|
|
name = argname;
|
|
nptype = QCC_PR_NewType("Vector", ev_vector);
|
|
}
|
|
else
|
|
{
|
|
name = QCC_PR_ParseName();
|
|
QCC_PR_Expect(":");
|
|
nptype = QCC_PR_ParseType(true);
|
|
}
|
|
|
|
if (nptype->type == ev_void)
|
|
break;
|
|
if (!ptype)
|
|
{
|
|
ptype = nptype;
|
|
ftype->param = ptype;
|
|
}
|
|
else
|
|
{
|
|
ptype->next = nptype;
|
|
ptype = ptype->next;
|
|
}
|
|
// type->name = "FUNC PARAMETER";
|
|
|
|
if (definenames)
|
|
strcpy (pr_parm_names[ftype->num_parms], name);
|
|
ftype->num_parms++;
|
|
} while (QCC_PR_CheckToken (";"));
|
|
|
|
QCC_PR_Expect (")");
|
|
}
|
|
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];
|
|
sprintf(name, "*%s", pointsto->name);
|
|
ptype = QCC_PR_NewType(name, ev_pointer);
|
|
ptype->aux_type = pointsto;
|
|
return QCC_PR_FindType (ptype);
|
|
}
|
|
QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto)
|
|
{
|
|
QCC_type_t *ptype;
|
|
char name[128];
|
|
sprintf(name, "FIELD TYPE(%s)", pointsto->name);
|
|
ptype = QCC_PR_NewType(name, ev_field);
|
|
ptype->aux_type = pointsto;
|
|
ptype->size = ptype->aux_type->size;
|
|
return QCC_PR_FindType (ptype);
|
|
}
|
|
|
|
pbool type_inlinefunction;
|
|
QCC_type_t *QCC_PR_ParseType (int newtype)
|
|
{
|
|
QCC_type_t *newparm;
|
|
QCC_type_t *newt;
|
|
QCC_type_t *type;
|
|
char *name;
|
|
int i;
|
|
|
|
type_inlinefunction = false; //doesn't really matter so long as its not from an inline function type
|
|
|
|
// int ofs;
|
|
|
|
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);
|
|
newt->aux_type = QCC_PR_ParseType (false);
|
|
|
|
newt->size = newt->aux_type->size;
|
|
|
|
newt = QCC_PR_FindType (newt);
|
|
|
|
type = QCC_PR_NewType("FIELD TYPE", ev_field);
|
|
type->aux_type = newt;
|
|
|
|
type->size = type->aux_type->size;
|
|
|
|
if (newtype)
|
|
return type;
|
|
return QCC_PR_FindType (type);
|
|
}
|
|
if (QCC_PR_CheckToken ("."))
|
|
{
|
|
newt = QCC_PR_NewType("FIELD TYPE", ev_field);
|
|
newt->aux_type = QCC_PR_ParseType (false);
|
|
|
|
newt->size = newt->aux_type->size;
|
|
|
|
if (newtype)
|
|
return newt;
|
|
return QCC_PR_FindType (newt);
|
|
}
|
|
|
|
name = QCC_PR_CheakCompConstString(pr_token);
|
|
|
|
if (QCC_PR_CheckKeyword (keyword_class, "class"))
|
|
{
|
|
// int parms;
|
|
QCC_type_t *fieldtype;
|
|
char membername[2048];
|
|
char *classname = QCC_PR_ParseName();
|
|
int forwarddeclaration;
|
|
|
|
newt = 0;
|
|
|
|
/* Don't advance the line number yet */
|
|
forwarddeclaration = pr_token[0] == ';';
|
|
|
|
/* Look to see if this type is already defined */
|
|
for(i=0;i<numtypeinfos;i++)
|
|
{
|
|
if (STRCMP(qcc_typeinfo[i].name, classname) == 0)
|
|
{
|
|
newt = &qcc_typeinfo[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (newt && forwarddeclaration)
|
|
QCC_PR_ParseError(ERR_REDECLARATION, "Forward declaration of already defined class %s", classname);
|
|
|
|
if (newt && newt->num_parms != 0)
|
|
QCC_PR_ParseError(ERR_REDECLARATION, "Redeclaration of class %s", classname);
|
|
|
|
if (!newt)
|
|
newt = QCC_PR_NewType(classname, ev_entity);
|
|
|
|
newt->size=type_entity->size;
|
|
|
|
type = NULL;
|
|
|
|
if (forwarddeclaration)
|
|
{
|
|
QCC_PR_CheckToken(";");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
if (QCC_PR_CheckToken(":"))
|
|
{
|
|
char *parentname = QCC_PR_ParseName();
|
|
newt->parentclass = QCC_TypeForName(parentname);
|
|
if (!newt->parentclass)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "Parent class %s was not defined", parentname);
|
|
}
|
|
else
|
|
newt->parentclass = type_entity;
|
|
|
|
|
|
QCC_PR_Expect("{");
|
|
if (QCC_PR_CheckToken(","))
|
|
QCC_PR_ParseError(ERR_NOTANAME, "member missing name");
|
|
while (!QCC_PR_CheckToken("}"))
|
|
{
|
|
// if (QCC_PR_CheckToken(","))
|
|
// type->next = QCC_PR_NewType(type->name, type->type);
|
|
// else
|
|
newparm = QCC_PR_ParseType(true);
|
|
|
|
if (newparm->type == ev_struct || newparm->type == ev_union) //we wouldn't be able to handle it.
|
|
QCC_PR_ParseError(ERR_INTERNAL, "Struct or union in class %s", classname);
|
|
|
|
if (!QCC_PR_CheckToken(";"))
|
|
{
|
|
newparm->name = QCC_CopyString(pr_token)+strings;
|
|
QCC_PR_Lex();
|
|
if (QCC_PR_CheckToken("["))
|
|
{
|
|
type->next->size*=atoi(pr_token);
|
|
QCC_PR_Lex();
|
|
QCC_PR_Expect("]");
|
|
}
|
|
QCC_PR_CheckToken(";");
|
|
}
|
|
else
|
|
newparm->name = QCC_CopyString("")+strings;
|
|
|
|
sprintf(membername, "%s::"MEMBERFIELDNAME, classname, newparm->name);
|
|
fieldtype = QCC_PR_NewType(newparm->name, ev_field);
|
|
fieldtype->aux_type = newparm;
|
|
fieldtype->size = newparm->size;
|
|
QCC_PR_GetDef(fieldtype, membername, pr_scope, 2, 1, false);
|
|
|
|
|
|
newparm->ofs = 0;//newt->size;
|
|
newt->num_parms++;
|
|
|
|
if (type)
|
|
type->next = newparm;
|
|
else
|
|
newt->param = newparm;
|
|
|
|
type = newparm;
|
|
}
|
|
|
|
|
|
QCC_PR_Expect(";");
|
|
return NULL;
|
|
}
|
|
if (QCC_PR_CheckKeyword (keyword_struct, "struct"))
|
|
{
|
|
newt = QCC_PR_NewType("struct", ev_struct);
|
|
newt->size=0;
|
|
QCC_PR_Expect("{");
|
|
|
|
type = NULL;
|
|
if (QCC_PR_CheckToken(","))
|
|
QCC_PR_ParseError(ERR_NOTANAME, "element missing name");
|
|
|
|
newparm = NULL;
|
|
while (!QCC_PR_CheckToken("}"))
|
|
{
|
|
if (QCC_PR_CheckToken(","))
|
|
{
|
|
if (!newparm)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "element missing type");
|
|
newparm = QCC_PR_NewType(newparm->name, newparm->type);
|
|
}
|
|
else
|
|
newparm = QCC_PR_ParseType(true);
|
|
|
|
if (!QCC_PR_CheckToken(";"))
|
|
{
|
|
newparm->name = QCC_CopyString(pr_token)+strings;
|
|
QCC_PR_Lex();
|
|
if (QCC_PR_CheckToken("["))
|
|
{
|
|
newparm->size*=atoi(pr_token);
|
|
QCC_PR_Lex();
|
|
QCC_PR_Expect("]");
|
|
}
|
|
QCC_PR_CheckToken(";");
|
|
}
|
|
else
|
|
newparm->name = QCC_CopyString("")+strings;
|
|
newparm->ofs = newt->size;
|
|
newt->size += newparm->size;
|
|
newt->num_parms++;
|
|
|
|
if (type)
|
|
type->next = newparm;
|
|
else
|
|
newt->param = newparm;
|
|
type = newparm;
|
|
}
|
|
return newt;
|
|
}
|
|
if (QCC_PR_CheckKeyword (keyword_union, "union"))
|
|
{
|
|
newt = QCC_PR_NewType("union", ev_union);
|
|
newt->size=0;
|
|
QCC_PR_Expect("{");
|
|
|
|
type = NULL;
|
|
if (QCC_PR_CheckToken(","))
|
|
QCC_PR_ParseError(ERR_NOTANAME, "element missing name");
|
|
newparm = NULL;
|
|
while (!QCC_PR_CheckToken("}"))
|
|
{
|
|
if (QCC_PR_CheckToken(","))
|
|
{
|
|
if (!newparm)
|
|
QCC_PR_ParseError(ERR_NOTANAME, "element missing type");
|
|
newparm = QCC_PR_NewType(newparm->name, newparm->type);
|
|
}
|
|
else
|
|
newparm = QCC_PR_ParseType(true);
|
|
if (QCC_PR_CheckToken(";"))
|
|
newparm->name = QCC_CopyString("")+strings;
|
|
else
|
|
{
|
|
newparm->name = QCC_CopyString(pr_token)+strings;
|
|
QCC_PR_Lex();
|
|
QCC_PR_Expect(";");
|
|
}
|
|
newparm->ofs = 0;
|
|
if (newparm->size > newt->size)
|
|
newt->size = newparm->size;
|
|
newt->num_parms++;
|
|
|
|
if (type)
|
|
type->next = newparm;
|
|
else
|
|
newt->param = newparm;
|
|
type = newparm;
|
|
}
|
|
return newt;
|
|
}
|
|
type = NULL;
|
|
for (i = 0; i < numtypeinfos; i++)
|
|
{
|
|
if (!STRCMP(qcc_typeinfo[i].name, name))
|
|
{
|
|
type = &qcc_typeinfo[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == numtypeinfos)
|
|
{
|
|
if (!*name)
|
|
return NULL;
|
|
if (!stricmp("Void", name))
|
|
type = type_void;
|
|
else if (!stricmp("Real", name))
|
|
type = type_float;
|
|
else if (!stricmp("Vector", name))
|
|
type = type_vector;
|
|
else if (!stricmp("Object", name))
|
|
type = type_entity;
|
|
else if (!stricmp("String", name))
|
|
type = type_string;
|
|
else if (!stricmp("PFunc", name))
|
|
type = type_function;
|
|
else
|
|
{
|
|
QCC_PR_ParseError (ERR_NOTATYPE, "\"%s\" is not a type", name);
|
|
type = type_float; // shut up compiler warning
|
|
}
|
|
}
|
|
QCC_PR_Lex ();
|
|
|
|
if (QCC_PR_CheckToken ("(")) //this is followed by parameters. Must be a function.
|
|
{
|
|
type_inlinefunction = true;
|
|
return QCC_PR_ParseFunctionType(newtype, type);
|
|
}
|
|
else
|
|
{
|
|
if (newtype)
|
|
{
|
|
type = QCC_PR_DuplicateType(type);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|