diff --git a/engine/qclib/Makefile b/engine/qclib/Makefile index 4cbafe7ea..a09465910 100644 --- a/engine/qclib/Makefile +++ b/engine/qclib/Makefile @@ -87,6 +87,6 @@ testapp.bin: qcvm.so test.o $(CC) $(BASE_CFLAGS) -o testapp.bin -O3 $(BASE_LDFLAGS) qcvm.so test.o tests: testapp.bin - @$(foreach a,$(wildcard tests/*.src), echo TEST: $a; ./testapp.bin progs.dat -srcfile $a; echo; echo) + @$(foreach a,$(wildcard tests/*.src), echo TEST: $a; rm progs.dat; ./testapp.bin progs.dat -srcfile $a; echo; echo) .PHONY: tests \ No newline at end of file diff --git a/engine/qclib/cmdlib.h b/engine/qclib/cmdlib.h index 418b48ad1..652a8e6d6 100644 --- a/engine/qclib/cmdlib.h +++ b/engine/qclib/cmdlib.h @@ -39,8 +39,18 @@ int QC_strcasecmp (const char *s1, const char *s2); #ifdef _MSC_VER #define QC_vsnprintf _vsnprintf +static void VARGS QC_snprintfz (char *dest, size_t size, const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + vsnprintf (dest, size-1, fmt, args); + va_end (args); + //make sure its terminated. + dest[size-1] = 0; +} #else #define QC_vsnprintf vsnprintf +#define QC_snprintfz snprintf #endif #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) @@ -62,7 +72,7 @@ int SafeOpenWrite (char *filename, int maxsize); int SafeOpenRead (char *filename); void SafeRead (int handle, void *buffer, long count); void SafeWrite (int handle, void *buffer, long count); -void SafeClose(int handle); +pbool SafeClose(int hand); int SafeSeek(int hand, int ofs, int mode); void *SafeMalloc (long size); diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 40f22e8a6..26e715efc 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -411,8 +411,8 @@ reeval: tmpf = OPA->_float; ptr = QCPOINTER(OPB); OPC->_vector[0] = (ptr->_vector[0] *= tmpf); - OPC->_vector[0] = (ptr->_vector[1] *= tmpf); - OPC->_vector[0] = (ptr->_vector[2] *= tmpf); + OPC->_vector[1] = (ptr->_vector[1] *= tmpf); + OPC->_vector[2] = (ptr->_vector[2] *= tmpf); break; case OP_DIVSTORE_F: // f /= f @@ -824,8 +824,8 @@ reeval: case OP_GLOBALADDRESS: OPC->_int = ENGINEPOINTER(&OPA->_int + OPB->_int); /*pointer arithmatic*/ break; - case OP_POINTER_ADD: //pointer to 32 bit (remember to *3 for vectors) - OPC->_int = OPA->_int + OPB->_int*4; + case OP_ADD_PIW: //pointer to 32 bit (remember to *3 for vectors) + OPC->_int = OPA->_int + OPB->_int*sizeof(float); break; case OP_LOADA_I: @@ -882,7 +882,7 @@ reeval: pr_xstatement = st-pr_statements; PR_RunError (&progfuncs->funcs, "bad pointer read in %s", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name)); } - ptr = QCPOINTERM(OPA->_int + OPB->_int*4); + ptr = QCPOINTERM(i); OPC->_int = ptr->_int; break; @@ -948,10 +948,10 @@ reeval: break; - case OP_BITSET: // b (+) a + case OP_BITSETSTORE_F: // b (+) a OPB->_float = (float)((int)OPB->_float | (int)OPA->_float); break; - case OP_BITSETP: // .b (+) a + case OP_BITSETSTOREP_F: // .b (+) a if (QCPOINTERWRITEFAIL(OPB)) { pr_xstatement = st-pr_statements; @@ -960,10 +960,10 @@ reeval: ptr = QCPOINTER(OPB); ptr->_float = (float)((int)ptr->_float | (int)OPA->_float); break; - case OP_BITCLR: // b (-) a + case OP_BITCLRSTORE_F: // b (-) a OPB->_float = (float)((int)OPB->_float & ~((int)OPA->_float)); break; - case OP_BITCLRP: // .b (-) a + case OP_BITCLRSTOREP_F: // .b (-) a if (QCPOINTERWRITEFAIL(OPB)) { pr_xstatement = st-pr_statements; @@ -1118,8 +1118,8 @@ reeval: case OP_MUL_VI: tmpi = OPB->_int; OPC->_vector[0] = OPA->_vector[0] * tmpi; - OPC->_vector[1] = OPA->_vector[0] * tmpi; - OPC->_vector[2] = OPA->_vector[0] * tmpi; + OPC->_vector[1] = OPA->_vector[1] * tmpi; + OPC->_vector[2] = OPA->_vector[2] * tmpi; break; case OP_MUL_IV: tmpi = OPA->_int; diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index 345f2e90f..aaf4dc561 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -431,6 +431,16 @@ int PDECL PR_InitEnts(pubprogfuncs_t *ppf, int max_ents) sv_num_edicts = 0; +#if 0 + { + int i; + for (i = 0; i < prinst.numfields; i++) + { + printf("%s(%i) %i -> %i\n", prinst.field[i].name, prinst.field[i].type, prinst.field[i].progsofs, prinst.field[i].ofs); + } + } +#endif + max_fields_size = fields_size; prinst.edicttable = PRHunkAlloc(progfuncs, maxedicts*sizeof(struct edicts_s *), "edicttable"); diff --git a/engine/qclib/pr_comp.h b/engine/qclib/pr_comp.h index ec39f7646..44ab00674 100644 --- a/engine/qclib/pr_comp.h +++ b/engine/qclib/pr_comp.h @@ -148,10 +148,10 @@ enum qcop_e { OP_THINKTIME, //87 - OP_BITSET, //88 redundant, for h2 compat - OP_BITSETP, //89 - OP_BITCLR, //90 - OP_BITCLRP, //91 + OP_BITSETSTORE_F, //88 redundant, for h2 compat + OP_BITSETSTOREP_F, //89 + OP_BITCLRSTORE_F, //90 + OP_BITCLRSTOREP_F, //91 OP_RAND0, //92 OP_RAND1, //93 @@ -227,7 +227,7 @@ enum qcop_e { OP_LSHIFT_I, OP_GLOBALADDRESS, - OP_POINTER_ADD, //32 bit pointers + OP_ADD_PIW, //add B words to A pointer OP_LOADA_F, OP_LOADA_V, @@ -328,8 +328,9 @@ enum qcop_e { These ops are emulated out, always, and are only present in the compiler. */ - OP_BITSET_I, //220 - OP_BITSETP_I, + OP_BITSETSTORE_I, //220 + OP_BITSETSTOREP_I, + OP_BITCLRSTORE_I, OP_MULSTORE_I, OP_DIVSTORE_I, @@ -338,9 +339,9 @@ enum qcop_e { OP_MULSTOREP_I, OP_DIVSTOREP_I, OP_ADDSTOREP_I, - OP_SUBSTOREP_I, + OP_SUBSTOREP_I, //230 - OP_MULSTORE_IF, //230 + OP_MULSTORE_IF, OP_MULSTOREP_IF, OP_DIVSTORE_IF, OP_DIVSTOREP_IF, @@ -350,8 +351,8 @@ enum qcop_e { OP_SUBSTOREP_IF, OP_MULSTORE_FI, - OP_MULSTOREP_FI, - OP_DIVSTORE_FI, //240 + OP_MULSTOREP_FI, //240 + OP_DIVSTORE_FI, OP_DIVSTOREP_FI, OP_ADDSTORE_FI, OP_ADDSTOREP_FI, @@ -374,6 +375,10 @@ enum qcop_e { OP_LT_P, OP_GT_P, + OP_ANDSTORE_F, + OP_BITCLR_F, + OP_BITCLR_I, + OP_NUMOPS }; diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index fc7775d70..4d89577ca 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -523,7 +523,7 @@ char *PR_ValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val, pbool v else QC_snprintfz (line, sizeof(line), "entity %i", val->edict); - if (verbose) + if (verbose && (unsigned)val->edict < (unsigned)sv_num_edicts) { struct edict_s *ed = EDICT_NUM(progfuncs, val->edict); int size = strlen(line); @@ -877,6 +877,7 @@ char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs) int i; ddef16_t *def16; ddef32_t *def32; + int nameofs = 0; static char line[128]; switch (current_progstate->structtype) @@ -884,23 +885,31 @@ char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs) case PST_DEFAULT: case PST_KKQWSV: def16 = ED_GlobalAtOfs16(progfuncs, ofs); - if (!def16) - sprintf (line,"%i(?""?""?)", ofs); - else - sprintf (line,"%i(%s)", ofs, def16->s_name+progfuncs->funcs.stringtable); + if (def16) + nameofs = def16->s_name; break; case PST_QTEST: case PST_FTE32: def32 = ED_GlobalAtOfs32(progfuncs, ofs); - if (!def32) - sprintf (line,"%i(?""?""?)", ofs); - else - sprintf (line,"%i(%s)", ofs, def32->s_name+progfuncs->funcs.stringtable); + if (def32) + nameofs = def32->s_name; break; default: Sys_Error("Bad struct type in PR_GlobalStringNoContents"); } + if (nameofs) + sprintf (line,"%i(%s)", ofs, nameofs+progfuncs->funcs.stringtable); + else + { + if (ofs >= OFS_RETURN && ofs < OFS_PARM0) + sprintf (line,"%i(return_%c)", ofs, 'x' + (ofs - OFS_RETURN)%3); + else if (ofs >= OFS_PARM0 && ofs < RESERVED_OFS) + sprintf (line,"%i(parm%i_%c)", ofs, (ofs - OFS_PARM0)/3, 'x' + (ofs - OFS_PARM0)%3); + else + sprintf (line,"%i(?""?""?)", ofs); + } + i = strlen(line); for ( ; i<20 ; i++) strcat (line," "); diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index 90191505d..aca6f7f5f 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -8,7 +8,7 @@ #define Host_Error Sys_Error // I put the following here to resolve "undefined reference to `__imp__vsnprintf'" with MinGW64 ~ Moodles -#ifdef _WIN32 +#if 0//def _WIN32 #if (_MSC_VER >= 1400) //with MSVC 8, use MS extensions #define snprintf linuxlike_snprintf_vc8 @@ -150,22 +150,9 @@ void PDECL PR_GenerateStatementString (pubprogfuncs_t *ppf, int statementnum, ch #if !defined(MINIMAL) && !defined(OMIT_QCC) if ( (unsigned)op < OP_NUMOPS) { - int i; - QC_snprintfz (out, outlen, "%s", pr_opcodes[op].name); + QC_snprintfz (out, outlen, "%-12s ", pr_opcodes[op].opname); outlen -= strlen(out); out += strlen(out); - QC_snprintfz (out, outlen, " "); - outlen -= 1; - out += 1; - - QC_snprintfz (out, outlen, "%s ", pr_opcodes[op].name); - i = strlen(pr_opcodes[op].name); - for ( ; i<10 ; i++) - { - QC_snprintfz (out, outlen, " "); - outlen -= 1; - out += 1; - } } else #endif @@ -175,15 +162,15 @@ void PDECL PR_GenerateStatementString (pubprogfuncs_t *ppf, int statementnum, ch out += strlen(out); } - if (op == OP_IF_F || op == OP_IFNOT_F) + if (op == OP_IF_F || op == OP_IFNOT_F || op == OP_IF_I || op == OP_IFNOT_I || op == OP_IF_S || op == OP_IFNOT_S) { - QC_snprintfz (out, outlen, "%sbranch %i",PR_GlobalStringNoContents(progfuncs, arg[0]),arg[1]); + QC_snprintfz (out, outlen, "%sbranch %i(%i)",PR_GlobalStringNoContents(progfuncs, arg[0]),(short)arg[1], statementnum+(short)arg[0]); outlen -= strlen(out); out += strlen(out); } else if (op == OP_GOTO) { - QC_snprintfz (out, outlen, "branch %i",arg[0]); + QC_snprintfz (out, outlen, "branch %i(%i)",(short)arg[0], statementnum+(short)arg[0]); outlen -= strlen(out); out += strlen(out); } @@ -727,7 +714,7 @@ pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *ppf, char *key) char *PDECL PR_EvaluateDebugString(pubprogfuncs_t *ppf, char *key) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; - static char buf[256]; + static char buf[8192]; fdef_t *fdef; eval_t *val; char *assignment; @@ -1129,7 +1116,7 @@ static char *lastfile = 0; lastline = externs->useeditor(&progfuncs->funcs, lastfile, lastline, statement, 0, NULL); if (!pr_progstate[pn].linenums) return statement; - if (lastline < 0) + if (lastline <= 0) return -lastline; if (pr_progstate[pn].linenums[statement] != lastline) @@ -1221,6 +1208,7 @@ void PR_ExecuteCode (progfuncs_t *progfuncs, int s) if (!--runaway) \ { \ pr_xstatement = st-pr_statements; \ + PR_RunError (&progfuncs->funcs, "runaway loop error\n");\ PR_StackTrace(&progfuncs->funcs); \ printf ("runaway loop error\n"); \ while(pr_depth > prinst.exitdepth) \ diff --git a/engine/qclib/pr_multi.c b/engine/qclib/pr_multi.c index dd36390de..fa21e9091 100644 --- a/engine/qclib/pr_multi.c +++ b/engine/qclib/pr_multi.c @@ -347,6 +347,18 @@ int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, char *name prinst.field[fnum].ofs = ofs = prinst.field[i].ofs; break; } + if (prinst.field[i].type == ev_vector && prinst.field[i].progsofs+1 == (unsigned)progsofs) + { +// printf("found union field %s %i -> %i\n", prinst.field[i].name, prinst.field[i].progsofs+1, prinst.field[i].ofs+1); + prinst.field[fnum].ofs = ofs = prinst.field[i].ofs+1; + break; + } + if (prinst.field[i].type == ev_vector && prinst.field[i].progsofs+2 == (unsigned)progsofs) + { +// printf("found union field %s %i -> %i\n", prinst.field[i].name, prinst.field[i].progsofs+2, prinst.field[i].ofs+2); + prinst.field[fnum].ofs = ofs = prinst.field[i].ofs+2; + break; + } } } } diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index 51f122eaa..bf0d4c35a 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -176,7 +176,7 @@ typedef struct int targetflags; //weather we need to mark the progs as a newer version char *name; char *opname; - int priority; + int priority; //FIXME: priority should be done differently... enum {ASSOC_LEFT, ASSOC_RIGHT, ASSOC_RIGHT_RESULT} associative; struct QCC_type_s **type_a, **type_b, **type_c; } QCC_opcode_t; diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 70996858c..83609a66d 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -1,6 +1,8 @@ #ifndef PROGSLIB_H #define PROGSLIB_H + +#include #ifdef _MSC_VER #define VARGS __cdecl #endif diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index f857990ab..8aee71658 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -331,7 +331,7 @@ typedef struct temp_s { struct QCC_def_s *lastfunc; #endif struct temp_s *next; - pbool used; + int used; unsigned int size; } temp_t; void QCC_PurgeTemps(void); @@ -366,6 +366,23 @@ typedef struct QCC_def_s temp_t *temp; } QCC_def_t; +typedef struct +{ + enum{ + REF_GLOBAL, //(global.ofs) - use vector[2] is an array ref or vector_z + REF_ARRAY, //(global.ofs+wordoffset) - constant offsets should be direct references, variable offsets will generally result in function calls + REF_POINTER,//*(pointerdef+wordindex) - maths... + REF_FIELD, //(entity.field) - reading is a single load, writing requires address+storep + REF_STRING, //"hello"[1]=='e' - special opcodes, or str2chr builtin, or something + } type; + + QCC_def_t *base; + QCC_def_t *index; + QCC_type_t *cast; //entity.float is float, not pointer. + int postinc; //+1 or -1 + pbool readonly; //for whatever reason, like base being a const +} QCC_ref_t; + //============================================================================ // pr_loc.h -- program local defs @@ -398,7 +415,7 @@ struct QCC_function_s char *file; // source file with definition int file_line; struct QCC_def_s *def; - unsigned int parm_ofs[MAX_PARMS]; // always contiguous, right? +// unsigned int parm_ofs[MAX_PARMS]; // always contiguous, right? }; @@ -503,8 +520,10 @@ extern pbool flag_hashonly; extern pbool flag_fasttrackarrays; extern pbool flag_assume_integer; extern pbool flag_msvcstyle; +extern pbool flag_debugmacros; extern pbool flag_filetimes; extern pbool flag_typeexplicit; +extern pbool flag_noboundchecks; extern pbool opt_overlaptemps; extern pbool opt_shortenifnots; @@ -635,7 +654,6 @@ enum { WARN_DEADCODE, WARN_UNREACHABLECODE, WARN_NOTSTANDARDBEHAVIOUR, - WARN_INEFFICIENTPLUSPLUS, WARN_DUPLICATEPRECOMPILER, WARN_IDENTICALPRECOMPILER, WARN_FTE_SPECIFIC, //extension that only FTEQCC will have a clue about. @@ -811,6 +829,7 @@ void QCC_PR_NewLine (pbool incomment); #define GDF_STATIC 2 #define GDF_CONST 4 QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool allocate, int arraysize, unsigned int flags); +char *QCC_PR_CheckCompConstTooltip(char *word, char *outstart, char *outend); void QCC_PR_PrintDefs (void); diff --git a/engine/qclib/qcc_cmdlib.c b/engine/qclib/qcc_cmdlib.c index afcd9e2b1..9953d12ad 100644 --- a/engine/qclib/qcc_cmdlib.c +++ b/engine/qclib/qcc_cmdlib.c @@ -775,6 +775,11 @@ struct { int SafeOpenWrite (char *filename, int maxsize) { int i; + if (strlen(filename) >= sizeof(qccfile[0].name)) + { + QCC_Error(ERR_TOOMANYOPENFILES, "Filename %s too long", filename); + return -1; + } for (i = 0; i < MAXQCCFILES; i++) { if (!qccfile[i].buff) @@ -848,14 +853,16 @@ int SafeSeek(int hand, int ofs, int mode) return 0; } } -void SafeClose(int hand) +pbool SafeClose(int hand) { - externs->WriteFile(qccfile[hand].name, qccfile[hand].buff, qccfile[hand].maxofs); + pbool ret; + ret = externs->WriteFile(qccfile[hand].name, qccfile[hand].buff, qccfile[hand].maxofs); // if (qccfile[hand].buffismalloc) free(qccfile[hand].buff); // else // externs->memfree(qccfile[hand].buff); qccfile[hand].buff = NULL; + return ret; } qcc_cachedsourcefile_t *qcc_sourcefile; diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 17d3baa2d..2b1788acd 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -79,9 +79,11 @@ pbool flag_laxcasts; //Allow lax casting. This'll produce loadsa warnings of co pbool flag_hashonly; //Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile pbool flag_fasttrackarrays; //Faster arrays, dynamically detected, activated only in supporting engines. pbool flag_msvcstyle; //MSVC style warnings, so msvc's ide works properly +pbool flag_debugmacros; //Print out #defines as they are expanded, for debugging. pbool flag_assume_integer; //5 - is that an integer or a float? qcc says float. but we support int too, so maybe we want that instead? pbool flag_filetimes; pbool flag_typeexplicit; //no implicit type conversions, you must do the casts yourself. +pbool flag_noboundchecks; //Disable generation of bound check instructions. pbool opt_overlaptemps; //reduce numpr_globals by reuse of temps. When they are not needed they are freed for reuse. The way this is implemented is better than frikqcc's. (This is the single most important optimisation) pbool opt_assignments; //STORE_F isn't used if an operation wrote to a temp. @@ -103,7 +105,7 @@ pbool opt_stripfunctions; //if a functions is only ever called directly or by e pbool opt_locals_overlapping; //make the local vars of all functions occupy the same globals. pbool opt_logicops; //don't make conditions enter functions if the return value will be discarded due to a previous value. (C style if statements) pbool opt_vectorcalls; //vectors can be packed into 3 floats, which can yield lower numpr_globals, but cost two more statements per call (only works for q1 calling conventions). -pbool opt_simplifiedifs; //if (f != 0) -> if (f). if (f == 0) -> ifnot (f) +pbool opt_simplifiedifs; //if (f != 0) -> if_f (f). if (f == 0) -> ifnot_f (f) //bool opt_comexprremoval; //these are the results of the opt_. The values are printed out when compilation is compleate, showing effectivness. @@ -139,14 +141,31 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int a QCC_type_t *QCC_PR_FindType (QCC_type_t *type); QCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto); QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto); -QCC_def_t *QCC_PR_Term (int exprflags); +QCC_def_t *QCC_PR_Term (unsigned int exprflags); QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, pbool expandmemberfields, pbool makearraypointers); -QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign, pbool makestructpointers); QCC_def_t *QCC_PR_GenerateFunctionCall (QCC_def_t *newself, QCC_def_t *func, QCC_def_t *arglist[], QCC_type_t *argtypelist[], int argcount); void QCC_Marshal_Locals(int firststatement, int laststatement); +QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign, pbool makestructpointers); + +QCC_ref_t *QCC_DefToRef(QCC_ref_t *ref, QCC_def_t *def); //ref is a buffer to write into, to avoid excessive allocs +QCC_def_t *QCC_RefToDef(QCC_ref_t *ref, pbool freetemps); +QCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags); +QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbool allowarrayassign, pbool expandmemberfields, pbool makearraypointers); +QCC_ref_t *QCC_PR_ParseRefArrayPointer (QCC_ref_t *refbuf, QCC_ref_t *d, pbool allowarrayassign, pbool makestructpointers); +QCC_ref_t *QCC_PR_BuildRef(QCC_ref_t *retbuf, unsigned int reftype, QCC_def_t *base, QCC_def_t *index, QCC_type_t *cast, pbool readonly); +QCC_def_t *QCC_StoreToRef(QCC_ref_t *dest, QCC_def_t *source, pbool readable, pbool preservedest); + +enum +{ + STFL_PRESERVEA=1<<0, //if a temp is released as part of the statement, it can be reused for the result. Which is bad if the temp is needed for something else, like e.e.f += 4; + STFL_CONVERTA=1<<1, //convert to/from ints/floats to match the operand types required by the opcode + STFL_PRESERVEB=1<<2, + STFL_CONVERTB=1<<3 +}; +#define QCC_PR_Statement(op,a,b,st) QCC_PR_StatementFlags(op,a,b,st,STFL_CONVERTA|STFL_CONVERTB) +QCC_def_t *QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_dstatement_t **outstatement, unsigned int flags); void QCC_PR_ParseState (void); -pbool simplestore; pbool expandedemptymacro; QCC_pr_info_t pr; @@ -164,6 +183,7 @@ QCC_string_t s_file, s_file2; // filename for function definition unsigned int locals_start; // for tracking local variables vs temps unsigned int locals_end; // for tracking local variables vs temps +unsigned int locals_marshalled; // largest local block size that needs to be allocated for locals overlapping. jmp_buf pr_parse_abort; // longjump with this on parse error @@ -237,19 +257,19 @@ QCC_opcode_t pr_opcodes[] = {6, "!=", "NE_E", 5, ASSOC_LEFT, &type_entity, &type_entity, &type_float}, {6, "!=", "NE_FNC", 5, ASSOC_LEFT, &type_function, &type_function, &type_float}, - {6, "<=", "LE", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, ">=", "GE", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, "<", "LT", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, ">", "GT", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "<=", "LE_F", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, ">=", "GE_F", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "<", "LT_F", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, ">", "GT_F", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, ".", "INDIRECT_F", 1, ASSOC_LEFT, &type_entity, &type_field, &type_float}, - {6, ".", "INDIRECT_V", 1, ASSOC_LEFT, &type_entity, &type_field, &type_vector}, - {6, ".", "INDIRECT_S", 1, ASSOC_LEFT, &type_entity, &type_field, &type_string}, - {6, ".", "INDIRECT_E", 1, ASSOC_LEFT, &type_entity, &type_field, &type_entity}, - {6, ".", "INDIRECT_FI", 1, ASSOC_LEFT, &type_entity, &type_field, &type_field}, - {6, ".", "INDIRECT_FU", 1, ASSOC_LEFT, &type_entity, &type_field, &type_function}, + {6, ".", "LOADF_F", 1, ASSOC_LEFT, &type_entity, &type_field, &type_float}, + {6, ".", "LOADF_V", 1, ASSOC_LEFT, &type_entity, &type_field, &type_vector}, + {6, ".", "LOADF_S", 1, ASSOC_LEFT, &type_entity, &type_field, &type_string}, + {6, ".", "LOADF_E", 1, ASSOC_LEFT, &type_entity, &type_field, &type_entity}, + {6, ".", "LOADF_FI", 1, ASSOC_LEFT, &type_entity, &type_field, &type_field}, + {6, ".", "LOADF_FU", 1, ASSOC_LEFT, &type_entity, &type_field, &type_function}, - {6, ".", "ADDRESS", 1, ASSOC_LEFT, &type_entity, &type_field, &type_pointer}, + {6, ".", "FLDADDRESS", 1, ASSOC_LEFT, &type_entity, &type_field, &type_pointer}, {6, "=", "STORE_F", 6, ASSOC_RIGHT, &type_float, &type_float, &type_float}, {6, "=", "STORE_V", 6, ASSOC_RIGHT, &type_vector, &type_vector, &type_vector}, @@ -407,8 +427,8 @@ QCC_opcode_t pr_opcodes[] = {7, "<<", "LSHIFT_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, //var, offset return - {7, "", "GET_POINTER", -1, ASSOC_LEFT, &type_float, &type_integer, &type_pointer}, - {7, "", "MUL4ADD_I", -1, ASSOC_LEFT, &type_pointer, &type_integer, &type_pointer}, + {7, "", "GLOBALADDRESS", -1, ASSOC_LEFT, &type_float, &type_integer, &type_pointer}, + {7, "", "ADD_PIW", -1, ASSOC_LEFT, &type_pointer, &type_integer, &type_pointer}, {7, "=", "LOADA_F", 6, ASSOC_LEFT, &type_float, &type_integer, &type_float}, {7, "=", "LOADA_V", 6, ASSOC_LEFT, &type_vector, &type_integer, &type_vector}, @@ -419,7 +439,7 @@ QCC_opcode_t pr_opcodes[] = {7, "=", "LOADA_I", 6, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, {7, "=", "STORE_P", 6, ASSOC_RIGHT, &type_pointer, &type_pointer, &type_void}, - {7, ".", "INDIRECT_P", 1, ASSOC_LEFT, &type_entity, &type_field, &type_pointer}, + {7, ".", "LOADF_P", 1, ASSOC_LEFT, &type_entity, &type_field, &type_pointer}, {7, "=", "LOADP_F", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_float}, {7, "=", "LOADP_V", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_vector}, @@ -468,8 +488,8 @@ QCC_opcode_t pr_opcodes[] = {7, "&", "BITAND_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, {7, "|", "BITOR_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, -{7, "&", "BITAND_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, -{7, "|", "BITOR_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, +{7, "&", "BITAND_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, +{7, "|", "BITOR_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, {7, "&&", "AND_I", 7, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, {7, "||", "OR_I", 7, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, @@ -518,8 +538,9 @@ QCC_opcode_t pr_opcodes[] = {7, "<>", "OP_EMULATED", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {7, "|=", "BITSET_I", 6, ASSOC_RIGHT, &type_integer, &type_integer, &type_integer}, - {7, "|=", "BITSETP_I", 6, ASSOC_RIGHT, &type_pointer, &type_integer, &type_integer}, + {7, "|=", "BITSET_I", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_integer, &type_integer}, + {7, "|=", "BITSETP_I", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_integer}, + {7, "&~=", "BITCLR_I", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_integer, &type_integer}, {7, "*=", "MULSTORE_I", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_integer, &type_integer}, @@ -566,6 +587,9 @@ QCC_opcode_t pr_opcodes[] = {7, "<", "LT_P", 5, ASSOC_LEFT, &type_pointer, &type_pointer, &type_float}, {7, ">", "GT_P", 5, ASSOC_LEFT, &type_pointer, &type_pointer, &type_float}, + {7, "&=", "ANDSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, + {7, "&~", "BITCLR_F", 6, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {7, "&~", "BITCLR_I", 6, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, {0, NULL} }; @@ -582,7 +606,7 @@ pbool OpAssignsToC(unsigned int op) return false; // they use a and b, but have 3 types // safety - if(op >= OP_BITSET && op <= OP_BITCLRP) + if(op >= OP_BITSETSTORE_F && op <= OP_BITCLRSTOREP_F) return false; /*if(op >= OP_STORE_I && op <= OP_STORE_FI) return false; <- add STOREP_*?*/ @@ -594,7 +618,7 @@ pbool OpAssignsToC(unsigned int op) } pbool OpAssignsToB(unsigned int op) { - if(op >= OP_BITSET && op <= OP_BITCLRP) + if(op >= OP_BITSETSTORE_F && op <= OP_BITCLRSTOREP_F) return true; if(op >= OP_STORE_I && op <= OP_STORE_FI) return true; @@ -626,6 +650,114 @@ pbool OpAssignsToB(unsigned int op) //conditional and/or #define CONDITION_PRIORITY 7 +QCC_opcode_t *opcodes_store[] = +{ + NULL +}; +QCC_opcode_t *opcodes_addstore[] = +{ +/* &pr_opcodes[OP_ADDSTORE_F], + &pr_opcodes[OP_ADDSTORE_V], + &pr_opcodes[OP_ADDSTORE_I], + &pr_opcodes[OP_ADDSTORE_IF], + &pr_opcodes[OP_ADDSTORE_FI], + &pr_opcodes[OP_ADDSTOREP_F], + &pr_opcodes[OP_ADDSTOREP_V], + &pr_opcodes[OP_ADDSTOREP_I], + &pr_opcodes[OP_ADDSTOREP_IF], + &pr_opcodes[OP_ADDSTOREP_FI],*/ + &pr_opcodes[OP_ADD_F], + &pr_opcodes[OP_ADD_V], + &pr_opcodes[OP_ADD_I], + &pr_opcodes[OP_ADD_FI], + &pr_opcodes[OP_ADD_IF], + &pr_opcodes[OP_ADD_SF], + NULL +}; +QCC_opcode_t *opcodes_substore[] = +{ +/* &pr_opcodes[OP_SUBSTORE_F], + &pr_opcodes[OP_SUBSTORE_V], + &pr_opcodes[OP_SUBSTORE_I], + &pr_opcodes[OP_SUBSTORE_IF], + &pr_opcodes[OP_SUBSTORE_FI], + &pr_opcodes[OP_SUBSTOREP_F], + &pr_opcodes[OP_SUBSTOREP_V], + &pr_opcodes[OP_SUBSTOREP_I], + &pr_opcodes[OP_SUBSTOREP_IF], + &pr_opcodes[OP_SUBSTOREP_FI],*/ + &pr_opcodes[OP_SUB_F], + &pr_opcodes[OP_SUB_V], + &pr_opcodes[OP_SUB_I], + &pr_opcodes[OP_SUB_FI], + &pr_opcodes[OP_SUB_IF], + &pr_opcodes[OP_SUB_S], + NULL +}; +QCC_opcode_t *opcodes_mulstore[] = +{ +/* &pr_opcodes[OP_MULSTORE_F], + &pr_opcodes[OP_MULSTORE_VF], + &pr_opcodes[OP_MULSTORE_VI], + &pr_opcodes[OP_MULSTORE_I], + &pr_opcodes[OP_MULSTORE_IF], + &pr_opcodes[OP_MULSTORE_FI], + &pr_opcodes[OP_MULSTOREP_F], + &pr_opcodes[OP_MULSTOREP_VF], + &pr_opcodes[OP_MULSTOREP_VI], + &pr_opcodes[OP_MULSTOREP_I], + &pr_opcodes[OP_MULSTOREP_IF], + &pr_opcodes[OP_MULSTOREP_FI],*/ + &pr_opcodes[OP_MUL_F], + &pr_opcodes[OP_MUL_V], + &pr_opcodes[OP_MUL_FV], + &pr_opcodes[OP_MUL_IV], + &pr_opcodes[OP_MUL_VF], + &pr_opcodes[OP_MUL_VI], + &pr_opcodes[OP_MUL_I], + &pr_opcodes[OP_MUL_FI], + &pr_opcodes[OP_MUL_IF], + NULL +}; +QCC_opcode_t *opcodes_divstore[] = +{ + &pr_opcodes[OP_DIV_F], + &pr_opcodes[OP_DIV_I], + &pr_opcodes[OP_DIV_FI], + &pr_opcodes[OP_DIV_IF], + &pr_opcodes[OP_DIV_VF], + NULL +}; +QCC_opcode_t *opcodes_orstore[] = +{ + &pr_opcodes[OP_BITOR_F], + &pr_opcodes[OP_BITOR_I], + &pr_opcodes[OP_BITOR_IF], + &pr_opcodes[OP_BITOR_FI], + NULL +}; +QCC_opcode_t *opcodes_xorstore[] = +{ + &pr_opcodes[OP_XOR_I], + NULL +}; +QCC_opcode_t *opcodes_andstore[] = +{ + &pr_opcodes[OP_BITAND_F], + &pr_opcodes[OP_BITAND_I], + &pr_opcodes[OP_BITAND_IF], + &pr_opcodes[OP_BITAND_FI], + NULL +}; +QCC_opcode_t *opcodes_clearstore[] = +{ +// &pr_opcodes[OP_BITCLRSTORE_F], +// &pr_opcodes[OP_BITCLRSTORE_I], +// &pr_opcodes[OP_BITCLRSTOREP_F], + &pr_opcodes[OP_BITCLR_F], + &pr_opcodes[OP_BITCLR_I], + NULL +}; //this system cuts out 10/120 //these evaluate as top first. @@ -662,15 +794,15 @@ QCC_opcode_t *opcodeprioritized[TOP_PRIORITY+1][128] = */ NULL }, { //1 - &pr_opcodes[OP_LOAD_F], - &pr_opcodes[OP_LOAD_V], - &pr_opcodes[OP_LOAD_S], - &pr_opcodes[OP_LOAD_ENT], - &pr_opcodes[OP_LOAD_FLD], - &pr_opcodes[OP_LOAD_FNC], - &pr_opcodes[OP_LOAD_I], - &pr_opcodes[OP_LOAD_P], - &pr_opcodes[OP_ADDRESS], +// &pr_opcodes[OP_LOAD_F], +// &pr_opcodes[OP_LOAD_V], +// &pr_opcodes[OP_LOAD_S], +// &pr_opcodes[OP_LOAD_ENT], +// &pr_opcodes[OP_LOAD_FLD], +// &pr_opcodes[OP_LOAD_FNC], +// &pr_opcodes[OP_LOAD_I], +// &pr_opcodes[OP_LOAD_P], +// &pr_opcodes[OP_ADDRESS], NULL }, { //2 /* //conversion. don't use @@ -836,16 +968,18 @@ QCC_opcode_t *opcodeprioritized[TOP_PRIORITY+1][128] = &pr_opcodes[OP_SUBSTOREP_IF], &pr_opcodes[OP_SUBSTOREP_FI], - &pr_opcodes[OP_BITSET], - &pr_opcodes[OP_BITSET_I], -// &pr_opcodes[OP_BITSET_IF], -// &pr_opcodes[OP_BITSET_FI], - &pr_opcodes[OP_BITSETP], - &pr_opcodes[OP_BITSETP_I], -// &pr_opcodes[OP_BITSETP_IF], -// &pr_opcodes[OP_BITSETP_FI], - &pr_opcodes[OP_BITCLR], - &pr_opcodes[OP_BITCLRP], + &pr_opcodes[OP_ANDSTORE_F], + + &pr_opcodes[OP_BITSETSTORE_F], + &pr_opcodes[OP_BITSETSTORE_I], +// &pr_opcodes[OP_BITSETSTORE_IF], +// &pr_opcodes[OP_BITSETSTORE_FI], + &pr_opcodes[OP_BITSETSTOREP_F], + &pr_opcodes[OP_BITSETSTOREP_I], +// &pr_opcodes[OP_BITSETSTOREP_IF], +// &pr_opcodes[OP_BITSETSTOREP_FI], + &pr_opcodes[OP_BITCLRSTORE_F], + &pr_opcodes[OP_BITCLRSTOREP_F], NULL }, { //7 @@ -1008,10 +1142,10 @@ pbool QCC_OPCodeValid(QCC_opcode_t *op) case OP_RANDV2: return true; - case OP_BITSET: // b |= a - case OP_BITCLR: // b &= ~a - case OP_BITSETP: // *b |= a - case OP_BITCLRP: // *b &= ~a + case OP_BITSETSTORE_F: // b |= a + case OP_BITCLRSTORE_F: // b &= ~a + case OP_BITSETSTOREP_F: // *b |= a + case OP_BITCLRSTOREP_F: // *b &= ~a return false; //FIXME: I do not fully follow the controversy over these. case OP_SWITCH_F: @@ -1075,7 +1209,7 @@ pbool QCC_OPCodeValid(QCC_opcode_t *op) return false; //DPFIXME: These are not bounds checked at all. case OP_GLOBALADDRESS: return true; //DPFIXME: DP will reject these pointers if they are ever used. - case OP_POINTER_ADD: + case OP_ADD_PIW: return true; //just maths. case OP_ADD_SF: //(char*)c = (char*)a + (float)b @@ -1136,7 +1270,6 @@ PR_Statement Emits a primitive statement, returning the var it places it's value in ============ */ -QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_dstatement_t **outstatement); static int QCC_ShouldConvert(QCC_def_t *var, etype_t wanted) { /*no conversion needed*/ @@ -1180,7 +1313,7 @@ QCC_def_t *QCC_SupplyConversion(QCC_def_t *var, etype_t wanted, pbool fatal) QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, var, "Implicit type mismatch. Needed %s, got %s.", basictypenames[wanted], basictypenames[var->type->type]); if (o < 0) { - if (fatal) + if (fatal && wanted != ev_variant && var->type->type != ev_variant) QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, var, "Implicit type mismatch. Needed %s, got %s.", basictypenames[wanted], basictypenames[var->type->type]); else return var; @@ -1387,13 +1520,13 @@ static void QCC_RemapLockedTemp(temp_t *t, int firststatement, int laststatement newofs = QCC_GetFreeLocalOffsetSpace(t->size); numtemps+=t->size; - def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, 0); - def->nextlocal = pr.localvars; + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size==1?0:t->size, newofs, false, 0); #ifdef WRITEASM sprintf(buffer, "locked_%i", t->ofs); def->name = qccHunkAlloc(strlen(buffer)+1); strcpy(def->name, buffer); #endif + def->nextlocal = pr.localvars; pr.localvars = def; } st->a = st->a - t->ofs + newofs; @@ -1405,13 +1538,13 @@ static void QCC_RemapLockedTemp(temp_t *t, int firststatement, int laststatement newofs = QCC_GetFreeLocalOffsetSpace(t->size); numtemps+=t->size; - def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, 0); - def->nextlocal = pr.localvars; + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size==1?0:t->size, newofs, false, 0); #ifdef WRITEASM sprintf(buffer, "locked_%i", t->ofs); def->name = qccHunkAlloc(strlen(buffer)+1); strcpy(def->name, buffer); #endif + def->nextlocal = pr.localvars; pr.localvars = def; } st->b = st->b - t->ofs + newofs; @@ -1423,13 +1556,13 @@ static void QCC_RemapLockedTemp(temp_t *t, int firststatement, int laststatement newofs = QCC_GetFreeLocalOffsetSpace(t->size); numtemps+=t->size; - def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, 0); - def->nextlocal = pr.localvars; + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size==1?0:t->size, newofs, false, 0); #ifdef WRITEASM sprintf(buffer, "locked_%i", t->ofs); def->name = qccHunkAlloc(strlen(buffer)+1); strcpy(def->name, buffer); #endif + def->nextlocal = pr.localvars; pr.localvars = def; } st->c = st->c - t->ofs + newofs; @@ -1475,7 +1608,7 @@ static void QCC_fprintfLocals(FILE *f, gofs_t paramstart, gofs_t paramend) { if (t->lastfunc == pr_scope) { - fprintf(f, "local %s temp_%i;\n", (t->size == 1)?"float":"vector", i); + fprintf(f, "local %s temp_%i;\n", (t->size == 1)?"float":"vector", t->ofs); } } } @@ -1513,13 +1646,53 @@ static const char *QCC_VarAtOffset(unsigned int ofs, unsigned int size) if (!STRCMP(var->name, "IMMEDIATE")) //continue, don't get bogged down by multiple bits of code continue; if (size < var->type->size) - sprintf(message, "%s_%c", var->name, 'x' + (ofs-var->ofs)%3); + { + if (var->type->type == ev_vector) + sprintf(message, "%s_%c", var->name, 'x' + (ofs-var->ofs)%3); + else + sprintf(message, "%s+%i", var->name, ofs-var->ofs); + } else sprintf(message, "%s", var->name); return message; } } } + for (var = pr.def_head.next; var; var = var->next) + { + if (var->scope && var->scope != pr_scope) + continue; + + if (ofs == var->ofs && size == var->type->size) + { + if (*var->name) + { + if (!STRCMP(var->name, "IMMEDIATE")) + { + switch(var->type->type) + { + case ev_string: + sprintf(message, "\"%.1020s\"", &strings[((int *)qcc_pr_globals)[var->ofs]]); + return message; + case ev_integer: + sprintf(message, "%ii", ((int *)qcc_pr_globals)[var->ofs]); + return message; + case ev_float: + sprintf(message, "%gf", qcc_pr_globals[var->ofs]); + return message; + case ev_vector: + sprintf(message, "'%g %g %g'", qcc_pr_globals[var->ofs], qcc_pr_globals[var->ofs+1], qcc_pr_globals[var->ofs+2]); + return message; + default: + sprintf(message, "IMMEDIATE"); + return message; + } + } + sprintf(message, "%s", var->name); + return message; + } + } + } for (var = pr.def_head.next; var; var = var->next) { @@ -1541,7 +1714,7 @@ static const char *QCC_VarAtOffset(unsigned int ofs, unsigned int size) sprintf(message, "%ii", ((int *)qcc_pr_globals)[var->ofs]); return message; case ev_float: - sprintf(message, "%g", qcc_pr_globals[var->ofs]); + sprintf(message, "%gf", qcc_pr_globals[var->ofs]); return message; case ev_vector: sprintf(message, "'%g %g %g'", qcc_pr_globals[var->ofs], qcc_pr_globals[var->ofs+1], qcc_pr_globals[var->ofs+2]); @@ -1552,7 +1725,12 @@ static const char *QCC_VarAtOffset(unsigned int ofs, unsigned int size) } } if (size < var->type->size) - sprintf(message, "%s_%c", var->name, 'x' + (ofs-var->ofs)%3); + { + if (var->type->type == ev_vector) + sprintf(message, "%s_%c", var->name, 'x' + (ofs-var->ofs)%3); + else + sprintf(message, "%s+%i", var->name, ofs-var->ofs); + } else sprintf(message, "%s", var->name); return message; @@ -1607,26 +1785,30 @@ int QCC_PR_FindSourceForTemp(QCC_def_t *tempdef, int op, pbool *need_lock) } QCC_dstatement_t *QCC_PR_SimpleStatement( int op, int var_a, int var_b, int var_c, int force); -QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_dstatement_t **outstatement) +QCC_def_t *QCC_PR_StatementFlags (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_dstatement_t **outstatement, unsigned int flags) { char typea[256], typeb[256]; QCC_dstatement_t *statement; QCC_def_t *var_c=NULL, *temp=NULL; if (outstatement == (QCC_dstatement_t **)0xffffffff) + { outstatement = NULL; - else if (op->priority != -1 && op->priority != CONDITION_PRIORITY) + flags &= ~(STFL_CONVERTA|STFL_CONVERTB); + } + + if (op->priority != -1 && op->priority != CONDITION_PRIORITY) { if (op->associative!=ASSOC_LEFT) { - if (op->type_a != &type_pointer) + if (op->type_a != &type_pointer && (flags&STFL_CONVERTB)) var_b = QCC_SupplyConversion(var_b, (*op->type_a)->type, false); } else { - if (var_a) + if (var_a && (flags&STFL_CONVERTA)) var_a = QCC_SupplyConversion(var_a, (*op->type_a)->type, false); - if (var_b) + if (var_b && (flags&STFL_CONVERTB)) var_b = QCC_SupplyConversion(var_b, (*op->type_b)->type, false); } } @@ -1634,12 +1816,14 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ if (var_a) { var_a->references++; - QCC_FreeTemp(var_a); + if (!(flags&STFL_PRESERVEA)) + QCC_FreeTemp(var_a); } if (var_b) { var_b->references++; - QCC_FreeTemp(var_b); + if (!(flags&STFL_PRESERVEB)) + QCC_FreeTemp(var_b); } //maths operators @@ -1953,6 +2137,13 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ case OP_STORE_I: case OP_STORE_ENT: case OP_STORE_FNC: + if (var_a->constant && var_a->type->type == ev_integer && !G_INT(var_a->ofs)) + { + //you're allowed to assign 0i to anything + if (op - pr_opcodes == OP_STORE_V) //make sure vectors get set properly. + var_a = QCC_MakeVectorConst(0, 0, 0); + } + /*else { QCC_type_t *t = var_a->type; while(t) @@ -1967,7 +2158,7 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ TypeName(var_b->type, typeb, sizeof(typeb)); QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "Implicit assignment from %s to %s %s", typea, typeb, var_b->name); } - } + }*/ break; case OP_STOREP_F: @@ -1977,7 +2168,13 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ case OP_STOREP_I: case OP_STOREP_ENT: case OP_STOREP_FNC: - if (typecmp_lax(var_a->type, var_b->type->aux_type)) + if (var_a->constant && var_a->type->type == ev_integer && !G_INT(var_a->ofs)) + { + //you're allowed to assign 0i to anything + if (op - pr_opcodes == OP_STOREP_V) //make sure vectors get set properly. + var_a = QCC_MakeVectorConst(0, 0, 0); + } + /*else { QCC_type_t *t = var_a->type; while(t) @@ -1992,7 +2189,7 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ TypeName(var_b->type->aux_type, typeb, sizeof(typeb)); QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "Implicit field assignment from %s to %s", typea, typeb); } - } + }*/ break; case OP_LOADA_F: @@ -2036,6 +2233,24 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ case OP_GT_F: if (typecmp_lax(var_a->type, var_b->type)) { + QCC_type_t *t; + //simplify a, see if we can get an inherited comparison + for (t = var_a->type; t; t = t->parentclass) + { + if (typecmp_lax(t, var_b->type)) + break; + } + if (t) + break; + //now try with b simplified + for (t = var_b->type; t; t = t->parentclass) + { + if (typecmp_lax(var_a->type, t)) + break; + } + if (t) + break; + //if both need to simplify then the classes are too diverse TypeName(var_a->type, typea, sizeof(typea)); TypeName(var_b->type, typeb, sizeof(typeb)); QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "'%s' type mismatch: %s with %s", op->name, typea, typeb); @@ -2072,7 +2287,8 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ else op = &pr_opcodes[OP_IF_I]; numstatements--; - QCC_FreeTemp(var_a); + if (!(flags & STFL_PRESERVEA)) + QCC_FreeTemp(var_a); memcpy(&nvara, var_a, sizeof(nvara)); nvara.ofs = statements[numstatements].a; var_a = &nvara; @@ -2090,7 +2306,8 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ static QCC_def_t nvara; op = &pr_opcodes[OP_IF_F]; numstatements--; - QCC_FreeTemp(var_a); + if (!(flags & STFL_PRESERVEA)) + QCC_FreeTemp(var_a); memcpy(&nvara, var_a, sizeof(nvara)); nvara.ofs = statements[numstatements].a; var_a = &nvara; @@ -2108,7 +2325,8 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ static QCC_def_t nvara; op = &pr_opcodes[OP_IF_S]; numstatements--; - QCC_FreeTemp(var_a); + if (!(flags & STFL_PRESERVEA)) + QCC_FreeTemp(var_a); memcpy(&nvara, var_a, sizeof(nvara)); nvara.ofs = statements[numstatements].a; var_a = &nvara; @@ -2136,11 +2354,10 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ QCC_PR_ParseWarning(0, "store type mismatch"); var_b->references++; var_a->references--; - QCC_FreeTemp(var_a); + if (!(flags & STFL_PRESERVEA)) + QCC_FreeTemp(var_a); optres_assignments++; - simplestore=true; - QCC_UnFreeTemp(var_b); return var_b; } @@ -2148,13 +2365,13 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ } } } - simplestore=false; statement = &statements[numstatements]; numstatements++; if (!QCC_OPCodeValid(op)) { +//FIXME: add support for flags so we don't corrupt temps switch(op - pr_opcodes) { case OP_LOADA_STRUCT: @@ -2203,6 +2420,19 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ return var_c; } break; + case OP_DIV_VF: + //v/f === v*(1/f) + op = &pr_opcodes[OP_MUL_VF]; +// var_a = var_a; + QCC_UnFreeTemp(var_a); + numstatements--; + var_b = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], QCC_MakeFloatConst(1), var_b, NULL); + statement = &statements[numstatements]; + numstatements++; + QCC_FreeTemp(var_a); + QCC_FreeTemp(var_b); +// var_c = var_c; + break; case OP_CONV_ITOF: case OP_STORE_IF: @@ -2435,14 +2665,14 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ var_c = var_a; break; - case OP_BITSET_I: + case OP_BITSETSTORE_I: op = &pr_opcodes[OP_BITOR_I]; var_c = var_b; var_b = var_a; var_a = var_c; var_c = var_a; break; - case OP_BITSET: + case OP_BITSETSTORE_F: op = &pr_opcodes[OP_BITOR_F]; var_c = var_b; var_b = var_a; @@ -2472,14 +2702,44 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ op = &pr_opcodes[OP_LT_I]; break; - case OP_BITCLR: + case OP_BITCLR_I: + var_c = var_b; + var_b = var_a; + var_a = var_c; + //fallthrough + case OP_BITCLRSTORE_I: //b = var, a = bit field. QCC_UnFreeTemp(var_a); QCC_UnFreeTemp(var_b); numstatements--; - var_c = QCC_PR_Statement(&pr_opcodes[OP_BITAND_F], var_b, var_a, NULL); + var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], var_b, var_a, NULL, STFL_PRESERVEA); + QCC_FreeTemp(var_c); + statement = &statements[numstatements]; + numstatements++; + + QCC_FreeTemp(var_a); + QCC_FreeTemp(var_b); + + op = &pr_opcodes[OP_SUB_I]; + var_a = var_b; + var_b = var_c; + var_c = ((op - pr_opcodes)==OP_BITCLRSTORE_I)?var_a:NULL; + break; + case OP_BITCLR_F: + var_c = var_b; + var_b = var_a; + var_a = var_c; + //fallthrough + case OP_BITCLRSTORE_F: + //b = var, a = bit field. + + QCC_UnFreeTemp(var_a); + QCC_UnFreeTemp(var_b); + + numstatements--; + var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_F], var_b, var_a, NULL, STFL_PRESERVEA); QCC_FreeTemp(var_c); statement = &statements[numstatements]; numstatements++; @@ -2490,7 +2750,7 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ op = &pr_opcodes[OP_SUB_F]; var_a = var_b; var_b = var_c; - var_c = var_a; + var_c = ((op - pr_opcodes)==OP_BITCLRSTORE_F)?var_a:NULL; break; case OP_SUBSTOREP_FI: @@ -2513,16 +2773,16 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ case OP_ADDSTOREP_F: case OP_MULSTOREP_F: case OP_DIVSTOREP_F: - case OP_BITSETP: - case OP_BITSETP_I: - case OP_BITCLRP: + case OP_BITSETSTOREP_F: + case OP_BITSETSTOREP_I: + case OP_BITCLRSTOREP_F: // QCC_PR_ParseWarning(0, "XSTOREP_F emulation is still experimental"); QCC_UnFreeTemp(var_a); QCC_UnFreeTemp(var_b); //don't chain these... this expansion is not the same. { int st; - int need_lock; + pbool need_lock; st = QCC_PR_FindSourceForTemp(var_b, OP_ADDRESS, &need_lock); var_c = QCC_GetTemp(*op->type_c); @@ -2541,7 +2801,7 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ { QCC_ForceLockTempForOffset(statements[st].a); QCC_ForceLockTempForOffset(statements[st].b); - QCC_LockTemp(var_c); /*that temp needs to be preserved over calls*/ +// QCC_LockTemp(var_c); /*that temp needs to be preserved over calls*/ } /*generate new OP_ADDRESS instruction - FIXME: the arguments may have changed since the original instruction*/ @@ -2549,14 +2809,14 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ statement->op = OP_ADDRESS; statement->a = statements[st].a; statement->b = statements[st].b; - statement->c = statements[st].c; + statement->c = var_c->ofs; /*convert old one to an OP_LOAD*/ statement_linenums[st] = pr_token_line_last; statements[st].op = ((*op->type_c)->type==ev_vector)?OP_LOAD_V:OP_LOAD_F; - statements[st].a = statements[st].a; - statements[st].b = statements[st].b; - statements[st].c = var_c->ofs; +// statements[st].a = statements[st].a; +// statements[st].b = statements[st].b; +// statements[st].c = statements[st].c; } } @@ -2620,13 +2880,13 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ case OP_DIVSTOREP_F: statement->op = OP_DIV_F; break; - case OP_BITSETP: + case OP_BITSETSTOREP_F: statement->op = OP_BITOR_F; break; - case OP_BITSETP_I: + case OP_BITSETSTOREP_I: statement->op = OP_BITOR_I; break; - case OP_BITCLRP: + case OP_BITCLRSTOREP_F: //float pointer float temp = QCC_GetTemp(type_float); statement->op = OP_BITAND_F; @@ -2646,24 +2906,24 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ default: //no way will this be hit... QCC_PR_ParseError(ERR_INTERNAL, "opcode invalid 3 times %i", op - pr_opcodes); } - if (op - pr_opcodes == OP_BITCLRP) + if (op - pr_opcodes == OP_BITCLRSTOREP_F) { - statement->a = var_c ? var_c->ofs : 0; + statement->a = var_b ? var_b->ofs : 0; statement->b = temp ? temp->ofs : 0; - statement->c = var_c->ofs; + statement->c = var_b->ofs; QCC_FreeTemp(temp); - var_b = var_b; //this is the ptr. QCC_FreeTemp(var_a); - var_a = var_c; //this is the value. + var_a = var_b; //this is the value. + var_b = var_c; //this is the ptr. } else { - statement->a = var_c ? var_c->ofs : 0; + statement->a = var_b ? var_b->ofs : 0; statement->b = var_a ? var_a->ofs : 0; - statement->c = var_c->ofs; - var_b = var_b; //this is the ptr. + statement->c = var_b->ofs; QCC_FreeTemp(var_a); - var_a = var_c; //this is the value. + var_a = var_b; //this is the value. + var_b = var_c; //this is the ptr. } op = &pr_opcodes[((*op->type_c)->type==ev_vector)?OP_STOREP_V:OP_STOREP_F]; @@ -2698,12 +2958,28 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ statement->c = 0; // ifs, gotos, and assignments // don't need vars allocated } + else if (op-pr_opcodes == OP_ADD_PIW) + { + var_c = QCC_GetTemp(var_a->type); + statement->c = var_c->ofs; + } else { // allocate result space var_c = QCC_GetTemp(*op->type_c); statement->c = var_c->ofs; if (op->type_b == &type_field) { + //&(a.b) returns a pointer to b, so that pointer's auxtype should have the same type as b's auxtype + if (var_b->type->type == ev_variant) + var_c->type = type_variant; + else if (var_c->type->type == ev_pointer) + var_c->type = QCC_PR_PointerType(var_b->type->aux_type); + else if (var_c->type->type == ev_field) + var_c->type = QCC_PR_FieldType(var_b->type->aux_type); + else + var_c->type = var_b->type->aux_type; + var_c->name = var_b->name; + /* if (var_b->type->type == ev_field && var_b->type->aux_type->type == ev_entity) { var_c->type = var_b->type->aux_type; @@ -2712,7 +2988,7 @@ QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_ else { var_c->name = var_b->name; - } + }*/ var_c->s_file = var_b->s_file; var_c->s_line = var_b->s_line; } @@ -2896,44 +3172,54 @@ QCC_def_t *QCC_PR_ParseImmediate (void) return cn; } -QCC_def_t *QCC_PR_GenerateAddressOf(int expressionstart, QCC_def_t *operand) +QCC_ref_t *QCC_PR_GenerateAddressOf(QCC_ref_t *retbuf, QCC_ref_t *operand) { - QCC_def_t *e2; - if (expressionstart != numstatements) - //woo, something like ent.field? +// QCC_def_t *e2; + if (operand->type == REF_FIELD) { - if ((OP_LOAD_F <= statements[numstatements-1].op && statements[numstatements-1].op <= OP_LOAD_FNC) || statements[numstatements-1].op == OP_LOAD_I || statements[numstatements-1].op == OP_LOAD_P) - { - statements[numstatements-1].op = OP_ADDRESS; - operand->type = QCC_PR_PointerType(operand->type); - return operand; - } - else if ((OP_LOADA_F <= statements[numstatements-1].op && statements[numstatements-1].op <= OP_LOADA_I) || statements[numstatements-1].op == OP_LOADA_STRUCT) - { - statements[numstatements-1].op = OP_GLOBALADDRESS; - operand->type = QCC_PR_PointerType(operand->type); - return operand; - } - else if (OP_LOADP_F <= statements[numstatements-1].op && statements[numstatements-1].op <= OP_LOADP_I) - { - statements[numstatements-1].op = OP_POINTER_ADD; - operand->type = QCC_PR_PointerType(operand->type); - return operand; - } - else //this is a restriction that could be lifted, I just want to make sure that I got all the bits first. - { - QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for '&' Must be singular expression or field reference"); - return operand; - } + //&e.f should generate a pointer def + //as opposed to a ref + return QCC_PR_BuildRef(retbuf, + REF_GLOBAL, + QCC_PR_Statement(&pr_opcodes[OP_ADDRESS], operand->base, operand->index, NULL), + NULL, + (operand->index->type->type == ev_field)?operand->index->type->aux_type:type_variant, + true); } -// QCC_PR_ParseWarning(0, "debug: &global"); + if (operand->type == REF_GLOBAL || operand->type == REF_ARRAY) + { + if (!QCC_OPCodeValid(&pr_opcodes[OP_GLOBALADDRESS])) + QCC_PR_ParseError (ERR_BADEXTENSION, "Address-of operator is not supported in this form without extensions. Please use the FTE target."); - if (!QCC_OPCodeValid(&pr_opcodes[OP_GLOBALADDRESS])) - QCC_PR_ParseError (ERR_BADEXTENSION, "Cannot use addressof operator ('&') on a global. Please use the FTE target."); - - e2 = QCC_PR_Statement (&pr_opcodes[OP_GLOBALADDRESS], operand, 0, NULL); - e2->type = QCC_PR_PointerType(operand->type); - return e2; + //&foo (or &((&foo)[5]), which is basically an array). the result is a temp and thus cannot be assigned to (but should be possible to dereference further). + return QCC_PR_BuildRef(retbuf, + REF_GLOBAL, + QCC_PR_Statement(&pr_opcodes[OP_GLOBALADDRESS], operand->base, QCC_SupplyConversion(operand->index, ev_integer, true), NULL), + NULL, + QCC_PR_PointerType(operand->cast), + true); + } + if (operand->type == REF_POINTER) + { + //&(p[5]) just reverts back to p+5. it cannot be assigned to. + QCC_def_t *addr; + if (operand->index) + { + if (!QCC_OPCodeValid(&pr_opcodes[OP_ADD_PIW])) + QCC_PR_ParseError (ERR_BADEXTENSION, "Address-of operator is not supported in this form without extensions. Please use the FTE target."); + addr = QCC_PR_Statement(&pr_opcodes[OP_ADD_PIW], operand->base, QCC_SupplyConversion(operand->index, ev_integer, true), NULL); + } + else + addr = operand->base; + return QCC_PR_BuildRef(retbuf, + REF_GLOBAL, + addr, + NULL, + QCC_PR_PointerType(operand->cast), + true); + } + QCC_PR_ParseError (ERR_BADEXTENSION, "Cannot use addressof operator ('&') on a global. Please use the FTE target."); + return operand; } @@ -3315,8 +3601,9 @@ QCC_def_t *QCC_PR_GenerateFunctionCall (QCC_def_t *newself, QCC_def_t *func, QCC PR_ParseFunctionCall ============ */ -QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *newself, QCC_def_t *func) //warning, the func could have no name set if it's a field call. +QCC_def_t *QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func could have no name set if it's a field call. { + QCC_def_t *newself, *func; QCC_def_t *e, *d, *out; unsigned int arg; QCC_type_t *t, *p; @@ -3326,6 +3613,17 @@ QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *newself, QCC_def_t *func) //warn QCC_def_t *param[MAX_PARMS+MAX_EXTRA_PARMS]; QCC_type_t *paramtypes[MAX_PARMS+MAX_EXTRA_PARMS]; + if (funcref->type == REF_FIELD && strstr(funcref->index->name, "::")) + { + newself = funcref->base; + func = QCC_RefToDef(funcref, false); + } + else + { + newself = NULL; + func = QCC_RefToDef(funcref, true); + } + func->timescalled++; t = func->type; @@ -3358,10 +3656,25 @@ QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *newself, QCC_def_t *func) //warn { int sz; int oldstcount = numstatements; +#if 1 + QCC_ref_t refbuf, *r; + r = QCC_PR_ParseRefValue(&refbuf, pr_classtype, false, false, false); + if (r->type == REF_GLOBAL && !r->index) + { + e = r->base; + if (!e->arraysize) + sz = 1; + else + sz = e->arraysize; + } + else + sz = 1; + sz *= r->cast->size; + QCC_FreeTemp(r->base); + if (r->index) + QCC_FreeTemp(r->index); +#else e = QCC_PR_ParseValue(pr_classtype, false, true, false); - //the term should not have side effects, or generate any actual statements. - numstatements = oldstcount; - QCC_PR_Expect(")"); if (!e) QCC_PR_ParseErrorPrintDef (ERR_NOTAFUNCTION, func, "sizeof term not supported"); if (!e->arraysize) @@ -3369,8 +3682,12 @@ QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *newself, QCC_def_t *func) //warn else sz = e->arraysize; sz *= e->type->size; - sz *= 4; QCC_FreeTemp(e); +#endif + //the term should not have side effects, or generate any actual statements. + numstatements = oldstcount; + QCC_PR_Expect(")"); + sz *= 4; //4 bytes per word return QCC_MakeIntConst(sz); } } @@ -3517,7 +3834,9 @@ QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *newself, QCC_def_t *func) //warn if (old) { d = QCC_GetTemp(type_float); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, &def_ret, d, NULL)); + QCC_PR_SimpleStatement(OP_STORE_F, OFS_RETURN, d->ofs, 0, false); + d->references++; +// QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, &def_ret, d, NULL)); if (def_ret.type->size == 3) QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, old, &def_ret, NULL)); else @@ -3796,7 +4115,6 @@ QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *newself, QCC_def_t *func) //warn p = QCC_PR_Statement(&pr_opcodes[OP_ADDRESS], result, f, NULL); QCC_UnFreeTemp(result); - type_pointer->aux_type = f->type->aux_type; if (v->type->size == 3) QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STOREP_V], v, p, NULL)); else @@ -4269,10 +4587,27 @@ char *basictypenames[] = { "function", "pointer", "integer", + "variant", "struct", "union" }; +QCC_type_t **basictypes[] = +{ + &type_void, + &type_string, + &type_float, + &type_vector, + &type_entity, + &type_field, + &type_function, + &type_pointer, + &type_integer, + &type_variant, + NULL, //type_struct + NULL, //type_union +}; + QCC_def_t *QCC_MemberInParentClass(char *name, QCC_type_t *clas) { //if a member exists, return the member field (rather than mapped-to field) QCC_def_t *def; @@ -4304,6 +4639,7 @@ QCC_def_t *QCC_MemberInParentClass(char *name, QCC_type_t *clas) return QCC_MemberInParentClass(name, clas->parentclass); } +#if 0 //create fields for the types, instanciate the members to the fields. //we retouch the parents each time to guarentee polymorphism works. //FIXME: virtual methods will not work properly. Need to trace down to see if a parent already defined it @@ -4368,12 +4704,8 @@ void QCC_PR_EmitFieldsForMembers(QCC_type_t *clas, int *basictypefield) } //we need the type in here so saved games can still work without saving ints as floats. (would be evil) - ft = QCC_PR_NewType(basictypenames[mt->type], ev_field, false); - ft->aux_type = QCC_PR_NewType(basictypenames[mt->type], mt->type, false); - ft->aux_type->aux_type = type_void; - ft->size = ft->aux_type->size; - ft = QCC_PR_FindType(ft); - sprintf(membername, "__f_%s_%i", ft->name, ++basictypefield[mt->type]); + ft = QCC_PR_FieldType(*basictypes[mt->type]); + sprintf(membername, "::%s%i", basictypenames[mt->type], ++basictypefield[mt->type]); f = QCC_PR_GetDef(ft, membername, NULL, false, 0, GDF_CONST); if (!f) { @@ -4394,7 +4726,7 @@ void QCC_PR_EmitFieldsForMembers(QCC_type_t *clas, int *basictypefield) } } } - +#endif void QCC_PR_EmitClassFunctionTable(QCC_type_t *clas, QCC_type_t *childclas, QCC_def_t *ed) { //go through clas, do the virtual thing only if the child class does not override. @@ -4467,7 +4799,7 @@ void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, QCC_type_t *basetype) pr_scope = NULL; memset(basictypefield, 0, sizeof(basictypefield)); - QCC_PR_EmitFieldsForMembers(basetype, basictypefield); +// QCC_PR_EmitFieldsForMembers(basetype, basictypefield); @@ -4526,143 +4858,124 @@ void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, QCC_type_t *basetype) df->numparms = locals_end - locals_start; } -static QCC_def_t *QCC_PR_ExpandField(QCC_def_t *ent, QCC_def_t *field) +static QCC_def_t *QCC_PR_ExpandField(QCC_def_t *ent, QCC_def_t *field, QCC_type_t *fieldtype, unsigned int preserveflags) { QCC_def_t *r, *tmp; - //FIXME: class.staticmember should directly read staticmember instead of trying to dereference - if (field->type->type == ev_variant || field->type->type != ev_field || !field->type->aux_type) + if (!fieldtype) { - if (field->type->type != ev_variant) + if (field->type->type == ev_field) + fieldtype = field->type->aux_type; + else { - QCC_PR_ParseErrorPrintDef(ERR_INTERNAL, field, "QCC_PR_ExpandField: invalid field type"); + if (field->type->type != ev_variant) + QCC_PR_ParseErrorPrintDef(ERR_INTERNAL, field, "QCC_PR_ExpandField: invalid field type"); + fieldtype = type_variant; } - r = QCC_PR_Statement(&pr_opcodes[OP_LOAD_V], ent, field, NULL); + } + //FIXME: class.staticmember should directly read staticmember instead of trying to dereference + switch(fieldtype->type) + { + default: + QCC_PR_ParseErrorPrintDef(ERR_INTERNAL, field, "QCC_PR_ExpandField: invalid field type"); + r = field; + break; + case ev_integer: + r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_I], ent, field, NULL, preserveflags); + break; + case ev_pointer: + r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_P], ent, field, NULL, preserveflags); tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); memset (tmp, 0, sizeof(QCC_def_t)); - tmp->type = type_variant; + tmp->type = fieldtype; tmp->ofs = r->ofs; tmp->temp = r->temp; tmp->constant = false; tmp->name = r->name; r = tmp; - } - else - { - switch(field->type->aux_type->type) - { - default: - QCC_PR_ParseErrorPrintDef(ERR_INTERNAL, field, "QCC_PR_ExpandField: invalid field type"); - r = field; - break; - case ev_integer: - r = QCC_PR_Statement(&pr_opcodes[OP_LOAD_I], ent, field, NULL); - break; - case ev_pointer: - r = QCC_PR_Statement(&pr_opcodes[OP_LOAD_P], ent, field, NULL); - tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (tmp, 0, sizeof(QCC_def_t)); - tmp->type = field->type->aux_type; - tmp->ofs = r->ofs; - tmp->temp = r->temp; - tmp->constant = false; - tmp->name = r->name; - r = tmp; - break; - case ev_field: - r = QCC_PR_Statement(&pr_opcodes[OP_LOAD_FLD], ent, field, NULL); - tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (tmp, 0, sizeof(QCC_def_t)); - tmp->type = field->type->aux_type; - tmp->ofs = r->ofs; - tmp->temp = r->temp; - tmp->constant = false; - tmp->name = r->name; - r = tmp; - break; - case ev_float: - r = QCC_PR_Statement(&pr_opcodes[OP_LOAD_F], ent, field, NULL); - break; - case ev_string: - r = QCC_PR_Statement(&pr_opcodes[OP_LOAD_S], ent, field, NULL); - break; - case ev_vector: - r = QCC_PR_Statement(&pr_opcodes[OP_LOAD_V], ent, field, NULL); - break; - case ev_function: - //e.f.func() should call the function after switching the 'this' (aka: self) argument. - //which requires that we actually track what the new 'this' is. - //FIXME: maybe we should have a thiscall attribute instead? need to relax the check in QCC_PR_ParseField to any entity type - if (ent->type->parentclass && QCC_PR_CheckToken("(")) - { - QCC_def_t *func; - QCC_def_t *nthis = ent; - nthis->references++; - field->references++; - if (ent->temp) - { - //we need to make sure that d does not get clobbered by the load_fld. - //note that this is kinda inefficient as we'll be copying this value into self later on anyway. - QCC_def_t *t = QCC_GetTemp(type_entity); - nthis = QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], nthis, t, NULL); - QCC_UnFreeTemp(nthis); - } - func = QCC_GetTemp(field->type->aux_type); - QCC_PR_SimpleStatement(OP_LOAD_FNC, nthis->ofs, field->ofs, func->ofs, false); - qcc_usefulstatement=true; - r = QCC_PR_ParseFunctionCall(nthis, func); - r = QCC_PR_ParseArrayPointer(r, true, true); - } - else - { - r = QCC_PR_Statement(&pr_opcodes[OP_LOAD_FNC], ent, field, NULL); - tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (tmp, 0, sizeof(QCC_def_t)); - tmp->type = field->type->aux_type; - tmp->ofs = r->ofs; - tmp->temp = r->temp; - tmp->constant = false; - tmp->name = r->name; - r = tmp; - } - break; - case ev_entity: - r = QCC_PR_Statement(&pr_opcodes[OP_LOAD_ENT], ent, field, NULL); - break; - } + break; + case ev_field: + r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_FLD], ent, field, NULL, preserveflags); + tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (tmp, 0, sizeof(QCC_def_t)); + tmp->type = fieldtype; + tmp->ofs = r->ofs; + tmp->temp = r->temp; + tmp->constant = false; + tmp->name = r->name; + r = tmp; + break; + case ev_variant: + r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_FLD], ent, field, NULL, preserveflags); + tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (tmp, 0, sizeof(QCC_def_t)); + tmp->type = fieldtype; + tmp->ofs = r->ofs; + tmp->temp = r->temp; + tmp->constant = false; + tmp->name = r->name; + r = tmp; + break; + case ev_float: + r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_F], ent, field, NULL, preserveflags); + break; + case ev_string: + r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_S], ent, field, NULL, preserveflags); + break; + case ev_vector: + r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_V], ent, field, NULL, preserveflags); + break; + case ev_function: + r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_FNC], ent, field, NULL, preserveflags); + tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (tmp, 0, sizeof(QCC_def_t)); + tmp->type = fieldtype; + tmp->ofs = r->ofs; + tmp->temp = r->temp; + tmp->constant = false; + tmp->name = r->name; + r = tmp; + break; + case ev_entity: + r = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_ENT], ent, field, NULL, preserveflags); + break; } return r; } -/*checks for .foo an expands in a class-aware fashion +/*checks for .foo and expands in a class-aware fashion normally invoked via QCC_PR_ParseArrayPointer */ -static QCC_def_t *QCC_PR_ParseField(QCC_def_t *d) +static QCC_ref_t *QCC_PR_ParseField(QCC_ref_t *refbuf, QCC_ref_t *lhs) { QCC_type_t *t; - t = d->type; + t = lhs->cast; if (t->type == ev_entity && (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->"))) { - QCC_def_t *field; + QCC_ref_t *field; + QCC_ref_t fieldbuf; if (QCC_PR_CheckToken("(")) { - field = QCC_PR_Expression(TOP_PRIORITY, 0); + field = QCC_PR_RefExpression(&fieldbuf, TOP_PRIORITY, 0); QCC_PR_Expect(")"); } else - field = QCC_PR_ParseValue(d->type, false, false, true); - if (field->type->type == ev_field || field->type->type == ev_variant) - d = QCC_PR_ExpandField(d, field); + field = QCC_PR_ParseRefValue(&fieldbuf, t, false, false, true); + if (field->cast->type == ev_field || field->cast->type == ev_variant) + { + //fields are generally always readonly. that refers to the field def itself, rather than products of said field. + lhs = QCC_PR_BuildRef(refbuf, REF_FIELD, QCC_RefToDef(lhs, true), QCC_RefToDef(field, true), (field->cast->type == ev_field)?field->cast->aux_type:type_variant, lhs->readonly); + } else { - if (d->type->parentclass) - QCC_PR_ParseError(ERR_INTERNAL, "%s is not a field of class %s", field->name, d->type->name); + if (t->parentclass) + QCC_PR_ParseError(ERR_INTERNAL, "%s is not a field of class %s", QCC_RefToDef(field, false)->name, t->name); else - QCC_PR_ParseError(ERR_INTERNAL, "%s is not a field", field->name); + QCC_PR_ParseError(ERR_INTERNAL, "%s is not a field", QCC_RefToDef(field, false)->name); } - d = QCC_PR_ParseField(d); + lhs = QCC_PR_ParseField(refbuf, lhs); } - return d; + return lhs; } /*checks for: @@ -4673,18 +4986,19 @@ within types which are a contiguous block, expanding to an array index. Also calls QCC_PR_ParseField, which does fields too. */ -QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign, pbool makearraypointers) +QCC_ref_t *QCC_PR_ParseRefArrayPointer (QCC_ref_t *retbuf, QCC_ref_t *r, pbool allowarrayassign, pbool makearraypointers) { QCC_type_t *t; QCC_def_t *idx; QCC_def_t *tmp; - QCC_dstatement_t *st; pbool allowarray; unsigned int arraysize; - int statatementstart; - t = d->type; - arraysize = d->arraysize; + t = r->cast; + if (r->type == REF_GLOBAL && r->cast == r->base->type) + arraysize = r->base->arraysize; + else + arraysize = 0; idx = NULL; while(1) { @@ -4696,10 +5010,10 @@ QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign, pbool else if (!idx) { allowarray = arraysize>0 || - (d->type->type == ev_pointer) || - (d->type->type == ev_string) || - (d->type->type == ev_vector) || - (d->type->type == ev_field && d->type->aux_type->type == ev_vector); + (t->type == ev_pointer) || + (t->type == ev_string) || + (t->type == ev_vector) || + (t->type == ev_field && t->aux_type->type == ev_vector); } if (allowarray && QCC_PR_CheckToken("[")) @@ -4711,15 +5025,17 @@ QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign, pbool if (!idx && t->type == ev_pointer && !arraysize) t = t->aux_type; - if (!idx && d->type->type == ev_pointer) + if (!idx && r->cast->type == ev_pointer) { /*no bounds checks on pointer dereferences*/ } - else if (!idx && d->type->type == ev_string && !arraysize) + else if (!idx && r->cast->type == ev_string && !arraysize) { /*automatic runtime bounds checks on strings, I'm not going to check this too much...*/ + r = QCC_PR_BuildRef(retbuf, REF_STRING, QCC_RefToDef(r, true), tmp, type_float, r->readonly); + return QCC_PR_ParseRefArrayPointer(retbuf, r, allowarrayassign, makearraypointers); } - else if ((!idx && d->type->type == ev_vector && !arraysize) || (idx && t->type == ev_vector && !arraysize)) + else if ((!idx && r->cast->type == ev_vector && !arraysize) || (idx && t->type == ev_vector && !arraysize)) { /*array notation on vector*/ if (tmp->constant) @@ -4732,16 +5048,16 @@ QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign, pbool else i = -1; if (i < 0 || i >= 3) - QCC_PR_ParseErrorPrintDef(0, d, "(vector) array index out of bounds"); + QCC_PR_ParseErrorPrintDef(0, r->base, "(vector) array index out of bounds"); } - else if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK])) + else if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK]) && !flag_noboundchecks) { tmp = QCC_SupplyConversion(tmp, ev_integer, true); QCC_PR_SimpleStatement (OP_BOUNDCHECK, tmp->ofs, 3, 0, false); } t = type_float; } - else if ((!idx && d->type->type == ev_field && d->type->aux_type->type == ev_vector && !arraysize) || (idx && t->type == ev_field && t->aux_type->type && !arraysize)) + else if ((!idx && r->cast->type == ev_field && r->cast->aux_type->type == ev_vector && !arraysize) || (idx && t->type == ev_field && t->aux_type->type && !arraysize)) { /*array notation on vector field*/ if (tmp->constant) @@ -4754,9 +5070,9 @@ QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign, pbool else i = -1; if (i < 0 || i >= 3) - QCC_PR_ParseErrorPrintDef(0, d, "(vector) array index out of bounds"); + QCC_PR_ParseErrorPrintDef(0, r->base, "(vector) array index out of bounds"); } - else if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK])) + else if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK]) && !flag_noboundchecks) { tmp = QCC_SupplyConversion(tmp, ev_integer, true); QCC_PR_SimpleStatement (OP_BOUNDCHECK, tmp->ofs, 3, 0, false); @@ -4765,7 +5081,7 @@ QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign, pbool } else if (!arraysize) { - QCC_PR_ParseErrorPrintDef(0, d, "array index on non-array"); + QCC_PR_ParseErrorPrintDef(0, r->base, "array index on non-array"); } else if (tmp->constant) { @@ -4777,11 +5093,11 @@ QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign, pbool else i = -1; if (i < 0 || i >= arraysize) - QCC_PR_ParseErrorPrintDef(0, d, "(constant) array index out of bounds"); + QCC_PR_ParseErrorPrintDef(0, r->base, "(constant) array index out of bounds"); } else { - if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK])) + if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK]) && !flag_noboundchecks) { tmp = QCC_SupplyConversion(tmp, ev_integer, true); QCC_PR_SimpleStatement (OP_BOUNDCHECK, tmp->ofs, arraysize, 0, false); @@ -4851,256 +5167,39 @@ QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign, pbool break; } - statatementstart = numstatements; - if (idx) { - if (d->type->type == ev_pointer) + //okay, not a pointer, we'll have to read it in somehow + if (r->cast->type == ev_pointer) { - switch (t->type) - { - case ev_pointer: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_float: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_F], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_integer: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_string: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_S], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_vector: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_V], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_entity: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_ENT], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_field: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_FLD], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_function: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_FNC], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_struct: - case ev_union: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - - default: - QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); - } - d->type = t; + //generate a reference to ptr[x] + QCC_PR_BuildRef(retbuf, REF_POINTER, QCC_RefToDef(r, true), idx, t, r->readonly); } - else if (d->type->type == ev_string && d->arraysize == 0) - { //array notation on strings - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_C], d, QCC_SupplyConversion(idx, ev_float, true), NULL); - //d->type = type_float; - } - else if (d->type->type == ev_vector && d->arraysize == 0) +/* else if (d->type->type == ev_vector && d->arraysize == 0) { //array notation on vectors (non-field) d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_F], d, QCC_SupplyConversion(idx, ev_integer, true), (QCC_dstatement_t **)0xffffffff); d->type = type_float; } - else if (d->type->type == ev_field && d->type->aux_type->type == ev_vector && d->arraysize == 0) +*//* else if (d->type->type == ev_field && d->type->aux_type->type == ev_vector && d->arraysize == 0) { //array notation on vectors (fields) d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FLD], d, QCC_SupplyConversion(idx, ev_integer, true), (QCC_dstatement_t **)0xffffffff); d->type = type_floatfield; } - else if (QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F])) +*/ else { - /*don't care about assignments. the code can convert an OP_LOADA_F to an OP_ADDRESS on assign*/ - /*source type is a struct, or its an array, or something that can otherwise be directly accessed*/ - switch(t->type) - { - case ev_pointer: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, QCC_SupplyConversion(idx, ev_integer, true), (QCC_dstatement_t **)0xffffffff); - break; - case ev_float: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_F], d, QCC_SupplyConversion(idx, ev_integer, true), (QCC_dstatement_t **)0xffffffff); - break; - case ev_integer: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, QCC_SupplyConversion(idx, ev_integer, true), (QCC_dstatement_t **)0xffffffff); - break; - case ev_string: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_S], d, QCC_SupplyConversion(idx, ev_integer, true), (QCC_dstatement_t **)0xffffffff); - break; - case ev_vector: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_V], d, QCC_SupplyConversion(idx, ev_integer, true), (QCC_dstatement_t **)0xffffffff); - break; - case ev_entity: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_ENT], d, QCC_SupplyConversion(idx, ev_integer, true), (QCC_dstatement_t **)0xffffffff); - break; - case ev_field: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FLD], d, QCC_SupplyConversion(idx, ev_integer, true), (QCC_dstatement_t **)0xffffffff); - break; - case ev_function: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FNC], d, QCC_SupplyConversion(idx, ev_integer, true), (QCC_dstatement_t **)0xffffffff); - break; - case ev_struct: - case ev_union: - //FIXME... - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_STRUCT], d, QCC_SupplyConversion(idx, ev_integer, true), (QCC_dstatement_t **)0xffffffff); - break; - default: - QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); - } - d->type = t; + QCC_PR_BuildRef(retbuf, REF_ARRAY, QCC_RefToDef(r, true), idx, t, r->readonly); } - else if (idx->constant) - { - int cidx; - idx = QCC_SupplyConversion(idx, ev_integer, true); - cidx = G_INT(idx->ofs); + r = retbuf; - d->references++; - tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memcpy (tmp, d, sizeof(QCC_def_t)); - tmp->arraysize = 0; - tmp->ofs = d->ofs + cidx; - d = tmp; - d->type = t; - //d can be assigned to freely - } - else if (allowarrayassign && QCC_PR_CheckToken("=")) - { - /*if its assigned to, generate a functioncall to do the store*/ - QCC_def_t *args[2], *funcretr, *rhs; + //parse recursively + r = QCC_PR_ParseRefArrayPointer(retbuf, r, allowarrayassign, makearraypointers); - d->references++; - funcretr = QCC_PR_GetDef(type_function, qcva("ArraySet*%s", d->name), NULL, true, 0, false); - - rhs = QCC_PR_Expression(TOP_PRIORITY, 0); - if (rhs->type->type != t->type) - QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, d, "Type Mismatch on array assignment"); - - args[0] = QCC_SupplyConversion(idx, ev_float, true); - args[1] = rhs; - qcc_usefulstatement=true; - d = QCC_PR_GenerateFunctionCall(NULL, funcretr, args, NULL, 2); - d->type = t; - - return d; - } - else if (QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F])) - { - if (!d->arraysize) - QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, d, "array lookup on non-array"); - - if (d->temp) - QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, d, "array lookup on a temp"); - - /*hexen2 format has opcodes to read arrays (but has no way to write)*/ - switch(t->type) - { - case ev_field: - case ev_pointer: - case ev_integer: - d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_F], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. - d->type = t; - break; - case ev_float: - d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_F], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. -// st->a = d->ofs; - break; - case ev_vector: - idx = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], QCC_SupplyConversion(idx, ev_float, true), QCC_MakeFloatConst(3), NULL); - d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_V], d, idx, &st); //get pointer to precise def. -// st->a = d->ofs; - break; - case ev_string: - d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_S], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. -// st->a = d->ofs; - break; - case ev_entity: - d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_E], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. -// st->a = d->ofs; - break; - case ev_function: - d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_FNC], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. -// st->a = d->ofs; - break; - default: - QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); - d = NULL; - break; - } - d->type = t; - } - else - { - /*emulate the array access using a function call to do the read for us*/ - QCC_def_t *args[1], *funcretr; - QCC_type_t *ftype; - - d->references++; - - ftype = QCC_PR_NewType("__arrayget", ev_function, false); - ftype->aux_type = t; - ftype->num_parms = 1; - ftype->params = qccHunkAlloc(sizeof(*ftype->params)); - ftype->params[0].arraysize=0; - ftype->params[0].type = type_float; - ftype->params[0].paramname = "__index"; - funcretr = QCC_PR_GetDef(ftype, qcva("ArrayGet*%s", d->name), NULL, true, 0, false); - - /*make sure the function type that we're calling exists*/ - - if (d->type->type == ev_vector) - { - args[0] = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], QCC_SupplyConversion(idx, ev_float, true), QCC_MakeFloatConst(3), NULL); - d = QCC_PR_GenerateFunctionCall(NULL, funcretr, args, &type_float, 1); - d->type = t; - } - else - { - if (t->size > 1) - { - QCC_def_t *r; - unsigned int i; - int old_op = opt_assignments; - d = QCC_GetTemp(t); - idx = QCC_SupplyConversion(idx, ev_float, true); - - for (i = 0; i < t->size; i++) - { - if (i) - args[0] = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], idx, QCC_MakeFloatConst(i), (QCC_dstatement_t **)0xffffffff); - else - { - args[0] = idx; - opt_assignments = false; - } - r = QCC_PR_GenerateFunctionCall(NULL, funcretr, args, &type_float, 1); - opt_assignments = old_op; - QCC_UnFreeTemp(idx); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], r, d, (QCC_dstatement_t **)0xffffffff)); - d->ofs++; - } - QCC_FreeTemp(idx); - d->ofs -= i; - QCC_UnFreeTemp(d); - } - else - { - args[0] = QCC_SupplyConversion(idx, ev_float, true); - d = QCC_PR_GenerateFunctionCall(NULL, funcretr, args, &type_float, 1); - } - d->type = t; - } - } - - /*parse recursively*/ - d = QCC_PR_ParseArrayPointer(d, allowarrayassign, makearraypointers); + if (arraysize && makearraypointers) + r = QCC_PR_GenerateAddressOf(retbuf, r); } - //float b[64]; return b; should return a pointer, and NOT the value b[0]. - if (arraysize && makearraypointers) - d = QCC_PR_GenerateAddressOf(statatementstart, d); - - d = QCC_PR_ParseField(d); - return d; + r = QCC_PR_ParseField(retbuf, r); + return r; } /* @@ -5110,7 +5209,7 @@ PR_ParseValue Returns the global ofs for the current token ============ */ -QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, pbool expandmemberfields, pbool makearraypointers) +QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbool allowarrayassign, pbool expandmemberfields, pbool makearraypointers) { QCC_def_t *d, *od; QCC_type_t *t; @@ -5120,7 +5219,7 @@ QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, p // if the token is an immediate, allocate a constant for it if (pr_token_type == tt_immediate) - return QCC_PR_ParseImmediate (); + return QCC_DefToRef(refbuf, QCC_PR_ParseImmediate ()); if (QCC_PR_CheckToken("[")) { @@ -5160,7 +5259,7 @@ QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, p (z->type->type != ev_float && z->type->type != ev_integer)) { QCC_PR_ParseError(ERR_TYPEMISMATCH, "Argument not a single numeric value in vector constructor"); - return QCC_MakeVectorConst(0, 0, 0); + return QCC_DefToRef(refbuf, QCC_MakeVectorConst(0, 0, 0)); } //return a constant if we can. @@ -5173,7 +5272,7 @@ QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, p QCC_FreeTemp(x); QCC_FreeTemp(y); QCC_FreeTemp(z); - return d; + return QCC_DefToRef(refbuf, d); } //pack the variables into a vector @@ -5201,7 +5300,7 @@ QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, p QCC_FreeTemp(y); QCC_FreeTemp(z); QCC_UnFreeTemp(d); - return d; + return QCC_DefToRef(refbuf, d); } if (QCC_PR_CheckToken("::")) @@ -5211,6 +5310,7 @@ QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, p } name = QCC_PR_ParseName (); + //fixme: namespaces should be relative if (QCC_PR_CheckToken("::")) { expandmemberfields = false; //this::classname should also be available to the find builtin, etc. this won't affect self.classname::member nor classname::staticfunc @@ -5255,9 +5355,7 @@ QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, p } else { - // look through the defs - d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0, false); - + d = NULL; // 'testvar' becomes 'this::testvar' if (assumeclass && assumeclass->parentclass) { //try getting a member. @@ -5271,6 +5369,11 @@ QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, p type = type->parentclass; } } + if (!d) + { + // look through the defs + d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0, false); + } } if (!d) @@ -5353,12 +5456,15 @@ QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, p t = QCC_PR_GetDef(NULL, "self", NULL, true, 0, false); d = QCC_PR_ParseArrayPointer(d, allowarrayassign, makearraypointers); //opportunistic vecmember[0] handling - d = QCC_PR_ExpandField(t, d); + + //then return a reference to this.field + QCC_PR_BuildRef(refbuf, REF_FIELD, t, d, d->type->aux_type, false); + + //(a.(foo[4]))[2] should still function, and may be common with field vectors + return QCC_PR_ParseRefArrayPointer(refbuf, refbuf, allowarrayassign, makearraypointers); //opportunistic vecmember[0] handling } - d = QCC_PR_ParseArrayPointer(d, allowarrayassign, makearraypointers); - - return d; + return QCC_PR_ParseRefArrayPointer(refbuf, QCC_DefToRef(refbuf, d), allowarrayassign, makearraypointers); } @@ -5367,8 +5473,9 @@ QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, p PR_Term ============ */ -QCC_def_t *QCC_PR_Term (int exprflags) +QCC_ref_t *QCC_PR_RefTerm (QCC_ref_t *retbuf, unsigned int exprflags) { + QCC_ref_t *r; QCC_def_t *e, *e2; etype_t t; if (pr_token_type == tt_punct) //a little extra speed... @@ -5393,9 +5500,9 @@ QCC_def_t *QCC_PR_Term (int exprflags) QCC_PR_ParseError(ERR_BADPLUSPLUSOPERATOR, "++ operator on unsupported type"); break; } - return e; + return QCC_DefToRef(retbuf, e); } - else if (QCC_PR_CheckToken("--")) + if (QCC_PR_CheckToken("--")) { qcc_usefulstatement=true; e = QCC_PR_Term (0); @@ -5415,9 +5522,8 @@ QCC_def_t *QCC_PR_Term (int exprflags) QCC_PR_ParseError(ERR_BADPLUSPLUSOPERATOR, "-- operator on unsupported type"); break; } - return e; + return QCC_DefToRef(retbuf, e); } - if (QCC_PR_CheckToken ("!")) { e = QCC_PR_Expression (NOT_PRIORITY, EXPR_DISALLOW_COMMA|EXPR_WARN_ABOVE_1); @@ -5441,9 +5547,10 @@ QCC_def_t *QCC_PR_Term (int exprflags) e2 = NULL; // shut up compiler warning; QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for !"); } - return e2; + return QCC_DefToRef(retbuf, e2); } - else if (QCC_PR_CheckToken ("~")) + + if (QCC_PR_CheckToken ("~")) { e = QCC_PR_Expression (NOT_PRIORITY, EXPR_DISALLOW_COMMA|EXPR_WARN_ABOVE_1); t = e->type->type; @@ -5456,66 +5563,20 @@ QCC_def_t *QCC_PR_Term (int exprflags) e2 = NULL; // shut up compiler warning; QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for binary not"); } - return e2; + return QCC_DefToRef(retbuf, e2); } - else if (QCC_PR_CheckToken ("&")) + if (QCC_PR_CheckToken ("&")) { - int st = numstatements; - e = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA); + r = QCC_PR_RefExpression (retbuf, UNARY_PRIORITY, EXPR_DISALLOW_COMMA); - return QCC_PR_GenerateAddressOf(st, e); + return QCC_PR_GenerateAddressOf(retbuf, r); } - else if (QCC_PR_CheckToken ("*")) + if (QCC_PR_CheckToken ("*")) { e = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA); - t = e->type->type; - - if (t == ev_string) - e2 = QCC_PR_Statement(&pr_opcodes[OP_LOADP_C], e, QCC_MakeFloatConst(0), NULL); - else if (t == ev_pointer) - { - switch(e->type->aux_type->type) - { - case ev_float: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_F], e, 0, NULL); - break; - case ev_string: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_S], e, 0, NULL); - break; - case ev_vector: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_V], e, 0, NULL); - break; - case ev_entity: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_ENT], e, 0, NULL); - break; - case ev_field: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_FLD], e, 0, NULL); - break; - case ev_function: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_FLD], e, 0, NULL); - break; - case ev_integer: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_I], e, 0, NULL); - break; - case ev_pointer: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_I], e, 0, NULL); - break; - - default: - QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for * (unrecognised type)"); - e2 = NULL; - break; - } - e2->type = e->type->aux_type; - } - else - { - e2 = NULL; - QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for *"); - } - return e2; + return QCC_PR_BuildRef(retbuf, REF_POINTER, e, NULL, e->type->aux_type, false); } - else if (QCC_PR_CheckToken ("-")) + if (QCC_PR_CheckToken ("-")) { e = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA); @@ -5535,9 +5596,9 @@ QCC_def_t *QCC_PR_Term (int exprflags) e2 = NULL; break; } - return e2; + return QCC_DefToRef(retbuf, e2); } - else if (QCC_PR_CheckToken ("+")) + if (QCC_PR_CheckToken ("+")) { e = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA); @@ -5557,9 +5618,8 @@ QCC_def_t *QCC_PR_Term (int exprflags) e2 = NULL; break; } - return e2; + return QCC_DefToRef(retbuf, e2); } - if (QCC_PR_CheckToken ("(")) { QCC_type_t *newtype; @@ -5567,6 +5627,8 @@ QCC_def_t *QCC_PR_Term (int exprflags) if (newtype) { QCC_PR_Expect (")"); + //not a single term, so we can cast the result of function calls. just make sure its not too high a priority + //and yeah, okay, use defs not refs. whatever. e = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA); /*you may cast from a type to itself*/ @@ -5578,21 +5640,14 @@ QCC_def_t *QCC_PR_Term (int exprflags) { e->references++; //direct cast - e2 = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (e2, 0, sizeof(QCC_def_t)); - - e2->type = newtype; - e2->ofs = e->ofs; - e2->constant = true; - e2->temp = e->temp; - return e2; + return QCC_PR_BuildRef(retbuf, REF_GLOBAL, e, NULL, newtype, true); } /*cast from int->float will convert*/ else if (newtype->type == ev_float && e->type->type == ev_integer) - return QCC_PR_Statement (&pr_opcodes[OP_CONV_ITOF], e, 0, NULL); + return QCC_DefToRef(retbuf, QCC_PR_Statement (&pr_opcodes[OP_CONV_ITOF], e, 0, NULL)); /*cast from float->int will convert*/ else if (newtype->type == ev_integer && e->type->type == ev_float) - return QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], e, 0, NULL); + return QCC_DefToRef(retbuf, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], e, 0, NULL)); /*you may freely cast between pointers (and ints, as this is explicit) (strings count as pointers - WARNING: some strings may not be expressable as pointers)*/ else if ( //pointers @@ -5607,32 +5662,29 @@ QCC_def_t *QCC_PR_Term (int exprflags) { e->references++; //direct cast - e2 = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (e2, 0, sizeof(QCC_def_t)); - - e2->type = newtype; - e2->ofs = e->ofs; - e2->constant = true; - e2->temp = e->temp; - return e2; + return QCC_PR_BuildRef(retbuf, REF_GLOBAL, e, NULL, newtype, true); } else QCC_PR_ParseError(0, "Bad type cast\n"); + + return QCC_DefToRef(retbuf, e); } else { pbool oldcond = conditional; conditional = conditional?2:0; - e = QCC_PR_Expression (TOP_PRIORITY, 0); + r = QCC_PR_RefExpression(retbuf, TOP_PRIORITY, 0); QCC_PR_Expect (")"); conditional = oldcond; - QCC_PR_ParseArrayPointer(e, true, true); +// QCC_PR_ParseArrayPointer(r, true, true); + + r = QCC_PR_ParseRefArrayPointer(retbuf, r, true, true); } - return e; + return r; } } - return QCC_PR_ParseValue (pr_classtype, !(exprflags&EXPR_DISALLOW_ARRAYASSIGN), true, true); + return QCC_PR_ParseRefValue (retbuf, pr_classtype, !(exprflags&EXPR_DISALLOW_ARRAYASSIGN), true, true); } @@ -5672,29 +5724,868 @@ int QCC_canConv(QCC_def_t *from, etype_t to) } /* ============== -PR_Expression +QCC_PR_RefExpression ============== */ -QCC_def_t *QCC_PR_Expression (int priority, int exprflags) +QCC_ref_t *QCC_PR_BuildRef(QCC_ref_t *retbuf, unsigned int reftype, QCC_def_t *base, QCC_def_t *index, QCC_type_t *cast, pbool readonly) +{ + retbuf->postinc = 0; + retbuf->type = reftype; + retbuf->base = base; + retbuf->index = index; + retbuf->cast = cast?cast:base->type; + retbuf->readonly = readonly; + return retbuf; +} +QCC_ref_t *QCC_DefToRef(QCC_ref_t *retbuf, QCC_def_t *def) +{ + return QCC_PR_BuildRef(retbuf, + REF_GLOBAL, + def, + NULL, + def->type, + !!def->constant); +} +/* +void QCC_StoreToOffset(int dest, int source, QCC_type_t *type) +{ + //fixme: we should probably handle entire structs or something + switch(type->type) + { + default: + case ev_float: + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F, source, dest, 0, false); + break; + case ev_vector: + QCC_PR_SimpleStatement(OP_STORE_V, source, dest, 0, false); + break; + case ev_entity: + QCC_PR_SimpleStatement(OP_STORE_ENT, source, dest, 0, false); + break; + case ev_string: + QCC_PR_SimpleStatement(OP_STORE_S, source, dest, 0, false); + break; + case ev_function: + QCC_PR_SimpleStatement(OP_STORE_FNC, source, dest, 0, false); + break; + case ev_field: + QCC_PR_SimpleStatement(OP_STORE_FLD, source, dest, 0, false); + break; + case ev_integer: + QCC_PR_SimpleStatement(OP_STORE_I, source, dest, 0, false); + break; + case ev_pointer: + QCC_PR_SimpleStatement(OP_STORE_P, source, dest, 0, false); + break; + } +}*/ +void QCC_StoreToDef(QCC_def_t *dest, QCC_def_t *source, QCC_type_t *type, pbool preservesource, pbool preservedest) +{ + int flags = 0; + if (preservesource) + flags |= STFL_PRESERVEA; + if (preservedest) + flags |= STFL_PRESERVEB; + //fixme: we should probably handle entire structs or something + switch(type->type) + { + default: + case ev_float: + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], source, dest, NULL, flags); + break; + case ev_vector: + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_V], source, dest, NULL, flags); + break; + case ev_entity: + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_ENT], source, dest, NULL, flags); + break; + case ev_string: + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_S], source, dest, NULL, flags); + break; + case ev_function: + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FNC], source, dest, NULL, flags); + break; + case ev_field: + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FLD], source, dest, NULL, flags); + break; + case ev_integer: + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I], source, dest, NULL, flags); + break; + case ev_pointer: + QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_P], source, dest, NULL, flags); + break; + } +} +//if readable, returns source (or dest if the store was folded), otherwise returns NULL +QCC_def_t *QCC_CollapseStore(QCC_def_t *dest, QCC_def_t *source, QCC_type_t *type, pbool readable, pbool preservedest) +{ + if (opt_assignments && OpAssignsToC(statements[numstatements-1].op) && source->ofs == statements[numstatements-1].c) + { + if (source->temp) + { + QCC_dstatement_t *statement = &statements[numstatements-1]; + statement->c = dest->ofs; + dest->references++; + + optres_assignments++; + QCC_FreeTemp(source); + if (readable) + return dest; + if (!preservedest) + QCC_FreeTemp(dest); + return NULL; + } + } + QCC_StoreToDef(dest, source, type, readable, preservedest); + if (readable) + return source; + return NULL; +} +void QCC_StoreToPointer(int dest, int source, QCC_type_t *type) +{ + //fixme: we should probably handle entire structs or something + switch(type->type) + { + default: + case ev_float: + QCC_PR_SimpleStatement(OP_STOREP_F, source, dest, 0, false); + break; + case ev_vector: + QCC_PR_SimpleStatement(OP_STOREP_V, source, dest, 0, false); + break; + case ev_entity: + QCC_PR_SimpleStatement(OP_STOREP_ENT, source, dest, 0, false); + break; + case ev_string: + QCC_PR_SimpleStatement(OP_STOREP_S, source, dest, 0, false); + break; + case ev_function: + QCC_PR_SimpleStatement(OP_STOREP_FNC, source, dest, 0, false); + break; + case ev_field: + QCC_PR_SimpleStatement(OP_STOREP_FLD, source, dest, 0, false); + break; + case ev_integer: + QCC_PR_SimpleStatement(OP_STOREP_I, source, dest, 0, false); + break; + case ev_pointer: + QCC_PR_SimpleStatement(OP_STOREP_I/*OP_STOREP_P*/, source, dest, 0, false); + break; + } +} +void QCC_LoadFromPointer(int dest, int source, int idx, QCC_type_t *type) +{ + //fixme: we should probably handle entire structs or something + switch(type->type) + { + case ev_float: + QCC_PR_SimpleStatement (OP_LOADP_F, source, idx, dest, false); + break; + case ev_string: + QCC_PR_SimpleStatement (OP_LOADP_S, source, idx, dest, false); + break; + case ev_vector: + QCC_PR_SimpleStatement (OP_LOADP_V, source, idx, dest, false); + break; + case ev_entity: + QCC_PR_SimpleStatement (OP_LOADP_ENT, source, idx, dest, false); + break; + case ev_field: + QCC_PR_SimpleStatement (OP_LOADP_FLD, source, idx, dest, false); + break; + case ev_function: + QCC_PR_SimpleStatement (OP_LOADP_FNC, source, idx, dest, false); + break; + case ev_integer: + QCC_PR_SimpleStatement (OP_LOADP_I, source, idx, dest, false); + break; + default: + case ev_pointer: + QCC_PR_SimpleStatement (OP_LOADP_I/*OP_LOADP_P*/, source, idx, dest, false); + break; + } +} +void QCC_StoreToArray(QCC_def_t *base, QCC_def_t *index, QCC_def_t *source, QCC_type_t *t) +{ + /*if its assigned to, generate a functioncall to do the store*/ + QCC_def_t *args[2], *funcretr; + + if (QCC_OPCodeValid(&pr_opcodes[OP_GLOBALADDRESS])) + { + QCC_def_t *addr; + //ptr = &base[index]; + addr = QCC_PR_Statement(&pr_opcodes[OP_GLOBALADDRESS], base, QCC_SupplyConversion(index, ev_integer, true), NULL); + //*ptr = source + QCC_StoreToPointer(addr->ofs, source->ofs, t); + source->references++; + QCC_FreeTemp(addr); + } + else + { + base->references++; + funcretr = QCC_PR_GetDef(NULL, qcva("ArraySet*%s", base->name), NULL, false, 0, false); + if (!funcretr) + { + QCC_type_t *arraysetfunc = qccHunkAlloc(sizeof(*arraysetfunc)); + struct QCC_typeparam_s *fparms = qccHunkAlloc(sizeof(*fparms)*2); + arraysetfunc->size = 1; + arraysetfunc->type = ev_function; + arraysetfunc->aux_type = type_void; + arraysetfunc->params = fparms; + arraysetfunc->num_parms = 2; + arraysetfunc->name = "ArraySet"; + fparms[0].type = type_float; + fparms[1].type = base->type; + funcretr = QCC_PR_GetDef(arraysetfunc, qcva("ArraySet*%s", base->name), NULL, true, 0, false); + } + + if (source->type->type != t->type) + QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, base, "Type Mismatch on array assignment"); + + args[0] = QCC_SupplyConversion(index, ev_float, true); + args[1] = source; + qcc_usefulstatement=true; + QCC_FreeTemp(QCC_PR_GenerateFunctionCall(NULL, funcretr, args, NULL, 2)); + } +} +QCC_def_t *QCC_LoadFromArray(QCC_def_t *base, QCC_def_t *index, QCC_type_t *t, pbool preserve) +{ + int flags; + int accel; + + //dp-style opcodes take integer indicies, and thus often need type conversions + //h2-style opcodes take float indicies, but have a built in boundscheck that wrecks havoc with vectors and structs (and thus sucks when the types don't match) + + if (index->type->type != ev_float || t->type != base->type->type) + accel = 2; + else + accel = 1; + + if (accel == 2 && !QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F])) + accel = 1; + if (accel == 1 && !QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F])) + accel = QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F])?2:0; + + if (accel == 2) + { + if (index->type->type == ev_float) + { + flags = preserve?STFL_PRESERVEA:0; + index = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], index, NULL, NULL, preserve?STFL_PRESERVEA:0); + } + else + { + flags = preserve?STFL_PRESERVEA|STFL_PRESERVEB:0; + if (index->type->type != ev_integer) + QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, base, "array index is not a single numeric value"); + } + switch(t->type) + { + case ev_string: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_S], base, index, NULL, flags); //get pointer to precise def. + break; + case ev_float: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_F], base, index, NULL, flags); //get pointer to precise def. + break; + case ev_vector: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_V], base, index, NULL, flags); //get pointer to precise def. + break; + case ev_entity: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_ENT], base, index, NULL, flags); //get pointer to precise def. + break; + case ev_field: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_FLD], base, index, NULL, flags); //get pointer to precise def. + break; + case ev_function: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_FNC], base, index, NULL, flags); //get pointer to precise def. + break; + case ev_pointer: //no OP_LOADA_P + case ev_integer: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_I], base, index, NULL, flags); //get pointer to precise def. + break; +// case ev_variant: +// case ev_struct: +// case ev_union: + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "Unable to load type... oops."); + return NULL; + } + + base->type = t; + return base; + } + else if (accel == 1) + { + if (!base->arraysize) + QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, base, "array lookup on non-array"); + + if (base->temp) + QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, base, "array lookup on a temp"); + + if (index->type->type == ev_integer) + { + flags = preserve?STFL_PRESERVEA:0; + index = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], index, NULL, NULL, preserve?STFL_PRESERVEA:0); + } + else if (index->type->type != ev_float) + { + flags = preserve?STFL_PRESERVEA|STFL_PRESERVEB:0; + QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, base, "array index is not a single numeric value"); + } + + /*hexen2 format has opcodes to read arrays (but has no way to write)*/ + switch(t->type) + { + case ev_field: + case ev_pointer: + case ev_integer: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_FETCH_GBL_F], base, index, NULL, flags); //get pointer to precise def. + base->type = t; + break; + case ev_float: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_FETCH_GBL_F], base, index, NULL, flags); //get pointer to precise def. + break; + case ev_vector: + //hexen2 uses element indicies. we internally use words. + //words means you can pack vectors into structs without the offset needing to be a multiple of 3. + //as its floats, I'm going to try using 0/0.33/0.66 just for the luls + index = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], index, QCC_MakeFloatConst(3), NULL, flags&STFL_PRESERVEA); + flags &= ~STFL_PRESERVEB; + base = QCC_PR_StatementFlags(&pr_opcodes[OP_FETCH_GBL_V], base, index, NULL, flags); //get pointer to precise def. + break; + case ev_string: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_FETCH_GBL_S], base, index, NULL, flags); //get pointer to precise def. + break; + case ev_entity: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_FETCH_GBL_E], base, index, NULL, flags); //get pointer to precise def. + break; + case ev_function: + base = QCC_PR_StatementFlags(&pr_opcodes[OP_FETCH_GBL_FNC], base, index, NULL, flags); //get pointer to precise def. + break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + return NULL; + } + base->type = t; + return base; + } + else + { + /*emulate the array access using a function call to do the read for us*/ + QCC_def_t *args[1], *funcretr; + + base->references++; + + funcretr = QCC_PR_GetDef(NULL, qcva("ArrayGet*%s", base->name), NULL, false, 0, false); + if (!funcretr) + { + QCC_type_t *ftype = qccHunkAlloc(sizeof(*ftype)); + struct QCC_typeparam_s *fparms = qccHunkAlloc(sizeof(*fparms)*1); + ftype->size = 1; + ftype->type = ev_function; + ftype->aux_type = base->type; + ftype->params = fparms; + ftype->num_parms = 1; + ftype->name = "ArrayGet"; + fparms[0].type = type_float; + funcretr = QCC_PR_GetDef(ftype, qcva("ArrayGet*%s", base->name), NULL, true, 0, false); + } + + /*make sure the function type that we're calling exists*/ + + if (base->type->type == ev_vector) + { + args[0] = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], QCC_SupplyConversion(index, ev_float, true), QCC_MakeFloatConst(3), NULL); + base = QCC_PR_GenerateFunctionCall(NULL, funcretr, args, &type_float, 1); + base->type = t; + } + else + { + if (t->size > 1) + { + QCC_def_t *r; + unsigned int i; + int old_op = opt_assignments; + base = QCC_GetTemp(t); + index = QCC_SupplyConversion(index, ev_float, true); + + for (i = 0; i < t->size; i++) + { + if (i) + args[0] = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], index, QCC_MakeFloatConst(i), (QCC_dstatement_t **)0xffffffff); + else + { + args[0] = index; + opt_assignments = false; + } + r = QCC_PR_GenerateFunctionCall(NULL, funcretr, args, &type_float, 1); + opt_assignments = old_op; + QCC_UnFreeTemp(index); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], r, base, (QCC_dstatement_t **)0xffffffff)); + base->ofs++; + } + QCC_FreeTemp(index); + base->ofs -= i; + QCC_UnFreeTemp(base); + } + else + { + args[0] = QCC_SupplyConversion(index, ev_float, true); + base = QCC_PR_GenerateFunctionCall(NULL, funcretr, args, &type_float, 1); + } + base->type = t; + } + } + return base; +} +//reads a ref as required +QCC_def_t *QCC_RefToDef(QCC_ref_t *ref, pbool freetemps) +{ + QCC_def_t *tmp = NULL; + QCC_def_t *ret = ref->base; + if (ref->postinc) + { + QCC_def_t *origv; + int inc = ref->postinc; + ref->postinc = 0; + //read the value, without preventing the store later + ret = QCC_RefToDef(ref, false); + //archive off the old value + tmp = QCC_GetTemp(ret->type); + origv = QCC_CollapseStore(tmp, ret, ret->type, true, !freetemps); + ret = tmp; + //update the value + switch(ref->cast->type) + { + case ev_float: + QCC_StoreToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_F], ret, QCC_MakeFloatConst(inc), NULL, STFL_PRESERVEA), false, !freetemps); + break; + case ev_integer: + QCC_StoreToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], ret, QCC_MakeIntConst(inc), NULL, STFL_PRESERVEA), false, !freetemps); + break; + case ev_pointer: + inc *= ref->cast->aux_type->size; + QCC_StoreToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], ret, QCC_MakeIntConst(inc), NULL, STFL_PRESERVEA), false, !freetemps); + break; + default: + QCC_PR_ParseErrorPrintDef(ERR_INTERNAL, ret, "post increment operator not supported with this type"); + break; + } + //hack any following uses of the ref to refer to the temp + ref->type = REF_GLOBAL; + ref->base = origv; + ref->index = NULL; + ref->readonly = true; + return origv; + } + + switch(ref->type) + { + case REF_GLOBAL: + case REF_ARRAY: + if (ref->index) + { + //FIXME: this needs to be deprecated + if (ref->index->constant) + { + ref->base->references++; + if (ref->index) + ref->index->references++; + if (!tmp) + { + tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + tmp->ofs = ret->ofs; + tmp->temp = ret->temp; + tmp->type = ret->type; + tmp->constant = ret->constant; //don't let people assign to it + ret = tmp; + } + if (ref->index->type->type == ev_float) + ret->ofs += G_FLOAT(ref->index->ofs); + else + ret->ofs += G_INT(ref->index->ofs); + } + else + { +// QCC_PR_ParseWarning(ERR_INTERNAL, "FIXME: load global[var] not supported.\n"); + //array get + ret = QCC_LoadFromArray(ref->base, ref->index, ref->cast, !freetemps); + } + } + break; + case REF_POINTER: + tmp = QCC_GetTemp(ref->cast); + QCC_LoadFromPointer(tmp->ofs, ref->base->ofs, ref->index?ref->index->ofs:0, ref->cast); + return tmp; + case REF_FIELD: + return QCC_PR_ExpandField(ref->base, ref->index, ref->cast, freetemps?0:(STFL_PRESERVEA|STFL_PRESERVEB)); + case REF_STRING: + return QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_C], ref->base, ref->index, NULL, freetemps?0:(STFL_PRESERVEA|STFL_PRESERVEB)); + } + if (ref->cast != ret->type) + { + if (!tmp) + { + ret->references++; + tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + tmp->ofs = ret->ofs; + tmp->temp = ret->temp; + tmp->type = ret->type; + tmp->constant = false; //FIXME: don't let people assign to it. saying its const allows a*1 optimisations. which breaks because it'll be set to 0. that's bad. readonly and const should be different things. + ret = tmp; + } + ret->type = ref->cast; + } + return ret; +} + +//return value is the 'source', unless we folded the store and stripped a temp, in which case it'll be the new value at the given location, either way should have the same value as source. +QCC_def_t *QCC_StoreToRef(QCC_ref_t *dest, QCC_def_t *source, pbool readable, pbool preservedest) +{ + QCC_ref_t ptrref; + QCC_type_t *t = source->type; + if (dest->readonly) + QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to constant %s", dest->base->name); + + if (source->type->type == ev_integer && source->constant && !G_INT(source->ofs)) + { + if (dest->cast->type == ev_vector) + source = QCC_MakeVectorConst(0, 0, 0); + } + else + { + while(t) + { + if (!typecmp_lax(t, dest->cast)) + break; + t = t->parentclass; + } + if (!t) + { + char typea[256]; + char typeb[256]; + TypeName(source->type, typea, sizeof(typea)); + TypeName(dest->cast, typeb, sizeof(typeb)); + if (dest->type == REF_FIELD) + QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "type mismatch: %s to %s %s.%s", typea, typeb, dest->base->type->name, dest->index->name); + else if (dest->index) + QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "type mismatch: %s to %s[]", typea, typeb, dest->base->name); + else + QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "type mismatch: %s to %s %s", typea, typeb, dest->base->name); + } + } + + for(;;) + { + switch(dest->type) + { + case REF_GLOBAL: + case REF_ARRAY: + if (!dest->index || dest->index->constant) + { + QCC_def_t *dd; + // QCC_PR_ParseWarning(0, "FIXME: trying to do references: assignments to arrays with const offset not supported.\n"); + + dest->base->references++; + dd = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (dd, 0, sizeof(QCC_def_t)); + dd->type = dest->cast; + dd->ofs = dest->base->ofs; + dd->temp = NULL; + dd->constant = false; //provably false... + dd->name = dest->base->name; + + if (dest->index) + { + if (dest->index->type->type == ev_float) + dd->ofs += G_FLOAT(dest->index->ofs); + else + dd->ofs += G_INT(dest->index->ofs); + } + + //FIXME: can dest even be a temp? + source = QCC_CollapseStore(dd, source, dest->cast, readable, preservedest); + } + else + { + QCC_StoreToArray(dest->base, dest->index, source, dest->cast); + QCC_FreeTemp(dest->base); + if (dest->index) + QCC_FreeTemp(dest->index); + } + if (!readable) + { + QCC_FreeTemp(source); + source = NULL; + } + break; + case REF_POINTER: + if (dest->index) + { + QCC_def_t *addr; + addr = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], dest->base, QCC_SupplyConversion(dest->index, ev_integer, true), NULL, preservedest?STFL_PRESERVEA:0); + QCC_StoreToPointer(addr->ofs, source->ofs, dest->cast); + QCC_FreeTemp(addr); + } + else + { + QCC_StoreToPointer(dest->base->ofs, source->ofs, dest->cast); + dest->base->references++; + source->references++; + if (!preservedest) + QCC_FreeTemp(dest->base); + } + if (!readable) + { + QCC_FreeTemp(source); + source = NULL; + } + break; + case REF_STRING: + { + QCC_def_t *addr; + if (dest->index) + { + addr = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], dest->base, QCC_SupplyConversion(dest->index, ev_integer, true), NULL); + } + else + { + addr = dest->base; + } + QCC_PR_Statement(&pr_opcodes[OP_STOREP_C], addr, source, NULL); + } + break; + case REF_FIELD: +// { + //fixme: we should do this earlier, to preserve original instruction ordering. + //such that self.enemy = (self = world); still has the same result (more common with function calls) + + dest = QCC_PR_BuildRef(&ptrref, REF_POINTER, + QCC_PR_StatementFlags(&pr_opcodes[OP_ADDRESS], dest->base, dest->index, NULL, preservedest?STFL_PRESERVEA:0), //pointer address + NULL, (dest->index->type->type == ev_field)?dest->index->type->aux_type:type_variant, dest->readonly); + preservedest = false; + continue; + +// source = QCC_StoreToRef( +// QCC_PR_BuildRef(&tmp, REF_POINTER, +// QCC_PR_StatementFlags(&pr_opcodes[OP_ADDRESS], dest->base, dest->index, NULL, preservedest?STFL_PRESERVEA:0), //pointer address +// NULL, (dest->index->type->type == ev_field)?dest->index->type->aux_type:type_variant, dest->readonly), +// source, readable, false); + // QCC_PR_ParseWarning(ERR_INTERNAL, "FIXME: trying to do references: assignments to ent.field not supported.\n"); +// } +// break; + } + break; + } + return source; +} + +/*QCC_ref_t *QCC_PR_RefTerm (QCC_ref_t *ref, unsigned int exprflags) +{ + return QCC_DefToRef(ref, QCC_PR_Term(exprflags)); +}*/ +QCC_def_t *QCC_PR_Term (unsigned int exprflags) +{ + QCC_ref_t refbuf; + return QCC_RefToDef(QCC_PR_RefTerm(&refbuf, exprflags), true); +} +QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, pbool expandmemberfields, pbool makearraypointers) +{ + QCC_ref_t refbuf; + return QCC_RefToDef(QCC_PR_ParseRefValue(&refbuf, assumeclass, allowarrayassign, expandmemberfields, makearraypointers), true); +} +QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign, pbool makestructpointers) +{ + QCC_ref_t refbuf; + QCC_ref_t inr; + QCC_DefToRef(&inr, d); + return QCC_RefToDef(QCC_PR_ParseRefArrayPointer(&refbuf, &inr, allowarrayassign, makestructpointers), true); +} + +void QCC_PR_DiscardRef(QCC_ref_t *ref) +{ + if (ref->postinc) + { + QCC_def_t *oval; + int inc = ref->postinc; + ref->postinc = 0; + //read the value + oval = QCC_RefToDef(ref, false); + //and update it + switch(ref->cast->type) + { + case ev_float: + QCC_StoreToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_F], oval, QCC_MakeFloatConst(inc), NULL, 0), false, false); + break; + case ev_integer: + QCC_StoreToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], oval, QCC_MakeIntConst(inc), NULL, 0), false, false); + break; + case ev_pointer: + inc *= ref->cast->aux_type->size; + QCC_StoreToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], oval, QCC_MakeIntConst(inc), NULL, 0), false, false); + break; + default: + QCC_PR_ParseErrorPrintDef(ERR_INTERNAL, oval, "post increment operator not supported with this type"); + break; + } + qcc_usefulstatement = true; + } + + QCC_FreeTemp(ref->base); + if (ref->index) + QCC_FreeTemp(ref->index); +} + +QCC_opcode_t *QCC_PR_ChooseOpcode(QCC_def_t *lhs, QCC_def_t *rhs, QCC_opcode_t **priority) { - QCC_dstatement32_t *st; QCC_opcode_t *op, *oldop; QCC_opcode_t *bestop; int numconversions, c; + etype_t type_a; + etype_t type_c; + + op = oldop = *priority++; + + // type check + type_a = lhs->type->type; +// type_b = rhs->type->type; + + if (op->name[0] == '.')// field access gets type from field + { + if (rhs->type->aux_type) + type_c = rhs->type->aux_type->type; + else + type_c = -1; // not a field + } + else + type_c = ev_void; + + bestop = NULL; + numconversions = 32767; + while (op) + { + if (!(type_c != ev_void && type_c != (*op->type_c)->type)) + { + if (!STRCMP (op->name , oldop->name)) //matches + { + //return values are never converted - what to? +// if (type_c != ev_void && type_c != op->type_c->type->type) +// { +// op++; +// continue; +// } + + if (op->associative!=ASSOC_LEFT) + {//assignment + if (op->type_a == &type_pointer) //ent var + { + /*FIXME: I don't like this code*/ + if (lhs->type->type != ev_pointer) + c = -200; //don't cast to a pointer. + else if ((*op->type_c)->type == ev_void && op->type_b == &type_pointer && rhs->type->type == ev_pointer) + c = 0; //generic pointer... fixme: is this safe? make sure both sides are equivelent + else if (lhs->type->aux_type->type != (*op->type_b)->type) //if e isn't a pointer to a type_b + c = -200; //don't let the conversion work + else + c = QCC_canConv(rhs, (*op->type_c)->type); + } + else + { + c=QCC_canConv(rhs, (*op->type_b)->type); + if (type_a != (*op->type_a)->type) //in this case, a is the final assigned value + c = -300; //don't use this op, as we must not change var b's type + else if ((*op->type_a)->type == ev_pointer && lhs->type->aux_type->type != (*op->type_a)->aux_type->type) + c = -300; //don't use this op if its a pointer to a different type + } + } + else + { + /*if (op->type_a == &type_pointer) //ent var + { + if (e2->type->type != ev_pointer || e2->type->aux_type->type != (*op->type_b)->type) //if e isn't a pointer to a type_b + c = -200; //don't let the conversion work + else + c = 0; + } + else*/ + { + c=QCC_canConv(lhs, (*op->type_a)->type); + c+=QCC_canConv(rhs, (*op->type_b)->type); + } + } + + if (c>=0 && c < numconversions) + { + bestop = op; + numconversions=c; + if (c == 0)//can't get less conversions than 0... + break; + } + } + else + break; + } + op = *priority++; + } + if (bestop == NULL) + { + if (oldop->priority == CONDITION_PRIORITY) + op = oldop; + else + { + if (flag_laxcasts) + { + op = oldop; + QCC_PR_ParseWarning(WARN_LAXCAST, "type mismatch for %s (%s and %s)", oldop->name, lhs->type->name, rhs->type->name); + } + else + QCC_PR_ParseError (ERR_TYPEMISMATCH, "type mismatch for %s (%s and %s)", oldop->name, lhs->type->name, rhs->type->name); + } + } + else + { + if (numconversions>3) + QCC_PR_ParseWarning(WARN_IMPLICITCONVERSION, "Implicit conversion from %s to %s", rhs->type->name, lhs->type->name); + op = bestop; + } + return op; +} + +QCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags) +{ + QCC_ref_t rhsbuf; +// QCC_dstatement32_t *st; + QCC_opcode_t *op; + int opnum; - QCC_def_t *e, *e2; - etype_t type_a; - //etype_t type_b; - etype_t type_c; + QCC_ref_t *lhsr, *rhsr; + QCC_def_t *lhsd, *rhsd; if (priority == 0) - return QCC_PR_Term (exprflags); + { + lhsr = QCC_PR_RefTerm (retbuf, exprflags); + if (!STRCMP(pr_token, "++")) + { + if (lhsr->readonly) + QCC_PR_ParseError(ERR_PARSEERRORS, "postincrement: lhs is readonly"); + lhsr->postinc += 1; + QCC_PR_Lex(); + } + else if (!STRCMP(pr_token, "--")) + { + if (lhsr->readonly) + QCC_PR_ParseError(ERR_PARSEERRORS, "postdecrement: lhs is readonly"); + lhsr->postinc += -1; + QCC_PR_Lex(); + } + return lhsr; + } - e = QCC_PR_Expression (priority-1, exprflags); + lhsr = QCC_PR_RefExpression (retbuf, priority-1, exprflags); while (1) { @@ -5703,31 +6594,33 @@ QCC_def_t *QCC_PR_Expression (int priority, int exprflags) if (QCC_PR_CheckToken ("(") ) { qcc_usefulstatement=true; - e = QCC_PR_ParseFunctionCall (NULL, e); - e = QCC_PR_ParseArrayPointer(e, true, true); + lhsd = QCC_PR_ParseFunctionCall (lhsr); + lhsd = QCC_PR_ParseArrayPointer(lhsd, true, true); + lhsr = QCC_DefToRef(retbuf, lhsd); } if (QCC_PR_CheckToken ("?")) { + QCC_def_t *val, *r; QCC_dstatement32_t *fromj, *elsej; - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_IFNOT_I], e, NULL, &fromj)); - e = QCC_PR_Expression(TOP_PRIORITY, 0); - e2 = QCC_GetTemp(e->type); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[(e2->type->size>=3)?OP_STORE_V:OP_STORE_F], e, e2, (QCC_dstatement_t **)0xffffffff)); - //e2 can be stomped upon until its reused anyway - QCC_UnFreeTemp(e2); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_IFNOT_I], QCC_RefToDef(lhsr, true), NULL, &fromj)); + val = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + r = QCC_GetTemp(val->type); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[(r->type->size>=3)?OP_STORE_V:OP_STORE_F], val, r, (QCC_dstatement_t **)0xffffffff)); + //r can be stomped upon until its reused anyway + QCC_UnFreeTemp(r); QCC_PR_Expect(":"); QCC_PR_Statement(&pr_opcodes[OP_GOTO], NULL, NULL, &elsej); fromj->b = &statements[numstatements] - fromj; - e = QCC_PR_Expression(TOP_PRIORITY, 0); + val = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); - if (typecmp(e->type, e2->type) != 0) + if (typecmp(val->type, r->type) != 0) QCC_PR_ParseError(0, "Ternary operator with mismatching types\n"); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[(e2->type->size>=3)?OP_STORE_V:OP_STORE_F], e, e2, (QCC_dstatement_t **)0xffffffff)); - QCC_UnFreeTemp(e2); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[(r->type->size>=3)?OP_STORE_V:OP_STORE_F], val, r, (QCC_dstatement_t **)0xffffffff)); + QCC_UnFreeTemp(r); elsej->a = &statements[numstatements] - elsej; - return e2; + return QCC_DefToRef(retbuf, r); } } @@ -5749,377 +6642,214 @@ QCC_def_t *QCC_PR_Expression (int priority, int exprflags) QCC_PR_ParseWarning(WARN_UNEXPECTEDPUNCT, "Expected punctuation"); } - //go straight for the correct priority. - for (op = opcodeprioritized[priority][opnum]; op; op = opcodeprioritized[priority][++opnum]) -// for (op=pr_opcodes ; op->name ; op++) - { -// if (op->priority != priority) -// continue; - if (!QCC_PR_CheckToken (op->name)) - continue; - st = NULL; - if ( op->associative!=ASSOC_LEFT ) + if (priority == 6) + { //assignments + QCC_opcode_t **ops = NULL; + char *opname = NULL; + int i; + if (QCC_PR_CheckToken ("=")) { - // if last statement is an indirect, change it to an address of - if (!simplestore && ((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I || statements[numstatements-1].op == OP_LOAD_P) && statements[numstatements-1].c == e->ofs) - { - qcc_usefulstatement=true; - statements[numstatements-1].op = OP_ADDRESS; - type_pointer->aux_type = e->type; - e->type = type_pointer; - } - //if last statement retrieved a value, switch it to retrieve a usable pointer. - if ( !simplestore && (unsigned)(statements[numstatements-1].op - OP_LOADA_F) < 7)// || statements[numstatements-1].op == OP_LOADA_C) - { - statements[numstatements-1].op = OP_GLOBALADDRESS; - type_pointer->aux_type = e->type; - e->type = type_pointer; - } - if ( !simplestore && (unsigned)(statements[numstatements-1].op - OP_LOADP_F) < 7 && statements[numstatements-1].c == e->ofs) - { - if (!statements[numstatements-1].b) - { - //if the loadp has no offset, remove the instruction and convert the dest of this instruction directly to the pointer's load address - //this kills the add 0. - e->ofs = statements[numstatements-1].a; - numstatements--; - } - else - { - statements[numstatements-1].op = OP_POINTER_ADD; - } - if (e->type != type_pointer) - { - type_pointer->aux_type = e->type; - e->type = type_pointer; - } - } - if ( !simplestore && statements[numstatements-1].op == OP_LOADP_C && e->ofs == statements[numstatements-1].c) - { - statements[numstatements-1].op = OP_ADD_SF; - e->type = type_string; - - //now we want to make sure that string = float can't work without it being a dereferenced pointer. (we don't want to allow storep_c without dereferece) - e2 = QCC_PR_Expression (priority, exprflags); - if (e2->type->type == ev_float) - op = &pr_opcodes[OP_STOREP_C]; - } - else - e2 = QCC_PR_Expression (priority, exprflags); + ops = opcodes_store; + opname = "="; } - else + else if (QCC_PR_CheckToken ("+=")) { - if (op->priority == 7 && opt_logicops) - { - optres_logicops++; - st = &statements[numstatements]; - if (*op->name == '&') //statement 3 because we don't want to optimise this into if from not ifnot - QCC_PR_Statement3(&pr_opcodes[OP_IFNOT_I], e, NULL, NULL, false); - else - QCC_PR_Statement3(&pr_opcodes[OP_IF_I], e, NULL, NULL, false); - } - - e2 = QCC_PR_Expression (priority-1, exprflags | EXPR_DISALLOW_ARRAYASSIGN); + ops = opcodes_addstore; + opname = "+="; + } + else if (QCC_PR_CheckToken ("-=")) + { + ops = opcodes_substore; + opname = "-="; + } + else if (QCC_PR_CheckToken ("|=")) + { + ops = opcodes_orstore; + opname = "|="; + } + else if (QCC_PR_CheckToken ("&=")) + { + ops = opcodes_andstore; + opname = "&="; + } + else if (QCC_PR_CheckToken ("&~=")) + { + ops = opcodes_clearstore; + opname = "&~="; + } + else if (QCC_PR_CheckToken ("^=")) + { + ops = opcodes_xorstore; + opname = "^="; + } + else if (QCC_PR_CheckToken ("*=")) + { + ops = opcodes_mulstore; + opname = "*="; + } + else if (QCC_PR_CheckToken ("/=")) + { + ops = opcodes_divstore; + opname = "/="; } - // type check - type_a = e->type->type; -// type_b = e2->type->type; - - if (op->name[0] == '.')// field access gets type from field + if (ops) { - if (e2->type->aux_type) - type_c = e2->type->aux_type->type; - else - type_c = -1; // not a field - } - else - type_c = ev_void; + if (lhsr->postinc) + QCC_PR_ParseError(ERR_INTERNAL, "Assignment to lvalue"); + if (lhsr->readonly) + QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to lvalue"); - oldop = op; - bestop = NULL; - numconversions = 32767; - while (op) - { - if (!(type_c != ev_void && type_c != (*op->type_c)->type)) - { - if (!STRCMP (op->name , oldop->name)) //matches - { - //return values are never converted - what to? - // if (type_c != ev_void && type_c != op->type_c->type->type) - // { - // op++; - // continue; - // } + rhsr = QCC_PR_RefExpression (&rhsbuf, priority, exprflags | EXPR_DISALLOW_ARRAYASSIGN); - if (op->associative!=ASSOC_LEFT) - {//assignment - if (op->type_a == &type_pointer) //ent var - { - /*FIXME: I don't like this code*/ - if (e->type->type != ev_pointer) - c = -200; //don't cast to a pointer. - else if ((*op->type_c)->type == ev_void && op->type_b == &type_pointer && e2->type->type == ev_pointer) - c = 0; //generic pointer... fixme: is this safe? make sure both sides are equivelent - else if (e->type->aux_type->type != (*op->type_b)->type) //if e isn't a pointer to a type_b - c = -200; //don't let the conversion work - else - c = QCC_canConv(e2, (*op->type_c)->type); - } - else - { - c=QCC_canConv(e2, (*op->type_b)->type); - if (type_a != (*op->type_a)->type) //in this case, a is the final assigned value - c = -300; //don't use this op, as we must not change var b's type - else if ((*op->type_a)->type == ev_pointer && e->type->aux_type->type != (*op->type_a)->aux_type->type) - c = -300; //don't use this op if its a pointer to a different type - } - } - else - { - /*if (op->type_a == &type_pointer) //ent var - { - if (e2->type->type != ev_pointer || e2->type->aux_type->type != (*op->type_b)->type) //if e isn't a pointer to a type_b - c = -200; //don't let the conversion work - else - c = 0; - } - else*/ - { - c=QCC_canConv(e, (*op->type_a)->type); - c+=QCC_canConv(e2, (*op->type_b)->type); - } - } - - if (c>=0 && c < numconversions) - { - bestop = op; - numconversions=c; - if (c == 0)//can't get less conversions than 0... - break; - } - } - else - break; - } - op = opcodeprioritized[priority][++opnum]; - } - if (bestop == NULL) - { - if (oldop->priority == CONDITION_PRIORITY) - op = oldop; - else - { - if (flag_laxcasts) - { - op = oldop; - QCC_PR_ParseWarning(WARN_LAXCAST, "type mismatch for %s (%s and %s)", oldop->name, e->type->name, e2->type->name); - } - else - QCC_PR_ParseError (ERR_TYPEMISMATCH, "type mismatch for %s (%s and %s)", oldop->name, e->type->name, e2->type->name); - } - } - else - { - if (numconversions>3) - QCC_PR_ParseWarning(WARN_IMPLICITCONVERSION, "Implicit conversion"); - op = bestop; - } - -// if (type_a == ev_pointer && type_b != e->type->aux_type->type) -// QCC_PR_ParseError ("type mismatch for %s", op->name); - - if (st) - st->b = &statements[numstatements] - st; - - - if (op->associative!=ASSOC_LEFT) - { - qcc_usefulstatement = true; - if (e->constant || e->ofs < OFS_PARM0) - { - if (e->type->type == ev_function) - { - QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANTFUNC, "Assignment to function %s", e->name); - QCC_PR_ParsePrintDef(WARN_ASSIGNMENTTOCONSTANTFUNC, e); - } - else - { - QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to constant %s", e->name); - QCC_PR_ParsePrintDef(WARN_ASSIGNMENTTOCONSTANT, e); - } -#ifndef QCC - editbadfile(strings+s_file, pr_source_line); -#endif - } if (conditional&1) QCC_PR_ParseWarning(WARN_ASSIGNMENTINCONDITIONAL, "Assignment in conditional"); - e = QCC_PR_Statement (op, e2, e, NULL); + rhsd = QCC_RefToDef(rhsr, true); + + if (ops != opcodes_store) + { + lhsd = QCC_RefToDef(lhsr, false); + for (i = 0; ops[i]; i++) + { + op = ops[i]; +// if (QCC_OPCodeValid(op)) + { + if ((*op->type_b)->type == rhsd->type->type && (*op->type_a)->type == lhsd->type->type) + break; + } + } + if (!ops[i]) + { + rhsd = QCC_SupplyConversion(rhsd, lhsr->cast->type, true); + for (i = 0; ops[i]; i++) + { + op = ops[i]; + // if (QCC_OPCodeValid(op)) + { + if ((*op->type_b)->type == rhsd->type->type && (*op->type_a)->type == lhsd->type->type) + break; + } + } + if (!ops[i]) + QCC_PR_ParseError(0, "Type mismatch on assignment. %s %s %s is not supported\n", lhsd->type->name, opname, rhsd->type->name); + } + + if (op->associative != ASSOC_LEFT) + { + rhsd = QCC_PR_Statement(op, lhsd, rhsd, NULL); + } + else + rhsd = QCC_PR_Statement(op, lhsd, rhsd, NULL); + + //convert so we don't have issues with: i = (int)(float)(i+f) + //this will also catch things like vec *= vec; which would be trying to store a float into a vector. + rhsd = QCC_SupplyConversion(rhsd, lhsr->cast->type, true); + } + else + { + if (rhsd->constant && rhsd->type->type == ev_integer && !G_INT(rhsd->ofs)) + { + if (lhsr->cast->type == ev_vector) + rhsd = QCC_MakeVectorConst(0,0,0); + else if (lhsr->cast->type == ev_struct || lhsr->cast->type == ev_union) + { + QCC_PR_ParseError(0, "Type mismatch on assignment. %s %s %s is not supported\n", lhsd->type->name, opname, rhsd->type->name); + } + } + else + rhsd = QCC_SupplyConversion(rhsd, lhsr->cast->type, true); + } + rhsd = QCC_StoreToRef(lhsr, rhsd, true, false); //FIXME: this should not always be true, but we don't know if the caller actually needs it + qcc_usefulstatement = true; + lhsr = QCC_DefToRef(retbuf, rhsd); //we read the rhs, we can just return that as the result + lhsr->readonly = true; //(a=b)=c is an error } else - e = QCC_PR_Statement (op, e, e2, NULL); - - if (type_c != ev_void/* && type_c != ev_string*/) // field access gets type from field - e->type = e2->type->aux_type; - - if (priority > 1 && exprflags & EXPR_WARN_ABOVE_1) - QCC_PR_ParseWarning(WARN_UNARYNOTSCOPE, "You may wish to add brackets after that ! operator"); - - break; + break; } - if (!op) + else { - if (e == NULL) - QCC_PR_ParseError(ERR_INTERNAL, "e == null"); - - if (!STRCMP(pr_token, "++")) + //go straight for the correct priority. + for (op = opcodeprioritized[priority][opnum]; op; op = opcodeprioritized[priority][++opnum]) + // for (op=pr_opcodes ; op->name ; op++) { - //if the last statement was an ent.float (or something) - if (((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I) && statements[numstatements-1].c == e->ofs) - { //we have our load. - QCC_def_t *e3; -//the only inefficiency here is with an extra temp (we can't reuse the original) -//this is not a problem, as the optimise temps or locals marshalling can clean these up for us - qcc_usefulstatement=true; -//load -//add to temp -//store temp to offset -//return original loaded (which is not at the same offset as the pointer we store to) - e2 = QCC_GetTemp(type_float); - e3 = QCC_GetTemp(type_pointer); - QCC_PR_SimpleStatement(OP_ADDRESS, statements[numstatements-1].a, statements[numstatements-1].b, e3->ofs, false); - if (e->type->type == ev_float) - { - QCC_PR_Statement3(&pr_opcodes[OP_ADD_F], e, QCC_MakeFloatConst(1), e2, false); - QCC_PR_Statement3(&pr_opcodes[OP_STOREP_F], e2, e3, NULL, false); - } - else if (e->type->type == ev_integer) - { - QCC_PR_Statement3(&pr_opcodes[OP_ADD_I], e, QCC_MakeIntConst(1), e2, false); - QCC_PR_Statement3(&pr_opcodes[OP_STOREP_I], e2, e3, NULL, false); - } - else - { - QCC_PR_ParseError(ERR_PARSEERRORS, "-- suffix operator results in nonstandard behaviour. Use -=1 or prefix form instead"); - QCC_PR_IncludeChunk("-=1", false, NULL); - } - QCC_FreeTemp(e2); - QCC_FreeTemp(e3); - } - else if (e->type->type == ev_float) - { -//copy to temp -//add to original -//return temp (which == original) - QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "++ suffix operator results in inefficient behaviour. Use +=1 or prefix form instead"); - qcc_usefulstatement=true; + // if (op->priority != priority) + // continue; + if (!QCC_PR_CheckToken (op->name)) + continue; + + rhsr = QCC_PR_RefExpression (&rhsbuf, priority-1, exprflags | EXPR_DISALLOW_ARRAYASSIGN); - e2 = QCC_GetTemp(type_float); - QCC_PR_Statement3(&pr_opcodes[OP_STORE_F], e, e2, NULL, false); - QCC_PR_Statement3(&pr_opcodes[OP_ADD_F], e, QCC_MakeFloatConst(1), e, false); - QCC_FreeTemp(e); - e = e2; - } - else if (e->type->type == ev_integer) + if (op->associative!=ASSOC_LEFT) { - QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "++ suffix operator results in inefficient behaviour. Use +=1 or prefix form instead"); - qcc_usefulstatement=true; - - e2 = QCC_GetTemp(type_integer); - QCC_PR_Statement3(&pr_opcodes[OP_STORE_I], e, e2, NULL, false); - QCC_PR_Statement3(&pr_opcodes[OP_ADD_I], e, QCC_MakeIntConst(1), e, false); - QCC_FreeTemp(e); - e = e2; + QCC_PR_ParseError(ERR_INTERNAL, "internal error: shuold be unreachable\n"); } else { - QCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, "++ suffix operator results in nonstandard behaviour. Use +=1 or prefix form instead"); - QCC_PR_IncludeChunk("+=1", false, NULL); + lhsd = QCC_RefToDef(lhsr, true); + rhsd = QCC_RefToDef(rhsr, true); + op = QCC_PR_ChooseOpcode(lhsd, rhsd, &opcodeprioritized[priority][opnum]); + + lhsd = QCC_PR_Statement (op, lhsd, rhsd, NULL); + lhsr = QCC_DefToRef(retbuf, lhsd); } - QCC_PR_Lex(); - } - else if (!STRCMP(pr_token, "--")) - { - if (((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I) && statements[numstatements-1].c == e->ofs) - { //we have our load. - QCC_def_t *e3; -//load -//add to temp -//store temp to offset -//return original loaded (which is not at the same offset as the pointer we store to) - e2 = QCC_GetTemp(type_float); - e3 = QCC_GetTemp(type_pointer); - QCC_PR_SimpleStatement(OP_ADDRESS, statements[numstatements-1].a, statements[numstatements-1].b, e3->ofs, false); - if (e->type->type == ev_float) - { - QCC_PR_Statement3(&pr_opcodes[OP_SUB_F], e, QCC_MakeFloatConst(1), e2, false); - QCC_PR_Statement3(&pr_opcodes[OP_STOREP_F], e2, e3, NULL, false); - } - else if (e->type->type == ev_integer) - { - QCC_PR_Statement3(&pr_opcodes[OP_SUB_I], e, QCC_MakeIntConst(1), e2, false); - QCC_PR_Statement3(&pr_opcodes[OP_STOREP_I], e2, e3, NULL, false); - } - else - { - QCC_PR_ParseError(ERR_PARSEERRORS, "-- suffix operator results in nonstandard behaviour. Use -=1 or prefix form instead"); - QCC_PR_IncludeChunk("-=1", false, NULL); - } - QCC_FreeTemp(e2); - QCC_FreeTemp(e3); - } - else if (e->type->type == ev_float) - { - QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "-- suffix operator results in inefficient behaviour. Use -=1 or prefix form instead"); - qcc_usefulstatement=true; - e2 = QCC_GetTemp(type_float); - QCC_PR_Statement3(&pr_opcodes[OP_STORE_F], e, e2, NULL, false); - QCC_PR_Statement3(&pr_opcodes[OP_SUB_F], e, QCC_MakeFloatConst(1), e, false); - QCC_FreeTemp(e); - e = e2; - } - else if (e->type->type == ev_integer) - { - QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "-- suffix operator results in inefficient behaviour. Use -=1 or prefix form instead"); - qcc_usefulstatement=true; + if (priority > 1 && exprflags & EXPR_WARN_ABOVE_1) + QCC_PR_ParseWarning(WARN_UNARYNOTSCOPE, "You may wish to add brackets after that ! operator"); - e2 = QCC_GetTemp(type_integer); - QCC_PR_Statement3(&pr_opcodes[OP_STORE_I], e, e2, NULL, false); - QCC_PR_Statement3(&pr_opcodes[OP_SUB_I], e, QCC_MakeIntConst(1), e, false); - QCC_FreeTemp(e); - e = e2; - } - else - { - QCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, "-- suffix operator results in nonstandard behaviour. Use -=1 or prefix form instead"); - QCC_PR_IncludeChunk("-=1", false, NULL); - } - QCC_PR_Lex(); + break; } - break; // next token isn't at this priority level + if (!op) + break; } } - if (e == NULL) + if (lhsr == NULL) QCC_PR_ParseError(ERR_INTERNAL, "e == null"); if (!(exprflags&EXPR_DISALLOW_COMMA) && priority == TOP_PRIORITY && QCC_PR_CheckToken (",")) { + QCC_PR_DiscardRef(lhsr); if (!qcc_usefulstatement) QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "Effectless statement"); - - QCC_FreeTemp(e); qcc_usefulstatement = false; - e = QCC_PR_Expression(TOP_PRIORITY, exprflags); + lhsr = QCC_PR_RefExpression(retbuf, TOP_PRIORITY, exprflags); } - return e; + return lhsr; +} + +QCC_def_t *QCC_PR_Expression (int priority, int exprflags) +{ + QCC_ref_t refbuf, *ret; + ret = QCC_PR_RefExpression(&refbuf, priority, exprflags); + return QCC_RefToDef(ret, true); +} +//parse the expression and discard the result. generate a warning if there were no assignments +//this avoids generating getter statements from RefToDef in QCC_PR_Expression. +void QCC_PR_DiscardExpression (int priority, int exprflags) +{ + QCC_ref_t refbuf, *ref; + pbool olduseful = qcc_usefulstatement; + qcc_usefulstatement = false; + + ref = QCC_PR_RefExpression(&refbuf, priority, exprflags); + QCC_PR_DiscardRef(ref); + + if (ref->cast->type != ev_void && !qcc_usefulstatement) + { +// int osl = pr_source_line; +// pr_source_line = statementstart; + QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "Effectless statement"); +// pr_source_line = osl; + } + qcc_usefulstatement = olduseful; } int QCC_PR_IntConstExpr(void) { + //fixme: should make sure that no actual statements are generated QCC_def_t *def = QCC_PR_Expression(TOP_PRIORITY, 0); if (def->constant) { @@ -6323,7 +7053,16 @@ void QCC_PR_ParseStatement (void) return; } e = QCC_PR_Expression (TOP_PRIORITY, 0); - e2 = QCC_SupplyConversion(e, pr_scope->type->aux_type->type, true); + if (e->constant && e->type->type == ev_integer && !G_INT(e->ofs)) + { + //return __NULL__; is allowed regardless of actual return type. + if (pr_scope->type->aux_type->type == ev_vector) + e2 = QCC_MakeVectorConst(0, 0, 0); + else + e2 = e; + } + else + e2 = QCC_SupplyConversion(e, pr_scope->type->aux_type->type, true); if (e != e2) { QCC_PR_ParseWarning(WARN_CORRECTEDRETURNTYPE, "\'%s\' returned %s, expected %s, conversion supplied", pr_scope->name, e->type->name, pr_scope->type->aux_type->name); @@ -6460,7 +7199,7 @@ void QCC_PR_ParseStatement (void) QCC_PR_Expect("("); if (!QCC_PR_CheckToken(";")) { - QCC_FreeTemp(QCC_PR_Expression(TOP_PRIORITY, 0)); + QCC_PR_DiscardExpression(TOP_PRIORITY, 0); QCC_PR_Expect(";"); } @@ -7176,21 +7915,10 @@ void QCC_PR_ParseStatement (void) // qcc_functioncalled=0; - qcc_usefulstatement = false; - e = QCC_PR_Expression (TOP_PRIORITY, 0); + QCC_PR_DiscardExpression (TOP_PRIORITY, 0); expandedemptymacro = false; QCC_PR_Expect (";"); - if (e->type->type != ev_void && !qcc_usefulstatement) - { - int osl = pr_source_line; - pr_source_line = statementstart; - QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "Effectless statement"); - pr_source_line = osl; - } - - QCC_FreeTemp(e); - // qcc_functioncalled=false; } @@ -8012,7 +8740,7 @@ void QCC_Marshal_Locals(int firststatement, int laststatement) if (!opt_locals_overlapping) { if (qccwarningaction[WARN_UNINITIALIZED]) - QCC_CheckUninitialised(firststatement, laststatement); + QCC_CheckUninitialised(firststatement, laststatement); //still need to call it for warnings, but if those warnings are off we can skip the cost error = true; //always use the legacy behaviour } else if (QCC_CheckUninitialised(firststatement, laststatement)) @@ -8066,6 +8794,13 @@ void QCC_Marshal_Locals(int firststatement, int laststatement) } else { + if (localsused > locals_marshalled) + { + optres_locals_overlapping += locals_marshalled; + locals_marshalled = localsused; + optres_locals_overlapping -= locals_marshalled; + } + optres_locals_overlapping += localsused; // QCC_PR_Note(ERR_INTERNAL, strings+s_file, pr_source_line, "Overlapping %s", pr_scope->name); } pr.localvars = NULL; @@ -8234,14 +8969,14 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) defs[u] = QCC_PR_GetDef (type->params[u].type, pr_parm_names[u], pr_scope, true, 0, false); defs[u]->references++; - if (u < MAX_PARMS) + /*if (u < MAX_PARMS) { f->parm_ofs[u] = defs[u]->ofs; if (u > 0 && f->parm_ofs[u] < f->parm_ofs[u-1]) QCC_Error (ERR_BADPARAMORDER, "bad parm order"); if (u > 0 && f->parm_ofs[u] != f->parm_ofs[u-1]+defs[u-1]->type->size) QCC_Error (ERR_BADPARAMORDER, "parms not packed"); - } + }*/ } f->code = numstatements; @@ -8304,7 +9039,7 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) // // parse regular statements // - while (!QCC_PR_CheckToken("}")) + while (STRCMP ("}", pr_token)) //not check token to avoid the lex consuming following pragmas { QCC_PR_ParseStatement (); QCC_FreeTemps(); @@ -8363,7 +9098,7 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], e, QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 0, e2->ofs, false), NULL)); }*/ - QCC_PR_Statement (pr_opcodes, 0,0, NULL); + QCC_PR_Statement (&pr_opcodes[OP_DONE], 0,0, NULL); } else optres_return_only++; @@ -8402,6 +9137,8 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) QCC_PR_ParseError(ERR_ILLEGALCASES, "%s: function contains illegal cases", pr_scope->name); } + QCC_PR_Lex(); + return f; } @@ -8486,12 +9223,22 @@ QCC_def_t *QCC_PR_EmitArrayGetVector(QCC_def_t *array) int numslots; + QCC_type_t *ftype = qccHunkAlloc(sizeof(*ftype)); + struct QCC_typeparam_s *fparms = qccHunkAlloc(sizeof(*fparms)*1); + ftype->size = 1; + ftype->type = ev_function; + ftype->aux_type = array->type; + ftype->params = fparms; + ftype->num_parms = 1; + ftype->name = "ArrayGet"; + fparms[0].type = type_float; + //array shouldn't ever be a vector array numslots = array->arraysize*array->type->size; numslots = (numslots+2)/3; s_file = array->s_file; - func = QCC_PR_GetDef(type_function, qcva("ArrayGetVec*%s", array->name), NULL, true, 0, false); + func = QCC_PR_GetDef(ftype, qcva("ArrayGetVec*%s", array->name), NULL, true, 0, false); pr_scope = func; @@ -8618,6 +9365,7 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, char *arrayname) QCC_PR_Statement3(pr_opcodes+OP_STORE_F, index, &def_parms[0], NULL, false); QCC_PR_Statement3(pr_opcodes+OP_CALL1, vectortrick, NULL, NULL, false); vectortrick->references++; + vectortrick->timescalled++; ret = QCC_PR_GetDef(type_vector, "vec__", pr_scope, true, 0, false); ret->references+=4; QCC_PR_Statement3(pr_opcodes+OP_STORE_V, &def_ret, ret, NULL, false); @@ -8820,6 +9568,9 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int a KEYWORD(asm); } + if (!type) + return NULL; + for (a = 0; a < (arraysize?arraysize:1); a++) { if (a == 0) @@ -9479,7 +10230,10 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *def, QCC_type_t *type else if (type->type == ev_integer && tmp->type->type == ev_float) tmp = QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], tmp, 0, NULL); else + { + QCC_PR_ParseErrorPrintDef (ERR_BADIMMEDIATETYPE, def, "wrong initializer type for %s. got %s, needed %s", def->name, tmp->type->name, type->name); + } } } @@ -9487,7 +10241,10 @@ void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *def, QCC_type_t *type if (!pr_scope || def->constant || def->isstatic) { if (!tmp->constant) - QCC_PR_ParseErrorPrintDef (ERR_BADIMMEDIATETYPE, def, "initializer is not constant"); + { + QCC_PR_ParseWarning(WARN_NOTCONSTANT, "initializer is not constant"); + QCC_PR_ParsePrintDef(WARN_NOTCONSTANT, def); + } if (def->initialized && def->initialized != 3) { @@ -10413,14 +11170,14 @@ pbool QCC_PR_CompileFile (char *string, char *filename) { if (setjmp(pr_parse_abort)) { + num_continues = 0; + num_breaks = 0; + num_cases = 0; if (++pr_error_count > MAX_ERRORS) { memcpy(&pr_parse_abort, &oldjb, sizeof(oldjb)); return false; } - num_continues = 0; - num_breaks = 0; - num_cases = 0; QCC_PR_SkipToSemicolon (); if (pr_token_type == tt_eof) { @@ -10468,6 +11225,9 @@ pbool QCC_Include(char *filename) pr_source_line = opr_source_line; pr_file_p = opr_file_p; + if (pr_error_count > MAX_ERRORS) + longjmp (pr_parse_abort, 1); + // QCC_PR_IncludeChunk(newfile, false, fname); return true; diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index ca64a79f2..8af702a87 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -6,16 +6,6 @@ #endif #include "time.h" -#ifdef _WIN64 - #ifdef _SDL - #define snprintf linuxlike_snprintf - int VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...) LIKEPRINTF(3); - #define vsnprintf linuxlike_vsnprintf - int VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr); - //void *__imp__vsnprintf = vsnprintf; - #endif -#endif - #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 @@ -68,13 +58,13 @@ static void Q_strlcpy(char *dest, const char *src, int sizeofdest) char *pr_punctuation[] = // longer symbols must be before a shorter partial match -{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "(+)", "(-)", "|=", "&~=", "++", "--", "->", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "<<", "<", ">>", ">" , "?", "#" , "@", "&" , "|", "^", "~", ":", NULL}; +{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "(+)", "(-)", "|=", "&~=", "&=", "++", "--", "->", "^=", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "<<", "<", ">>", ">" , "?", "#" , "@", "&" , "|", "^", "~", ":", NULL}; char *pr_punctuationremap[] = //a nice bit of evilness. //(+) -> |= //-> -> . //(-) -> &~= -{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "|=", "&~=", "|=", "&~=", "++", "--", ".", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "<<", "<", ">>", ">" , "?", "#" , "@", "&" , "|", "^", "~", ":", NULL}; +{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "|=", "&~=", "|=", "&~=", "&=", "++", "--", ".", "^=", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "<<", "<", ">>", ">" , "?", "#" , "@", "&" , "|", "^", "~", ":", NULL}; // simple types. function types are dynamically allocated QCC_type_t *type_void;// = {ev_void/*, &def_void*/}; @@ -386,12 +376,13 @@ int ParsePrecompilerIf(int level) return eval; } -static void QCC_PR_SkipToEndOfLine(void) +//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 handlecomments = true; + 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] == '*' && handlecomments) + if (*pr_file_p == '/' && pr_file_p[1] == '*' && handleccomments) { pr_file_p += 2; while(*pr_file_p) @@ -406,9 +397,9 @@ static void QCC_PR_SkipToEndOfLine(void) pr_file_p++; } } - else if (*pr_file_p == '/' && pr_file_p[1] == '/' && handlecomments) + else if (*pr_file_p == '/' && pr_file_p[1] == '/' && handleccomments) { - handlecomments = false; + handleccomments = false; pr_file_p += 2; } else if (*pr_file_p == '\\' && pr_file_p[1] == '\r' && pr_file_p[2] == '\n') @@ -416,6 +407,32 @@ static void QCC_PR_SkipToEndOfLine(void) 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; @@ -423,7 +440,15 @@ static void QCC_PR_SkipToEndOfLine(void) 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++; + } } } /* @@ -456,7 +481,7 @@ pbool QCC_PR_Precompiler(void) { pr_file_p = directive; QCC_PR_ConditionCompilation(); - QCC_PR_SkipToEndOfLine(); + QCC_PR_SkipToEndOfLine(true); } else if (!strncmp(directive, "undef", 5)) { @@ -468,7 +493,7 @@ pbool QCC_PR_Precompiler(void) QCC_PR_UndefineName(pr_token); // QCC_PR_ConditionCompilation(); - QCC_PR_SkipToEndOfLine(); + QCC_PR_SkipToEndOfLine(true); } else if (!strncmp(directive, "if", 2)) { @@ -502,11 +527,6 @@ pbool QCC_PR_Precompiler(void) if (ifmode == 2) { eval = ParsePrecompilerIf(PPI_TOPLEVEL); - - if(*pr_file_p != '\r' && *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 { @@ -521,7 +541,7 @@ pbool QCC_PR_Precompiler(void) eval = eval?false:true; } - QCC_PR_SkipToEndOfLine(); + QCC_PR_SkipToEndOfLine(true); level = 1; if (eval) @@ -551,12 +571,13 @@ pbool QCC_PR_Precompiler(void) if (!strncmp(pr_file_p, "else", 4) && level == 1) { ifs+=1; - QCC_PR_SkipToEndOfLine(); + pr_file_p+=4; + QCC_PR_SkipToEndOfLine(true); break; } } - QCC_PR_SkipToEndOfLine(); + QCC_PR_SkipToEndOfLine(false); if (level <= 0) break; @@ -573,7 +594,8 @@ pbool QCC_PR_Precompiler(void) ifs -= 1; level = 1; - QCC_PR_SkipToEndOfLine(); + pr_file_p = directive+4; + QCC_PR_SkipToEndOfLine(true); while (1) { while(*pr_file_p && (*pr_file_p==' ' || *pr_file_p == '\t')) @@ -598,11 +620,13 @@ pbool QCC_PR_Precompiler(void) if (!strncmp(pr_file_p, "else", 4) && level == 1) { ifs+=1; + pr_file_p+=4; + QCC_PR_SkipToEndOfLine(true); break; } } - QCC_PR_SkipToEndOfLine(); + QCC_PR_SkipToEndOfLine(false); if (level <= 0) break; pr_file_p++; //go off the end @@ -611,7 +635,8 @@ pbool QCC_PR_Precompiler(void) } else if (!strncmp(directive, "endif", 5)) { - QCC_PR_SkipToEndOfLine(); + pr_file_p = directive+5; + QCC_PR_SkipToEndOfLine(true); if (ifs <= 0) QCC_PR_ParseError(ERR_NOPRECOMPILERIF, "unmatched #endif"); else @@ -630,7 +655,7 @@ pbool QCC_PR_Precompiler(void) msg[a] = '\0'; - QCC_PR_SkipToEndOfLine(); + QCC_PR_SkipToEndOfLine(true); QCC_PR_ParseError(ERR_HASHERROR, "#Error: %s", msg); } @@ -642,21 +667,23 @@ pbool QCC_PR_Precompiler(void) msg[a-1] = '\0'; - QCC_PR_SkipToEndOfLine(); + QCC_PR_SkipToEndOfLine(true); 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++) + 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'; - QCC_PR_SkipToEndOfLine(); - - printf("#message: %s\n", msg); + if (flag_msvcstyle) + printf ("%s(%i) : #message: %s\n", strings + s_file, pr_source_line, msg); + else + printf ("%s:%i: #message: %s\n", strings + s_file, pr_source_line, msg); + QCC_PR_SkipToEndOfLine(false); } else if (!strncmp(directive, "copyright", 9)) { @@ -666,7 +693,7 @@ pbool QCC_PR_Precompiler(void) msg[a-1] = '\0'; - QCC_PR_SkipToEndOfLine(); + QCC_PR_SkipToEndOfLine(true); if (strlen(msg) >= sizeof(QCC_copyright)) QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n"); @@ -690,10 +717,7 @@ pbool QCC_PR_Precompiler(void) 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_SkipToEndOfLine(true); if (ifmode == 0) QCC_packid = atoi(msg); @@ -715,7 +739,7 @@ pbool QCC_PR_Precompiler(void) msg[a-1] = '\0'; - QCC_PR_SkipToEndOfLine(); + QCC_PR_SkipToEndOfLine(true); } else if (!strncmp(directive, "includelist", 11)) { @@ -743,17 +767,18 @@ pbool QCC_PR_Precompiler(void) continue; } if (!strcmp(pr_token, "#endlist")) + { + QCC_PR_SkipToEndOfLine(true); break; + } QCC_FindBestInclude(pr_token, compilingfile, qccmsourcedir, true); if (*pr_file_p == '\r') pr_file_p++; - QCC_PR_SkipToEndOfLine(); + QCC_PR_SkipToEndOfLine(true); } - - QCC_PR_SkipToEndOfLine(); } else if (!strncmp(directive, "include", 7)) { @@ -792,11 +817,7 @@ pbool QCC_PR_Precompiler(void) pr_file_p++; - while(*pr_file_p != '\n' && *pr_file_p != '\0' && qcc_iswhitesameline(*pr_file_p)) - pr_file_p++; - - - QCC_PR_SkipToEndOfLine(); + QCC_PR_SkipToEndOfLine(true); } else if (!strncmp(directive, "datafile", 8)) { @@ -954,7 +975,7 @@ pbool QCC_PR_Precompiler(void) int o; extern pbool qcc_nopragmaoptimise; if (pr_scope) - QCC_PR_ParseWarning(WARN_BADPRAGMA, "pragma %s: unable to change optimisations mid-function", qcc_token, msg); + QCC_PR_ParseWarning(WARN_BADPRAGMA, "pragma %s: unable to change optimisation options mid-function", qcc_token, msg); else if (qcc_nopragmaoptimise) QCC_PR_ParseWarning(WARN_BADPRAGMA, "pragma %s %s: overriden by commandline", qcc_token, msg); else if (*msg >= '0' && *msg <= '3') @@ -1132,7 +1153,7 @@ pbool QCC_PR_Precompiler(void) } } if (!compiler_flag[f].enabled) - QCC_PR_ParseWarning(WARN_BADPRAGMA, "keyword/flag not recognised"); + QCC_PR_ParseWarning(WARN_BADPRAGMA, "keyword/flag %s not recognised", qcc_token); } } @@ -2295,6 +2316,8 @@ void QCC_PR_ConditionCompilation(void) int dbuflen; char *s; int quote=false; + pbool preprocessorhack = false; + int comment = 0; CompilerConstant_t *cnst; QCC_PR_SimpleGetToken (); @@ -2372,12 +2395,36 @@ void QCC_PR_ConditionCompilation(void) s++; } +/* +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_iswhite(*exploitcheck); exploitcheck++) ; if (*exploitcheck == '#') { - QCC_PR_ParseWarning(WARN_EVILPREPROCESSOR, "preprocessor directive within preprocessor constant %s", cnst->name); + QCC_PR_ParseWarning(WARN_EVILPREPROCESSOR, "preprocessor directive within preprocessor macro %s", cnst->name); *d++ = '\n'; + preprocessorhack = true; + } + else if (preprocessorhack) + { + *d++ = '\n'; + preprocessorhack = false; } } } @@ -2656,6 +2703,14 @@ int QCC_PR_CheckCompConst(void) QCC_PR_IncludeChunkEx(buffer, true, NULL, c); } free(buffer); + + if (flag_debugmacros) + { + if (flag_msvcstyle) + printf ("%s(%i) : macro %s: %s\n", strings+s_file, pr_source_line, c->name, pr_file_p); + else + printf ("%s:%i: macro %s: %s\n", strings+s_file, pr_source_line, c->name, pr_file_p); + } } else QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Macro without argument list"); @@ -2763,6 +2818,40 @@ CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *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, ")", c->name); + } + 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; +} + //============================================================================ /* @@ -3809,8 +3898,9 @@ QCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto) if (e == ptype) { char name[128]; - sprintf(name, "ptr to %s", pointsto->name); - e->name = strdup(name); + QC_snprintfz(name, sizeof(name), "ptr to %s", pointsto->name); + e->name = qccHunkAlloc(strlen(name)+1); + strcpy(e->name, name); } return e; } @@ -3818,13 +3908,17 @@ QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto) { QCC_type_t *ptype; char name[128]; - sprintf(name, "FIELD_TYPE(%s)", pointsto->name); + QC_snprintfz(name, sizeof(name), "FIELD_TYPE(%s)", pointsto->name); ptype = QCC_PR_NewType(name, ev_field, false); ptype->aux_type = pointsto; ptype->size = ptype->aux_type->size; return QCC_PR_FindType (ptype); } +extern char *basictypenames[]; +extern QCC_type_t **basictypes[]; +QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int ofs, int referable, unsigned int flags); + 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 @@ -3901,6 +3995,10 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) char *parmname; int arraysize; pbool redeclaration; + int basicindex; + QCC_def_t *d; + QCC_type_t *pc; + pbool found = false; parmname = QCC_PR_ParseName(); classname = qccHunkAlloc(strlen(parmname)+1); @@ -4149,42 +4247,89 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) } + 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].optional = false; parms[numparms].paramname = parmname; parms[numparms].arraysize = arraysize; parms[numparms].type = newparm; + + 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 (basicindex < pp[i].ofs+1) //if we found one with the index + basicindex = pp[i].ofs+1; //make sure we don't union it. + } + } + } + parms[numparms].ofs = basicindex; //ulp, its new numparms++; - fieldtype = QCC_PR_NewType(parmname, ev_field, false); - fieldtype->aux_type = newparm; - fieldtype->size = newparm->size; - { - QCC_type_t *pc; - QCC_def_t *d; - pc = newt; - d = NULL; - while(pc && !d) + 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) + { //don't go all weird with unioning generic fields + sprintf(membername, "::%s%i", basictypenames[newparm->type], basicindex+1); + d = QCC_PR_GetDef(NULL, membername, NULL, 0, 0, GDF_CONST); + if (!d) { - sprintf(membername, "%s::"MEMBERFIELDNAME, pc->name, parmname); - d = QCC_PR_GetDef (NULL, membername, pr_scope, false, 0, 0); - if (d && typecmp(d->type, fieldtype)) - { - char bufc[256]; - char bufp[256]; - TypeName(((d->type->type==ev_field)?d->type->aux_type:d->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); - break; - } - pc = pc->parentclass; + d = QCC_PR_GetDef(QCC_PR_FieldType(*basictypes[newparm->type]), membername, NULL, 2, 0, GDF_CONST); + for (i = 0; i < newparm->size; i++) + ((int *)qcc_pr_globals)[i+d->ofs] = pr.size_fields + i; + pr.size_fields += i; + + d->references++; //always referenced, so you can inherit safely. } - if (d) - continue; } + + //and make sure we can do member::__fname + //actually, that seems pointless. sprintf(membername, "%s::"MEMBERFIELDNAME, classname, parmname); - QCC_PR_GetDef(fieldtype, membername, pr_scope, 2, 0, 0); +// printf("define %s -> %s\n", membername, d->name); + d = QCC_PR_DummyDef(fieldtype, membername, pr_scope, 0, d->ofs, true, GDF_CONST); + d->references++; //always referenced, so you can inherit safely. } if (redeclaration) diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index 180e0185c..2264e5842 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -9,6 +9,9 @@ #include "qcc.h" #include "gui.h" +//#define EMBEDDEBUG + +void AddSourceFile(char *format, ...); #ifndef TVM_SETBKCOLOR #define TVM_SETBKCOLOR (TV_FIRST + 29) @@ -66,13 +69,72 @@ int PDECL QCC_FileSize (const char *fname) length = ftell(f); fclose(f); + if (strcmp(progssrcname, fname)) + AddSourceFile("%s/%s", progssrcname, fname); + return length; } +#ifdef AVAIL_ZLIB +#include "../libs/zlib.h" +#endif + pbool PDECL QCC_WriteFile (const char *name, void *data, int len) { long length; FILE *f; + + char *ext = strrchr(name, '.'); + if (!stricmp(ext, ".gz")) + { +#ifdef AVAIL_ZLIB + pbool okay = true; + char out[1024*8]; + + z_stream strm = { + data, + len, + 0, + + out, + sizeof(out), + 0, + + NULL, + NULL, + + NULL, + NULL, + NULL, + + Z_BINARY, + 0, + 0 + }; + + f = fopen(name, "wb"); + if (!f) + return false; + deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS|16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + while(okay && deflate(&strm, Z_FINISH) == Z_OK) + { + if (sizeof(out) - strm.avail_out != fwrite(out, 1, sizeof(out) - strm.avail_out, f)) + okay = false; + strm.next_out = out; + strm.avail_out = sizeof(out); + } + if (sizeof(out) - strm.avail_out != fwrite(out, 1, sizeof(out) - strm.avail_out, f)) + okay = false; + deflateEnd(&strm); + fclose(f); + if (!okay) + unlink(name); + return okay; +#else + return false; +#endif + } + f = fopen(name, "wb"); if (!f) return false; @@ -139,6 +201,7 @@ int logprintf(const char *format, ...) #define MDI_WINDOW_CLASS_NAME "FTEMDIWINDOW" #define EDIT_WINDOW_CLASS_NAME "FTEEDITWINDOW" #define OPTIONS_WINDOW_CLASS_NAME "FTEOPTIONSWINDOW" +#define ENGINE_WINDOW_CLASS_NAME "FTEEMBEDDEDWINDOW" #define EM_GETSCROLLPOS (WM_USER + 221) #define EM_SETSCROLLPOS (WM_USER + 222) @@ -150,8 +213,12 @@ void GUIPrint(HWND wnd, char *msg); char finddef[256]; char greptext[256]; +char enginebinary[MAX_PATH] = "fteglqw.exe"; +char enginebasedir[MAX_PATH] = "../.."; +char enginecommandline[8192] = "-game test -window +map start"; void RunCompiler(char *args); +void RunEngine(void); HINSTANCE ghInstance; HMODULE richedit; @@ -160,6 +227,7 @@ pbool resetprogssrc; //progs.src was changed, reload project info. HWND mainwindow; +HWND gamewindow; HWND mdibox; HWND outputwindow; HWND outputbox; @@ -178,15 +246,24 @@ struct{ int washit; } buttons[] = { {"Compile"}, - {"Edit"}, + {"Progs.src"}, +#ifdef EMBEDDEBUG + {"Run"}, +#endif {"Options"}, {"Quit"} }; -#define ID_COMPILE 0 -#define ID_EDIT 1 -#define ID_OPTIONS 2 -#define ID_QUIT 3 +enum +{ + ID_COMPILE = 0, + ID_EDIT, +#ifdef EMBEDDEBUG + ID_RUN, +#endif + ID_OPTIONS, + ID_QUIT +}; #define NUMBUTTONS sizeof(buttons)/sizeof(buttons[0]) @@ -197,6 +274,28 @@ void GUI_DialogPrint(char *title, char *text) MessageBox(mainwindow, text, title, 0); } +//available in xp+ +typedef LRESULT (CALLBACK *SUBCLASSPROC)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); +BOOL (WINAPI * pSetWindowSubclass)(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); +LRESULT (WINAPI *pDefSubclassProc)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK MySubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) +{ + if (uMsg == WM_KEYDOWN) + { + if (wParam == VK_F3) + { + SetFocus(search_name); + return 0; + } + if (wParam == VK_F5) + { + RunEngine(); + return 0; + } + } + return pDefSubclassProc(hWnd, uMsg, wParam, lParam); +} + HWND CreateAnEditControl(HWND parent) { HWND newc; @@ -263,6 +362,18 @@ HWND CreateAnEditControl(HWND parent) SendMessage(newc, EM_EXLIMITTEXT, 0, 1<<20); } + if (!pDefSubclassProc || !pSetWindowSubclass) + { + HMODULE lib = LoadLibrary("comctl32.dll"); + if (lib) + { + pDefSubclassProc = (void*)GetProcAddress(lib, "DefSubclassProc"); + pSetWindowSubclass = (void*)GetProcAddress(lib, "SetWindowSubclass"); + } + } + if (pDefSubclassProc && pSetWindowSubclass) + pSetWindowSubclass(newc, MySubclassWndProc, 0, (DWORD_PTR)parent); + ShowWindow(newc, SW_SHOW); return newc; @@ -301,6 +412,9 @@ enum { IDI_O_APPLY, IDI_O_TARGET, IDI_O_SYNTAX_HIGHLIGHTING, + IDI_O_ENGINE, + IDI_O_ENGINEBASEDIR, + IDI_O_ENGINECOMMANDLINE, IDM_FIRSTCHILD }; @@ -485,6 +599,7 @@ char *WordUnderCursor(editor_t *editor, char *buffer, int buffersize) } char *GetTooltipText(editor_t *editor) { + static char buffer[1024]; char wordbuf[256]; char *defname; defname = WordUnderCursor(editor, wordbuf, sizeof(wordbuf)); @@ -493,10 +608,13 @@ char *GetTooltipText(editor_t *editor) else if (globalstable.numbuckets) { QCC_def_t *def; + char *macro = QCC_PR_CheckCompConstTooltip(defname, buffer, buffer + sizeof(buffer)); + if (*macro) + return macro; + def = QCC_PR_GetDef(NULL, defname, NULL, false, 0, false); if (def) { - static char buffer[1024]; char typebuf[1024]; //note function argument names do not persist beyond the function def. we might be able to read the function's localdefs for them, but that's unreliable/broken with builtins where they're most needed. if (def->comment) @@ -510,7 +628,7 @@ char *GetTooltipText(editor_t *editor) else return NULL;//"Type info not available. Compile first."; } -static LONG CALLBACK EditorWndProc(HWND hWnd,UINT message, +static LRESULT CALLBACK EditorWndProc(HWND hWnd,UINT message, WPARAM wParam,LPARAM lParam) { RECT rect; @@ -1136,7 +1254,7 @@ void EditFile(char *name, int line) wndclass.style = 0; - wndclass.lpfnWndProc = (WNDPROC)EditorWndProc; + wndclass.lpfnWndProc = EditorWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = ghInstance; @@ -1280,12 +1398,240 @@ pbool EditorModified(editor_t *e) +//the engine thread simply sits waiting for responses from the engine +typedef struct +{ + int pipeclosed; + DWORD tid; + HWND window; + HANDLE thread; + HANDLE pipefromengine; + HANDLE pipetoengine; +} enginewindow_t; +void EngineCommand(enginewindow_t *ctx, char *message, ...) +{ + //qcresume - resume running + //qcinto - singlestep. execute-with-debugging child functions + //qcover - singlestep. execute-without-debugging child functions + //qcout - singlestep. leave current function and enter parent. + //qcbreak "$loc" - set breakpoint + //qcwatch "$var" - set watchpoint + //qcstack - force-report stack trace + va_list va; + char finalmessage[1024]; + va_start (va, message); + vsnprintf (finalmessage, sizeof(finalmessage)-1, message, va); + va_end (va); + if (ctx->pipetoengine) + { + DWORD written = 0; + WriteFile(ctx->pipetoengine, finalmessage, strlen(finalmessage), &written, NULL); + } +} +unsigned int WINAPI threadwrapper(void *args) +{ + enginewindow_t *ctx = args; + { + PROCESS_INFORMATION childinfo; + STARTUPINFO startinfo; + SECURITY_ATTRIBUTES pipesec = {sizeof(pipesec), NULL, TRUE}; + char cmdline[8192]; + _snprintf(cmdline, sizeof(cmdline), "\"%s\" %s -plugin qcdebug", enginebinary, enginecommandline); + memset(&startinfo, 0, sizeof(startinfo)); + startinfo.cb = sizeof(startinfo); + startinfo.hStdInput = NULL; + startinfo.hStdError = NULL; + startinfo.hStdOutput = NULL; + startinfo.dwFlags |= STARTF_USESTDHANDLES; + //create pipes for the stdin/stdout. + CreatePipe(&ctx->pipefromengine, &startinfo.hStdOutput, &pipesec, 0); + CreatePipe(&startinfo.hStdInput, &ctx->pipetoengine, &pipesec, 0); + SetHandleInformation(ctx->pipefromengine, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation(ctx->pipetoengine, HANDLE_FLAG_INHERIT, 0); + EngineCommand(ctx, "vid_recenter %i %i %i %i %#p\n", 0, 0, 640, 480, (void*)ctx->window); + + CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, enginebasedir, &startinfo, &childinfo); + + //these ends of the pipes were inherited by now, so we can discard them in the caller. + CloseHandle(startinfo.hStdOutput); + CloseHandle(startinfo.hStdInput); + } + + { + char buffer[8192]; + unsigned int bufoffs = 0; + char *nl; + while(1) + { + DWORD avail; + //use Peek so we can read exactly how much there is without blocking, so we don't have to read byte-by-byte. + PeekNamedPipe(ctx->pipefromengine, NULL, 0, NULL, &avail, NULL); + if (!avail) + avail = 1; //so we do actually sleep. + if (avail > sizeof(buffer)-1 - bufoffs) + avail = sizeof(buffer)-1 - bufoffs; + if (!ReadFile(ctx->pipefromengine, buffer + bufoffs, avail, &avail, NULL) || !avail) + { + break; + } + + bufoffs += avail; + while(1) + { + buffer[bufoffs] = 0; + nl = strchr(buffer, '\n'); + if (nl) + { + *nl = 0; + if (!strncmp(buffer, "status ", 7)) + { + //SetWindowText(ctx->window, buffer+7); + } + else if (!strcmp(buffer, "status")) + { + //SetWindowText(ctx->window, "Engine"); + } + else if (!strcmp(buffer, "curserver")) + { + //not interesting + } + else if (!strncmp(buffer, "qcstack ", 6)) + { + //qcvm is giving a stack trace + //stack reset + //stack "$func" "$loc" + //local $depth + } + else if (!strncmp(buffer, "qcstep ", 5)) + { + //qcvm stepped to the line specified, often dupes + //qcstep "$location" + EngineCommand(ctx, "qcstep\n"); //moves on to the next statement + } + else if (!strncmp(buffer, "qcvalue ", 6)) + { + //qcvalue "$variableformula" "$value" + } + else if (!strncmp(buffer, "qcreloaded ", 6)) + { + //so we can resend any breakpoint commands + //qcreloaded "$vmname" "$progsname" + EngineCommand(ctx, "qcresume\n"); + } + else + { + //handle anything else we need to handle here + printf("Unknown command from engine \"%s\"\n", buffer); + } + nl++; + bufoffs -= (nl-buffer); + memmove(buffer, nl, bufoffs); + } + else + break; + } + + } + } + + ctx->pipeclosed = true; + PostMessage(ctx->window, WM_CLOSE, 0, 0); //and tell the owning window to try to close it again + return 0; +} + +static LRESULT CALLBACK EngineWndProc(HWND hWnd,UINT message, + WPARAM wParam,LPARAM lParam) +{ + enginewindow_t *e; + switch (message) + { + case WM_CREATE: + e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)e); + e->window = hWnd; + e->thread = (HANDLE)CreateThread(NULL, 0, threadwrapper, e, 0, &e->tid); + break; + case WM_SIZE: + { + RECT r; + e = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(hWnd, GWLP_USERDATA); + GetClientRect(hWnd, &r); + EngineCommand(e, "vid_recenter %i %i %i %i %#p\n", r.left, r.top, r.right-r.left, r.bottom - r.top, (void*)e->window); + } + goto gdefault; + case WM_CLOSE: + e = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(hWnd, GWLP_USERDATA); + if (e->pipeclosed) + { + DestroyWindow(hWnd); + return 0; + } + //ask the engine to quit + EngineCommand(e, "quit force\n"); + break; + case WM_DESTROY: + e = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(hWnd, GWLP_USERDATA); + EngineCommand(e, "quit force\n"); //just in case + WaitForSingleObject(e->thread, INFINITE); + CloseHandle(e->thread); + free(e); + if (hWnd == gamewindow) + gamewindow = NULL; + break; + + default: + gdefault: + return DefMDIChildProc(hWnd,message,wParam,lParam); + } + return 0; +} +void RunEngine(void) +{ + if (!gamewindow) + { + WNDCLASS wndclass; + MDICREATESTRUCT mcs; + + memset(&wndclass, 0, sizeof(wndclass)); + wndclass.style = 0; + wndclass.lpfnWndProc = EngineWndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = ghInstance; + wndclass.hIcon = 0; + wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); + wndclass.hbrBackground = (void *)COLOR_WINDOW; + wndclass.lpszMenuName = 0; + wndclass.lpszClassName = ENGINE_WINDOW_CLASS_NAME; + RegisterClass(&wndclass); + + memset(&mcs, 0, sizeof(mcs)); + mcs.szClass = ENGINE_WINDOW_CLASS_NAME; + mcs.szTitle = "Debug"; + mcs.hOwner = ghInstance; + mcs.x = CW_USEDEFAULT; + mcs.y = CW_USEDEFAULT; + mcs.cx = 640; + mcs.cy = 480; + mcs.style = WS_OVERLAPPEDWINDOW; + mcs.lParam = 0; + + gamewindow = (HWND) SendMessage (mdibox, WM_MDICREATE, 0, (LONG_PTR) (LPMDICREATESTRUCT) &mcs); + ShowWindow(gamewindow, SW_SHOW); + } + else + { + enginewindow_t *e = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(gamewindow, GWLP_USERDATA); + } + SendMessage(mdibox, WM_MDIACTIVATE, (WPARAM)gamewindow, 0); +} @@ -1295,7 +1641,12 @@ HWND nokeywords_coexistitem; HWND autoprototype_item; HWND autohighlight_item; HWND extraparmsitem; -static LONG CALLBACK OptionsWndProc(HWND hWnd,UINT message, +#ifdef EMBEDDEBUG +HWND w_enginebinary; +HWND w_enginebasedir; +HWND w_enginecommandline; +#endif +static LRESULT CALLBACK OptionsWndProc(HWND hWnd,UINT message, WPARAM wParam,LPARAM lParam) { int i; @@ -1332,6 +1683,11 @@ static LONG CALLBACK OptionsWndProc(HWND hWnd,UINT message, } fl_autohighlight = Button_GetCheck(autohighlight_item); Edit_GetText(extraparmsitem, parameters, sizeof(parameters)-1); +#ifdef EMBEDDEBUG + Edit_GetText(w_enginebinary, enginebinary, sizeof(enginebinary)-1); + Edit_GetText(w_enginebasedir, enginebasedir, sizeof(enginebasedir)-1); + Edit_GetText(w_enginecommandline, enginecommandline, sizeof(enginecommandline)-1); +#endif if (wParam == IDI_O_USE) buttons[ID_COMPILE].washit = true; @@ -1531,7 +1887,7 @@ void OptionsDialog(void) memset(&wndclass, 0, sizeof(wndclass)); wndclass.style = 0; - wndclass.lpfnWndProc = (WNDPROC)OptionsWndProc; + wndclass.lpfnWndProc = OptionsWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = ghInstance; @@ -1589,7 +1945,7 @@ void OptionsDialog(void) r.left, r.top, r.right-r.left, r.bottom-r.top, NULL, NULL, ghInstance, NULL); subsection = CreateWindow("BUTTON", "Optimisations", WS_CHILD|WS_VISIBLE|BS_GROUPBOX, - 0, 0, 400, height-48, optionsmenu, NULL, ghInstance, NULL); + 0, 0, 400, height-40*4-8, optionsmenu, NULL, ghInstance, NULL); num = 0; for (i = 0; optimisations[i].enabled; i++) @@ -1621,46 +1977,77 @@ void OptionsDialog(void) CreateWindow("BUTTON","O0", WS_CHILD | WS_VISIBLE, - 8,height-88,64,32, + 8,height-40*5-8,64,32, optionsmenu, (HMENU)IDI_O_LEVEL0, ghInstance, NULL); CreateWindow("BUTTON","O1", WS_CHILD | WS_VISIBLE, - 8+64,height-88,64,32, + 8+64,height-40*5-8,64,32, optionsmenu, (HMENU)IDI_O_LEVEL1, ghInstance, NULL); CreateWindow("BUTTON","O2", WS_CHILD | WS_VISIBLE, - 8+64*2,height-88,64,32, + 8+64*2,height-40*5-8,64,32, optionsmenu, (HMENU)IDI_O_LEVEL2, ghInstance, NULL); CreateWindow("BUTTON","O3", WS_CHILD | WS_VISIBLE, - 8+64*3,height-88,64,32, + 8+64*3,height-40*5-8,64,32, optionsmenu, (HMENU)IDI_O_LEVEL3, ghInstance, NULL); CreateWindow("BUTTON","Debug", WS_CHILD | WS_VISIBLE, - 8+64*4,height-88,64,32, + 8+64*4,height-40*5-8,64,32, optionsmenu, (HMENU)IDI_O_DEBUG, ghInstance, NULL); CreateWindow("BUTTON","Default", WS_CHILD | WS_VISIBLE, - 8+64*5,height-88,64,32, + 8+64*5,height-40*5-8,64,32, optionsmenu, (HMENU)IDI_O_DEFAULT, ghInstance, NULL); + +#ifdef EMBEDDEBUG + w_enginebinary = CreateWindowEx(WS_EX_CLIENTEDGE, + "EDIT", + enginebinary, + WS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL, + 8, height-40-30*3, 400-16, 22, + optionsmenu, + (HMENU)IDI_O_ENGINE, + ghInstance, + NULL); + w_enginebasedir = CreateWindowEx(WS_EX_CLIENTEDGE, + "EDIT", + enginebasedir, + WS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL, + 8, height-40-30*2, 400-16, 22, + optionsmenu, + (HMENU)IDI_O_ENGINEBASEDIR, + ghInstance, + NULL); + w_enginecommandline = CreateWindowEx(WS_EX_CLIENTEDGE, + "EDIT", + enginecommandline, + WS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL, + 8, height-40-30, 400-16, 22, + optionsmenu, + (HMENU)IDI_O_ENGINECOMMANDLINE, + ghInstance, + NULL); +#endif + CreateWindow("BUTTON","Apply", WS_CHILD | WS_VISIBLE, 8,height-40,64,32, @@ -1780,7 +2167,7 @@ void OptionsDialog(void) -static LONG CALLBACK MainWndProc(HWND hWnd,UINT message, +static LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message, WPARAM wParam,LPARAM lParam) { int width; @@ -1854,7 +2241,7 @@ static LONG CALLBACK MainWndProc(HWND hWnd,UINT message, } break; case WM_CTLCOLORBTN: - return GetSysColorBrush(COLOR_HIGHLIGHT);//COLOR_BACKGROUND; + return (LRESULT)GetSysColorBrush(COLOR_HIGHLIGHT);//COLOR_BACKGROUND; case WM_DESTROY: mainwindow = NULL; break; @@ -1879,6 +2266,7 @@ static LONG CALLBACK MainWndProc(HWND hWnd,UINT message, SetWindowPos(buttons[i].hwnd, NULL, width*i, rect.bottom-rect.top - 32, width, 32, 0); } break; +// goto gdefault; case WM_PAINT: hdc=BeginPaint(hWnd,(LPPAINTSTRUCT)&ps); @@ -1920,7 +2308,7 @@ static LONG CALLBACK MainWndProc(HWND hWnd,UINT message, GenericMenu(wParam); break; } - break; + goto gdefault; case WM_NOTIFY: if (lParam) { @@ -1966,6 +2354,7 @@ static LONG CALLBACK MainWndProc(HWND hWnd,UINT message, } } default: + gdefault: if (mdibox) return DefFrameProc(hWnd,mdibox,message,wParam,lParam); else @@ -1974,7 +2363,7 @@ static LONG CALLBACK MainWndProc(HWND hWnd,UINT message, return 0; } -static LONG CALLBACK OutputWindowProc(HWND hWnd,UINT message, +static LRESULT CALLBACK OutputWindowProc(HWND hWnd,UINT message, WPARAM wParam,LPARAM lParam) { RECT rect; @@ -2074,6 +2463,11 @@ int GUIprintf(const char *msg, ...) va_start (argptr,msg); args = QC_vsnprintf (buf,sizeof(buf)-1, msg,argptr); va_end (argptr); + buf[sizeof(buf)-5] = '.'; + buf[sizeof(buf)-4] = '.'; + buf[sizeof(buf)-3] = '.'; + buf[sizeof(buf)-2] = '\n'; + buf[sizeof(buf)-1] = 0; printf("%s", buf); if (logfile) @@ -2225,7 +2619,14 @@ void RunCompiler(char *args) argc = GUI_BuildParms(args, argv); - CompileParams(&funcs, true, argc, argv); + if (CompileParams(&funcs, true, argc, argv)) + { + if (gamewindow) + { + enginewindow_t *e = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(gamewindow, GWLP_USERDATA); + EngineCommand(e, "restart\n"); + } + } if (logfile) fclose(logfile); @@ -2243,7 +2644,7 @@ void CreateOutputWindow(void) if (!outputwindow) { wndclass.style = 0; - wndclass.lpfnWndProc = (WNDPROC)OutputWindowProc; + wndclass.lpfnWndProc = OutputWindowProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = ghInstance; @@ -2264,8 +2665,7 @@ void CreateOutputWindow(void) mcs.style = WS_OVERLAPPEDWINDOW; mcs.lParam = 0; - outputwindow = (HWND) SendMessage (mdibox, WM_MDICREATE, 0, - (LONG_PTR) (LPMDICREATESTRUCT) &mcs); + outputwindow = (HWND) SendMessage (mdibox, WM_MDICREATE, 0, (LONG_PTR) (LPMDICREATESTRUCT) &mcs); ShowWindow(outputwindow, SW_SHOW); } @@ -2355,9 +2755,11 @@ void AddSourceFile(char *format, ...) item.item.state = TVIS_EXPANDED; item.item.stateMask = TVIS_EXPANDED; item.item.mask = TVIF_TEXT|TVIF_STATE; - while(slash = strchr(item.item.pszText, '/')) + while(item.item.pszText) { - *slash = '\0'; + slash = strchr(item.item.pszText, '/'); + if (slash) + *slash++ = '\0'; item.hParent = TreeView_GetChild(projecttree, item.hParent); do { @@ -2379,9 +2781,8 @@ void AddSourceFile(char *format, ...) } else pi = item.hParent; - item.item.pszText = slash+1; + item.item.pszText = slash; } - SendMessage(projecttree,TVM_INSERTITEM,0,(LPARAM)&item); } //progssrcname should already have been set. @@ -2418,6 +2819,7 @@ void SetProgsSrc(void) pr_file_p = QCC_COM_Parse(buffer); if (*qcc_token == '#') { + AddSourceFile("%s", progssrcname); free(buffer); //aaaahhh! newstyle! return; } @@ -2528,7 +2930,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin resetprogssrc = true; wndclass.style = 0; - wndclass.lpfnWndProc = (WNDPROC)MainWndProc; + wndclass.lpfnWndProc = MainWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = ghInstance; @@ -2579,7 +2981,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin WS_CHILD | WS_VISIBLE, 0, 0, 5, 5, mainwindow, - (HMENU)(i+1), + (HMENU)(LONG_PTR)(i+1), ghInstance, NULL); } @@ -2698,6 +3100,13 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin buttons[ID_EDIT].washit = false; EditFile(progssrcname, -1); } +#ifdef EMBEDDEBUG + if (buttons[ID_RUN].washit) + { + buttons[ID_RUN].washit = false; + RunEngine(); + } +#endif if (buttons[ID_OPTIONS].washit) { buttons[ID_OPTIONS].washit = false; diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index d6cfc4e88..c6b4a1084 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -20,7 +20,7 @@ extern int optres_test2; int writeasm; pbool verbose; pbool qcc_nopragmaoptimise; - +extern unsigned int locals_marshalled; pbool QCC_PR_SimpleGetToken (void); void QCC_PR_LexWhitespace (void); @@ -220,8 +220,8 @@ optimisations_t optimisations[] = //level 0 = no optimisations //level 1 = size optimisations //level 2 = speed optimisations - //level 3 = dodgy optimisations. - //level 4 = experimental... must be used explicitly + //level 3 = unsafe optimisations (they break multiprogs). + //level 4 = experimental or extreeme features... must be used explicitly {&opt_assignments, "t", 1, FLAG_ASDEFAULT, "assignments", "c = a*b is performed in one operation rather than two, and can cause older decompilers to fail."}, {&opt_shortenifnots, "i", 1, FLAG_ASDEFAULT, "shortenifs", "if (!a) was traditionally compiled in two statements. This optimisation does it in one, but can cause some decompilers to get confused."}, @@ -229,8 +229,8 @@ optimisations_t optimisations[] = {&opt_constant_names, "c", 2, FLAG_KILLSDEBUGGERS, "constant_names", "This optimisation strips out the names of constants (but not strings) from your progs, resulting in smaller files. It makes decompilers leave out names or fabricate numerical ones."}, {&opt_constant_names_strings, "cs", 3, FLAG_KILLSDEBUGGERS, "constant_names_strings", "This optimisation strips out the names of string constants from your progs. However, this can break addons, so don't use it in those cases."}, {&opt_dupconstdefs, "d", 1, FLAG_ASDEFAULT, "dupconstdefs", "This will merge definitions of constants which are the same value. Pay extra attention to assignment to constant warnings."}, - {&opt_noduplicatestrings, "s", 1, 0, "noduplicatestrings", "This will compact the string table that is stored in the progs. It will be considerably smaller with this."}, - {&opt_locals, "l", 1, FLAG_KILLSDEBUGGERS, "locals", "Strips out local names and definitions. This makes it REALLY hard to decompile"}, + {&opt_noduplicatestrings, "s", 1, FLAG_ASDEFAULT, "noduplicatestrings", "This will compact the string table that is stored in the progs. It will be considerably smaller with this."}, + {&opt_locals, "l", 1, FLAG_KILLSDEBUGGERS, "locals", "Strips out local names and definitions. Most decompiles will break on this."}, {&opt_function_names, "n", 1, FLAG_KILLSDEBUGGERS, "function_names", "This strips out the names of functions which are never called. Doesn't make much of an impact though."}, {&opt_filenames, "f", 1, FLAG_KILLSDEBUGGERS, "filenames", "This strips out the filenames of the progs. This can confuse the really old decompilers, but is nothing to the more recent ones."}, {&opt_unreferenced, "u", 1, FLAG_ASDEFAULT, "unreferenced", "Removes the entries of unreferenced variables. Doesn't make a difference in well maintained code."}, @@ -240,9 +240,9 @@ optimisations_t optimisations[] = {&opt_return_only, "ro", 3, FLAG_KILLSDEBUGGERS, "return_only", "Functions ending in a return statement do not need a done statement at the end of the function. This can confuse some decompilers, making functions appear larger than they were."}, {&opt_compound_jumps, "cj", 3, FLAG_KILLSDEBUGGERS, "compound_jumps", "This optimisation plays an effect mostly with nested if/else statements, instead of jumping to an unconditional jump statement, it'll jump to the final destination instead. This will bewilder decompilers."}, // {&opt_comexprremoval, "cer", 4, 0, "expression_removal", "Eliminate common sub-expressions"}, //this would be too hard... - {&opt_stripfunctions, "sf", 4, 0, "strip_functions", "Strips out the 'defs' of functions that were only ever called directly. This does not affect saved games. This can affect FTE_MULTIPROGS."}, - {&opt_locals_overlapping, "lo", 3, FLAG_KILLSDEBUGGERS, "locals_overlapping", "Store all locals in a single section of the pr_globals. Vastly reducing it. This effectivly does the job of overlaptemps.\nHowever, locals are no longer automatically initialised to 0 (and never were in the case of recursion, but at least then its the same type).\nIf locals appear uninitialised, fteqcc will disable this optimisation for the affected functions, you can optionally get a warning about these locals using: #pragma warning enable F302"}, - {&opt_vectorcalls, "vc", 4, FLAG_KILLSDEBUGGERS, "vectorcalls", "Where a function is called with just a vector, this causes the function call to store three floats instead of one vector. This can save a good number of pr_globals where those vectors contain many duplicate coordinates but do not match entirly."}, + {&opt_stripfunctions, "sf", 3, FLAG_KILLSDEBUGGERS, "strip_functions", "Strips out the 'defs' of functions that were only ever called directly. This does not affect saved games. This can affect FTE_MULTIPROGS."}, + {&opt_locals_overlapping, "lo", 2, FLAG_ASDEFAULT|FLAG_KILLSDEBUGGERS, "locals_overlapping", "Store all locals in a single section of the pr_globals. Vastly reducing it. This effectivly does the job of overlaptemps.\nHowever, locals are no longer automatically initialised to 0 (and never were in the case of recursion, but at least then its the same type).\nIf locals appear uninitialised, fteqcc will disable this optimisation for the affected functions, you can optionally get a warning about these locals using: #pragma warning enable F302"}, + {&opt_vectorcalls, "vc", 4, FLAG_KILLSDEBUGGERS, "vectorcalls", "Where a function is called with just a vector, this causes the function call to store three floats instead of one vector. This can save a good number of pr_globals where those vectors contain many duplicate coordinates but do not match entirly."}, {NULL} }; @@ -297,12 +297,14 @@ compiler_flag_t compiler_flag[] = { {&flag_hashonly, FLAG_MIDCOMPILE,"hashonly", "Hash-only constants", "Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile"}, {&opt_logicops, FLAG_MIDCOMPILE,"lo", "Logic ops", "This changes the behaviour of your code. It generates additional if operations to early-out in if statements. With this flag, the line if (0 && somefunction()) will never call the function. It can thus be considered an optimisation. However, due to the change of behaviour, it is not considered so by fteqcc. Note that due to inprecisions with floats, this flag can cause runaway loop errors within the player walk and run functions (without iffloat also enabled). This code is advised:\nplayer_stand1:\n if (self.velocity_x || self.velocity_y)\nplayer_run\n if (!(self.velocity_x || self.velocity_y))"}, {&flag_msvcstyle, FLAG_MIDCOMPILE,"msvcstyle", "MSVC-style errors", "Generates warning and error messages in a format that msvc understands, to facilitate ide integration."}, + {&flag_debugmacros, FLAG_MIDCOMPILE,"flag_debugmacros", "Verbose Macro Expansion", "Print out the contents of macros that are expanded. This can help look inside macros that are expanded and is especially handy if people are using preprocessor hacks."}, {&flag_filetimes, 0, "filetimes", "Check Filetimes", "Recompiles the progs only if the file times are modified."}, {&flag_fasttrackarrays, FLAG_MIDCOMPILE|FLAG_ASDEFAULT,"fastarrays","fast arrays where possible", "Generates extra instructions inside array handling functions to detect engine and use extension opcodes only in supporting engines.\nAdds a global which is set by the engine if the engine supports the extra opcodes. Note that this applies to all arrays or none."}, {&flag_assume_integer, FLAG_MIDCOMPILE,"assumeint", "Assume Integers", "Numerical constants are assumed to be integers, instead of floats."}, {&pr_subscopedlocals, FLAG_MIDCOMPILE,"subscope", "Subscoped Locals", "Restrict the scope of locals to the block they are actually defined within, as in C."}, {&verbose, FLAG_MIDCOMPILE,"verbose", "Verbose", "Lots of extra compiler messages."}, {&flag_typeexplicit, FLAG_MIDCOMPILE,"typeexplicit", "Explicit types", "All type conversions must be explicit or directly supported by instruction set."}, + {&flag_noboundchecks, FLAG_MIDCOMPILE,"noboundchecks","Disable Bound Checks", "Disable array index checks, speeding up array access but can result in your code misbehaving."}, {NULL} }; @@ -632,9 +634,10 @@ void QCC_UnmarshalLocals(void) unsigned int maxo; int i; extern int tempsused; + QCC_def_t *largestfunc = NULL; ofs = numpr_globals; - maxo = ofs; + maxo = ofs+locals_marshalled; for (def = pr.def_head.next ; def ; def = def->next) { @@ -659,7 +662,8 @@ void QCC_UnmarshalLocals(void) if (numpr_globals > MAX_REGS) QCC_Error(ERR_TOOMANYGLOBALS, "Too many globals are in use to unmarshal all locals"); - printf("Total of %i locals, %i temps\n", maxo-ofs, tempsused); + if (verbose) + printf("Total of %i locals, %i temps\n", maxo-ofs, tempsused); } CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def); @@ -844,12 +848,15 @@ pbool QCC_WriteData (int crc) { int wt = def->constant?WARN_NOTREFERENCEDCONST:WARN_NOTREFERENCED; pr_scope = def->scope; - if (QCC_PR_Warning(wt, strings + def->s_file, def->s_line, "%s no references.", def->name)) + if (strcmp(def->name, "IMMEDIATE")) { - if (!warnedunref) + if (QCC_PR_Warning(wt, strings + def->s_file, def->s_line, "%s no references.", def->name)) { - QCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, "You can use the noref prefix or pragma to silence this message."); - warnedunref = true; + if (!warnedunref) + { + QCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, "You can use the noref prefix or pragma to silence this message."); + warnedunref = true; + } } } pr_scope = NULL; @@ -885,7 +892,7 @@ pbool QCC_WriteData (int crc) // numfunctions++; } - else if (def->type->type == ev_field && def->constant && (!def->scope || def->isstatic)) + else if (def->type->type == ev_field && def->constant && (!def->scope || def->isstatic || def->initialized)) { if (numfielddefs >= MAX_FIELDS) QCC_PR_ParseError(0, "Too many fields. Limit is %u\n", MAX_FIELDS); @@ -1447,31 +1454,57 @@ strofs = (strofs+3)&~3; SafeSeek (h, 0, SEEK_SET); SafeWrite (h, &progs, sizeof(progs)); - SafeClose (h); + if (!SafeClose (h)) + { + printf("Error while writing output %s\n", destfile); + return false; + } + + printf("Compile finished: %s\n", destfile); if (!debugtarget) { if (opt_filenames) { - printf("Not writing linenumbers file due to conflicting optimisation\n"); + printf("Not writing linenumbers file due to conflicting optimisation (try -Ono-f)\n"); } else { unsigned int lnotype = *(unsigned int*)"LNOF"; unsigned int version = 1; - StripExtension(destfile); - strcat(destfile, ".lno"); - if (verbose) - printf("Writing %s for debugging\n", destfile); - h = SafeOpenWrite (destfile, 2*1024*1024); - SafeWrite (h, &lnotype, sizeof(int)); - SafeWrite (h, &version, sizeof(int)); - SafeWrite (h, &numglobaldefs, sizeof(int)); - SafeWrite (h, &numpr_globals, sizeof(int)); - SafeWrite (h, &numfielddefs, sizeof(int)); - SafeWrite (h, &numstatements, sizeof(int)); - SafeWrite (h, statement_linenums, numstatements*sizeof(int)); - SafeClose (h); + pbool gz = false; + while(1) + { + char *ext; + ext = strrchr(destfile, '.'); + if (strchr(ext, '/') || strchr(ext, '\\')) + break; + if (!stricmp(ext, ".gz")) + { + *ext = 0; + gz = true; + continue; + } + *ext = 0; + break; + } + if (strlen(destfile) < sizeof(destfile)-(4+3)) + { + strcat(destfile, ".lno"); + if (gz) + strcat(destfile, ".gz"); + if (verbose) + printf("Writing %s for debugging\n", destfile); + h = SafeOpenWrite (destfile, 2*1024*1024); + SafeWrite (h, &lnotype, sizeof(int)); + SafeWrite (h, &version, sizeof(int)); + SafeWrite (h, &numglobaldefs, sizeof(int)); + SafeWrite (h, &numpr_globals, sizeof(int)); + SafeWrite (h, &numfielddefs, sizeof(int)); + SafeWrite (h, &numstatements, sizeof(int)); + SafeWrite (h, statement_linenums, numstatements*sizeof(int)); + SafeClose (h); + } } } @@ -2740,7 +2773,18 @@ void QCC_PR_CommandLinePrecompilerOptions (void) if (!stricmp(myargv[i]+2, "all")) { for (j = 0; j < ERR_PARSEERRORS; j++) - qccwarningaction[j] = WA_WARN; + if (qccwarningaction[j] == WA_IGNORE) + { + if (j == WARN_FIXEDRETURNVALUECONFLICT) + continue; + qccwarningaction[j] = WA_WARN; + } + } + else if (!stricmp(myargv[i]+2, "extra")) + { + for (j = 0; j < ERR_PARSEERRORS; j++) + if (qccwarningaction[j] == WA_IGNORE) + qccwarningaction[j] = WA_WARN; } else if (!stricmp(myargv[i]+2, "none")) { @@ -2908,7 +2952,6 @@ void QCC_SetDefaultProperties (void) qccwarningaction[WARN_FIXEDRETURNVALUECONFLICT] = WA_IGNORE; qccwarningaction[WARN_EXTRAPRECACHE] = WA_IGNORE; qccwarningaction[WARN_DEADCODE] = WA_IGNORE; - qccwarningaction[WARN_INEFFICIENTPLUSPLUS] = WA_IGNORE; qccwarningaction[WARN_FTE_SPECIFIC] = WA_IGNORE; qccwarningaction[WARN_EXTENSION_USED] = WA_IGNORE; qccwarningaction[WARN_IFSTRING_USED] = WA_IGNORE; @@ -3020,6 +3063,7 @@ void QCC_main (int argc, char **argv) //as part of the quake engine myargc = argc; myargv = argv; pr_scope = NULL; + locals_marshalled = 0; qcc_compileactive = true; @@ -3613,7 +3657,7 @@ void QCC_FinishCompile(void) printf("numtemps %i\n", numtemps); } if (!flag_msvcstyle) - printf("%i warnings\n", pr_warning_count); + printf("Done. %i warnings\n", pr_warning_count); } qcc_compileactive = false; diff --git a/engine/qclib/qcd_main.c b/engine/qclib/qcd_main.c index 1647b124b..76b7a293c 100644 --- a/engine/qclib/qcd_main.c +++ b/engine/qclib/qcd_main.c @@ -6,12 +6,12 @@ #ifdef AVAIL_ZLIB #ifdef _WIN32 #define ZEXPORT VARGS -#include "../libs/zlib.h" +#include #ifdef _WIN64 -# pragma comment (lib, "../libs/zlib64.lib") +//# pragma comment (lib, "../libs/zlib64.lib") #else -# pragma comment (lib, "../libs/zlib.lib") +//# pragma comment (lib, "../libs/zlib.lib") #endif #else #include @@ -92,12 +92,12 @@ int SafeSeek(int hand, int ofs, int mode); int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle) { int i; - if (method == 0) //copy + if (method == 0) //copy, allows a lame pass-through. { SafeWrite(handle, in, len); return len; } - else if (method == 1) //xor encryption + else if (method == 1) //xor encryption, not secure. maybe useful for the string table. { for (i = 0; i < len; i++) in[i] = in[i] ^ 0xA5; diff --git a/engine/qclib/qcdecomp.c b/engine/qclib/qcdecomp.c index 4fa154741..f1d35341d 100644 --- a/engine/qclib/qcdecomp.c +++ b/engine/qclib/qcdecomp.c @@ -7,7 +7,7 @@ #define MAX_PARMS 8 // I put the following here to resolve "undefined reference to `__imp__vsnprintf'" with MinGW64 ~ Moodles -#ifdef _WIN32 +#if 0//def _WIN32 #if (_MSC_VER >= 1400) //with MSVC 8, use MS extensions #define snprintf linuxlike_snprintf_vc8 diff --git a/engine/qclib/test.c b/engine/qclib/test.c index 979730094..bee386ae0 100644 --- a/engine/qclib/test.c +++ b/engine/qclib/test.c @@ -18,7 +18,7 @@ //builtins and builtin management. -void PF_prints (pubprogfuncs_t *prinst, struct globalvars_s *gvars) +void PF_puts (pubprogfuncs_t *prinst, struct globalvars_s *gvars) { char *s; s = prinst->VarString(prinst, 0); @@ -26,16 +26,378 @@ void PF_prints (pubprogfuncs_t *prinst, struct globalvars_s *gvars) printf("%s", s); } -void PF_printv (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +void PF_putv (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { printf("%f %f %f\n", G_FLOAT(OFS_PARM0+0), G_FLOAT(OFS_PARM0+1), G_FLOAT(OFS_PARM0+2)); } -void PF_printf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +void PF_putf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { printf("%f\n", G_FLOAT(OFS_PARM0)); } +#ifdef _WIN32 + #define Q_snprintfz _snprintf + #define Q_vsnprintf _vsnprintf +#else + #define Q_snprintfz snprintf + #define Q_vsnprintf vsnprintf +#endif +char *va(char *format, ...) +{ + va_list argptr; + static char string[1024]; + va_start (argptr, format); + Q_vsnprintf (string, sizeof(string), format,argptr); + va_end (argptr); + return string; +} + +void QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, char *s, int firstarg, char *outbuf, int outbuflen) +{ + const char *s0; + char *o = outbuf, *end = outbuf + outbuflen, *err; + int width, precision, thisarg, flags; + char formatbuf[16]; + char *f; + int argpos = firstarg; + int isfloat; + static int dummyivec[3] = {0, 0, 0}; + static float dummyvec[3] = {0, 0, 0}; + +#define PRINTF_ALTERNATE 1 +#define PRINTF_ZEROPAD 2 +#define PRINTF_LEFT 4 +#define PRINTF_SPACEPOSITIVE 8 +#define PRINTF_SIGNPOSITIVE 16 + + formatbuf[0] = '%'; + +#define GETARG_FLOAT(a) (((a)>=firstarg && (a)callargc) ? (G_FLOAT(OFS_PARM0 + 3 * (a))) : 0) +#define GETARG_VECTOR(a) (((a)>=firstarg && (a)callargc) ? (G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec) +#define GETARG_INT(a) (((a)>=firstarg && (a)callargc) ? (G_INT(OFS_PARM0 + 3 * (a))) : 0) +#define GETARG_INTVECTOR(a) (((a)>=firstarg && (a)callargc) ? ((int*) G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec) +#define GETARG_STRING(a) (((a)>=firstarg && (a)callargc) ? (PR_GetStringOfs(prinst, OFS_PARM0 + 3 * (a))) : "") + + for(;;) + { + s0 = s; + switch(*s) + { + case 0: + goto finished; + case '%': + ++s; + + if(*s == '%') + goto verbatim; + + // complete directive format: + // %3$*1$.*2$ld + + width = -1; + precision = -1; + thisarg = -1; + flags = 0; + isfloat = -1; + + // is number following? + if(*s >= '0' && *s <= '9') + { + width = strtol(s, &err, 10); + if(!err) + { + printf("PF_sprintf: bad format string: %s\n", s0); + goto finished; + } + if(*err == '$') + { + thisarg = width + (firstarg-1); + width = -1; + s = err + 1; + } + else + { + if(*s == '0') + { + flags |= PRINTF_ZEROPAD; + if(width == 0) + width = -1; // it was just a flag + } + s = err; + } + } + + if(width < 0) + { + for(;;) + { + switch(*s) + { + case '#': flags |= PRINTF_ALTERNATE; break; + case '0': flags |= PRINTF_ZEROPAD; break; + case '-': flags |= PRINTF_LEFT; break; + case ' ': flags |= PRINTF_SPACEPOSITIVE; break; + case '+': flags |= PRINTF_SIGNPOSITIVE; break; + default: + goto noflags; + } + ++s; + } +noflags: + if(*s == '*') + { + ++s; + if(*s >= '0' && *s <= '9') + { + width = strtol(s, &err, 10); + if(!err || *err != '$') + { + printf("PF_sprintf: invalid format string: %s\n", s0); + goto finished; + } + s = err + 1; + } + else + width = argpos++; + width = GETARG_FLOAT(width); + if(width < 0) + { + flags |= PRINTF_LEFT; + width = -width; + } + } + else if(*s >= '0' && *s <= '9') + { + width = strtol(s, &err, 10); + if(!err) + { + printf("PF_sprintf: invalid format string: %s\n", s0); + goto finished; + } + s = err; + if(width < 0) + { + flags |= PRINTF_LEFT; + width = -width; + } + } + // otherwise width stays -1 + } + + if(*s == '.') + { + ++s; + if(*s == '*') + { + ++s; + if(*s >= '0' && *s <= '9') + { + precision = strtol(s, &err, 10); + if(!err || *err != '$') + { + printf("PF_sprintf: invalid format string: %s\n", s0); + goto finished; + } + s = err + 1; + } + else + precision = argpos++; + precision = GETARG_FLOAT(precision); + } + else if(*s >= '0' && *s <= '9') + { + precision = strtol(s, &err, 10); + if(!err) + { + printf("PF_sprintf: invalid format string: %s\n", s0); + goto finished; + } + s = err; + } + else + { + printf("PF_sprintf: invalid format string: %s\n", s0); + goto finished; + } + } + + for(;;) + { + switch(*s) + { + case 'h': isfloat = 1; break; + case 'l': isfloat = 0; break; + case 'L': isfloat = 0; break; + case 'j': break; + case 'z': break; + case 't': break; + default: + goto nolength; + } + ++s; + } +nolength: + + // now s points to the final directive char and is no longer changed + if (*s == 'p' || *s == 'P') + { + //%p is slightly different from %x. + //always 8-bytes wide with 0 padding, always ints. + flags |= PRINTF_ZEROPAD; + if (width < 0) width = 8; + if (isfloat < 0) isfloat = 0; + } + else if (*s == 'i') + { + //%i defaults to ints, not floats. + if(isfloat < 0) isfloat = 0; + } + + //assume floats, not ints. + if(isfloat < 0) + isfloat = 1; + + if(thisarg < 0) + thisarg = argpos++; + + if(o < end - 1) + { + f = &formatbuf[1]; + if(*s != 's' && *s != 'c') + if(flags & PRINTF_ALTERNATE) *f++ = '#'; + if(flags & PRINTF_ZEROPAD) *f++ = '0'; + if(flags & PRINTF_LEFT) *f++ = '-'; + if(flags & PRINTF_SPACEPOSITIVE) *f++ = ' '; + if(flags & PRINTF_SIGNPOSITIVE) *f++ = '+'; + *f++ = '*'; + if(precision >= 0) + { + *f++ = '.'; + *f++ = '*'; + } + if (*s == 'p') + *f++ = 'x'; + else if (*s == 'P') + *f++ = 'X'; + else + *f++ = *s; + *f++ = 0; + + if(width < 0) // not set + width = 0; + + switch(*s) + { + case 'd': case 'i': + if(precision < 0) // not set + Q_snprintfz(o, end - o, formatbuf, width, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg))); + else + Q_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg))); + o += strlen(o); + break; + case 'o': case 'u': case 'x': case 'X': case 'p': case 'P': + if(precision < 0) // not set + Q_snprintfz(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); + else + Q_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); + o += strlen(o); + break; + case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': + if(precision < 0) // not set + Q_snprintfz(o, end - o, formatbuf, width, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg))); + else + Q_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg))); + o += strlen(o); + break; + case 'v': case 'V': + f[-2] += 'g' - 'v'; + if(precision < 0) // not set + Q_snprintfz(o, end - o, va("%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf), + width, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]), + width, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]), + width, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2]) + ); + else + Q_snprintfz(o, end - o, va("%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf), + width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]), + width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]), + width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2]) + ); + o += strlen(o); + break; + case 'c': + //UTF-8-FIXME: figure it out yourself +// if(flags & PRINTF_ALTERNATE) + { + if(precision < 0) // not set + Q_snprintfz(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); + else + Q_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); + o += strlen(o); + } +/* else + { + unsigned int c = (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)); + char charbuf16[16]; + const char *buf = u8_encodech(c, NULL, charbuf16); + if(!buf) + buf = ""; + if(precision < 0) // not set + precision = end - o - 1; + o += u8_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision); + } +*/ break; + case 's': + //UTF-8-FIXME: figure it out yourself +// if(flags & PRINTF_ALTERNATE) + { + if(precision < 0) // not set + Q_snprintfz(o, end - o, formatbuf, width, GETARG_STRING(thisarg)); + else + Q_snprintfz(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg)); + o += strlen(o); + } +/* else + { + if(precision < 0) // not set + precision = end - o - 1; + o += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision); + } +*/ break; + default: + printf("PF_sprintf: invalid format string: %s\n", s0); + goto finished; + } + } + ++s; + break; + default: +verbatim: + if(o < end - 1) + *o++ = *s; + s++; + break; + } + } +finished: + *o = 0; +} + +void PF_printf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + char outbuf[4096]; + PF_sprintf_internal(prinst, pr_globals, PR_GetStringOfs(prinst, OFS_PARM0), 1, outbuf, sizeof(outbuf)); + printf("%s", outbuf); +} + +void PF_spawn (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + struct edict_s *ed; + ed = ED_Alloc(prinst); + pr_globals = PR_globals(prinst, PR_CURRENT); + RETURN_EDICT(prinst, ed); +} void PF_bad (pubprogfuncs_t *prinst, struct globalvars_s *gvars) { @@ -44,9 +406,11 @@ void PF_bad (pubprogfuncs_t *prinst, struct globalvars_s *gvars) builtin_t builtins[] = { PF_bad, - PF_prints, - PF_printv, - PF_printf + PF_puts, + PF_putv, + PF_putf, + PF_printf, + PF_spawn };