diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 1f9fe86ac..221265733 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -66,7 +66,7 @@ typedef struct { int spare[2]; } evalc_t; #define sizeofevalc sizeof(evalc_t) -typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_integer, ev_variant, ev_struct, ev_union, ev_accessor} etype_t; +typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_integer, ev_variant, ev_struct, ev_union, ev_accessor, ev_enum} etype_t; enum { DEBUG_TRACE_OFF, //debugging should be off. DEBUG_TRACE_INTO, //debug into functions diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index b1142e08b..ebba250cc 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -347,6 +347,7 @@ struct accessor_s struct QCC_type_s *type; struct QCC_type_s *indexertype; //null if not indexer QCC_sref_t getset_func[2]; + QCC_sref_t staticval; pbool getset_isref[2]; char *fieldname; }; @@ -366,8 +367,8 @@ typedef struct QCC_type_s pbool typedefed:1; pbool vargs:1; //function has vargs pbool vargcount:1; //function has special varg count param - char *name; - char *aname; + const char *name; + const char *aname; struct accessor_s *accessors; @@ -688,12 +689,12 @@ void QCC_PR_PrintStatement (QCC_statement_t *s); void QCC_PR_Lex (void); // reads the next token into pr_token and classifies its type -QCC_type_t *QCC_PR_NewType (char *name, int basictype, pbool typedefed); +QCC_type_t *QCC_PR_NewType (const char *name, int basictype, pbool typedefed); //note: name must be hunk/immediate QCC_type_t *QCC_PointerTypeTo(QCC_type_t *type); QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail); QCC_sref_t QCC_PR_ParseDefaultInitialiser(QCC_type_t *type); extern pbool type_inlinefunction; -QCC_type_t *QCC_TypeForName(char *name); +QCC_type_t *QCC_TypeForName(const char *name); QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype); QCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype); QCC_type_t *QCC_PR_GenFunctionType (QCC_type_t *rettype, struct QCC_typeparam_s *args, int numargs); @@ -785,6 +786,7 @@ enum { WARN_DUPLICATEPRECOMPILER, WARN_IDENTICALPRECOMPILER, WARN_FORMATSTRING, //sprintf + WARN_DEPRECACTEDSYNTAX, //triggered when syntax is used that I'm trying to kill WARN_GMQCC_SPECIFIC, //extension created by gmqcc that conflicts or isn't properly implemented. WARN_FTE_SPECIFIC, //extension that only FTEQCC will have a clue about. WARN_EXTENSION_USED, //extension that frikqcc also understands diff --git a/engine/qclib/qcc_cmdlib.c b/engine/qclib/qcc_cmdlib.c index 4395e4ef4..3ee9f7053 100644 --- a/engine/qclib/qcc_cmdlib.c +++ b/engine/qclib/qcc_cmdlib.c @@ -47,7 +47,8 @@ char *basictypenames[] = { "variant", "struct", "union", - "accessor" + "accessor", + "enum" }; /* diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 4e13c2937..efd19b7b0 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -225,9 +225,9 @@ QCC_sref_t QCC_PR_GenerateFunctionCallRef (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t QCC_PR_GenerateFunctionCall1 (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t a, QCC_type_t *type_a); QCC_sref_t QCC_PR_GenerateFunctionCall2 (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t a, QCC_type_t *type_a, QCC_sref_t b, QCC_type_t *type_b); -QCC_sref_t QCC_MakeTranslateStringConst(char *value); -QCC_sref_t QCC_MakeStringConst(char *value); -QCC_sref_t QCC_MakeStringConstLength(char *value, int length); +QCC_sref_t QCC_MakeTranslateStringConst(const char *value); +QCC_sref_t QCC_MakeStringConst(const char *value); +QCC_sref_t QCC_MakeStringConstLength(const char *value, int length); QCC_sref_t QCC_MakeFloatConst(float value); QCC_sref_t QCC_MakeIntConst(int value); QCC_sref_t QCC_MakeVectorConst(float a, float b, float c); @@ -6758,15 +6758,15 @@ static QCC_sref_t QCC_MakeStringConstInternal(const char *value, size_t length, return QCC_MakeSRefForce(cn, 0, type_string); } -QCC_sref_t QCC_MakeStringConstLength(char *value, int length) +QCC_sref_t QCC_MakeStringConstLength(const char *value, int length) { return QCC_MakeStringConstInternal(value, length, false); } -QCC_sref_t QCC_MakeStringConst(char *value) +QCC_sref_t QCC_MakeStringConst(const char *value) { return QCC_MakeStringConstInternal(value, strlen(value)+1, false); } -QCC_sref_t QCC_MakeTranslateStringConst(char *value) +QCC_sref_t QCC_MakeTranslateStringConst(const char *value) { return QCC_MakeStringConstInternal(value, strlen(value)+1, true); } @@ -7463,7 +7463,7 @@ vectorarrayindex: QCC_FreeTemp(idx); return QCC_PR_BuildRef(retbuf, REF_GLOBAL, QCC_MakeIntConst(arraysize), nullsref, type_integer, true); } - else if (t->type == ev_vector && !arraysize && QCC_PR_CheckToken(".")) + else if (t->type == ev_vector && !arraysize && !t->accessors && QCC_PR_CheckToken(".")) { char *swizzle = QCC_PR_ParseName(); //single-channel swizzles just result in a float. nice and easy. assignable, too. @@ -7492,7 +7492,7 @@ vectorarrayindex: } else if (((t->type == ev_pointer && !arraysize) || (t->type == ev_field && (t->aux_type->type == ev_struct || t->aux_type->type == ev_union)) || t->type == ev_struct || t->type == ev_union) && (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->"))) { - char *tname; + const char *tname; unsigned int ofs; pbool fld = t->type == ev_field; struct QCC_typeparam_s *p; @@ -7738,6 +7738,9 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo //fixme: namespaces should be relative if (QCC_PR_CheckToken("::")) { + struct accessor_s *a; + QCC_type_t *p; + char membername[1024]; expandmemberfields = false; //this::classname should also be available to the find builtin, etc. this won't affect self.classname::member nor classname::staticfunc if (assumeclass && !strcmp(name, "super")) @@ -7746,18 +7749,37 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo t = assumeclass; else t = QCC_TypeForName(name); - if (!t || t->type != ev_entity) + if (!t) { - QCC_PR_ParseError (ERR_NOTATYPE, "Not a class \"%s\"", name); - d = nullsref; + d = QCC_PR_GetSRef (pr_assumetermtype, name, pr_assumetermscope, false, 0, pr_assumetermflags); + if (d.cast) + { + QCC_FreeTemp(d); + t = d.cast; + } + else + QCC_PR_ParseError (ERR_UNKNOWNVALUE, "\"%s\" is not a type", name); } - else + + name = QCC_PR_ParseName (); + //walk up the parents if needed, to find one that has that field + for(d = nullsref, p = t; ; ) { - QCC_type_t *p; - char membername[1024]; - name = QCC_PR_ParseName (); - //walk up the parents if needed, to find one that has that field - for(d = nullsref, p = t; !d.cast && p; p = p->parentclass) + if (!d.cast && p->accessors) + { + for (a = t->accessors; a; a = a->next) + { + if (!strcmp(a->fieldname, name)) + { + d = a->staticval; + QCC_ForceUnFreeDef(d.sym); + break; + } + } + if (d.cast) + break; + } + if (!d.cast && t->type == ev_entity) { //use static functions in preference to virtual functions. kinda needed so you can use super::func... QC_snprintfz(membername, sizeof(membername), "%s::%s", p->name, name); @@ -7767,11 +7789,16 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo QC_snprintfz(membername, sizeof(membername), "%s::"MEMBERFIELDNAME, p->name, name); d = QCC_PR_GetSRef (NULL, membername, pr_scope, false, 0, false); } + + p = p->parentclass; + if (p) + continue; } - if (!d.cast) - { - QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s::%s\"", t->name, name); - } + break; + } + if (!d.cast) + { + QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s::%s\"", t->name, name); } } else @@ -8506,6 +8533,7 @@ void QCC_StoreToSRef(QCC_sref_t dest, QCC_sref_t source, QCC_type_t *type, pbool { case ev_struct: case ev_union: + case ev_enum: //don't bother trying to optimise any temps here, its not likely to happen anyway. for (i = 0; i+2 < type->size; i+=3, dest.ofs += 3, source.ofs += 3) { @@ -9196,6 +9224,11 @@ QCC_sref_t QCC_RefToDef(QCC_ref_t *ref, pbool freetemps) QCC_ForceUnFreeDef(ref->accessor->getset_func[0].sym); return QCC_PR_GenerateFunctionCallSref(nullsref, ref->accessor->getset_func[0], arg, args); } + else if (ref->accessor && ref->accessor->staticval.cast) + { + QCC_ForceUnFreeDef(ref->accessor->staticval.sym); + return ref->accessor->staticval; + } else QCC_PR_ParseErrorPrintSRef(ERR_NOFUNC, ref->base, "Accessor %s has no get function", ref->accessor?ref->accessor->fieldname:""); break; @@ -9473,6 +9506,16 @@ QCC_opcode_t *QCC_PR_ChooseOpcode(QCC_sref_t lhs, QCC_sref_t rhs, QCC_opcode_t * type_a = lhs.cast->type; // type_b = rhs.cast->type; + if (type_a == ev_enum) + { + if (lhs.cast == rhs.cast) + { + lhs.cast = lhs.cast->aux_type; + rhs.cast = rhs.cast->aux_type; +// type_a = lhs.cast->type; + } + } + if (op->name[0] == '.')// field access gets type from field { if (rhs.cast->aux_type) @@ -10925,6 +10968,7 @@ void QCC_PR_ParseStatement (void) int defaultcase = -1; int oldst; QCC_type_t *switchtype; + struct accessor_s *acc; breaks = num_breaks; cases = num_cases; @@ -10967,7 +11011,7 @@ void QCC_PR_ParseStatement (void) //it should be possible to nest these. switchtype = e.cast; - switch(switchtype->type) + switch(switchtype->type==ev_enum?switchtype->aux_type->type:switchtype->type) { case ev_float: op = OP_SWITCH_F; @@ -11028,6 +11072,39 @@ void QCC_PR_ParseStatement (void) oldst = numstatements; + for (acc = switchtype->accessors; acc; acc = acc->next) + { + const QCC_eval_t *match = QCC_SRef_EvalConst(acc->staticval); + if (!match) + continue; //not an enum value, ignore it. + for (i = cases; i < num_cases; i++) + { + if (!pr_casesref[i].cast) + break; //its a default + if (pr_casesref2[i].cast) + { //caserange + break; //FIXME: too lazy to check these. + } + else + { //case + if (pr_casesref[i].type == REF_GLOBAL && pr_casesref[i].cast == switchtype) + { + const QCC_eval_t *eval = QCC_SRef_EvalConst(pr_casesref[i].base); + if (!eval) + break; //can't verify it + if (!memcmp(eval, match, sizeof(*eval)*switchtype->size)) + break; //validated. + } + else + break; //can't verify it + } + } + if (i == num_cases) + { + QCC_PR_ParseWarning(0, "%s::%s not part of switch", switchtype->name, acc->fieldname); + } + } + QCC_ForceUnFreeDef(e.sym); //in the following code, e should still be live for (i = cases; i < num_cases; i++) { @@ -11090,7 +11167,7 @@ void QCC_PR_ParseStatement (void) const QCC_eval_t *eval = QCC_SRef_EvalConst(dmin); if (!eval || eval->_int) { - switch(e.cast->type) + switch(e.cast->type==ev_enum?e.cast->aux_type->type:e.cast->type) { case ev_float: e2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_F], e, dmin, NULL, STFL_PRESERVEA); @@ -14551,22 +14628,61 @@ QCC_sref_t QCC_PR_ParseDefaultInitialiser(QCC_type_t *type) int accglobalsblock; //0 = error, 1 = var, 2 = function, 3 = objdata -void QCC_PR_ParseEnum(pbool flags) +QCC_type_t *QCC_PR_ParseEnum(pbool flags) { - const char *name; + const char *name = NULL; QCC_sref_t sref; - pbool wantint = false; - int iv = flags?1:0; - float fv = iv; + int next_i = flags?1:0; + float next_f = next_i; + struct accessor_s *acc; + QCC_type_t *enumtype = NULL, *basetype; + pbool strictenum = false; + basetype = (flag_assume_integer?type_integer:type_float); - if (QCC_PR_CheckKeyword(keyword_integer, "integer") || QCC_PR_CheckKeyword(keyword_int, "int")) - wantint = true; - else if (QCC_PR_CheckKeyword(keyword_float, "float")) - wantint = false; - else - wantint = flag_assume_integer; + if (!QCC_PR_CheckToken("{")) + { + QCC_type_t *type; + + strictenum = QCC_PR_CheckName("class"); //c++11 style + + type = QCC_PR_ParseType(false, true); //legacy behaviour + if (type) + basetype = basetype; + else + { + basetype = (flag_assume_integer?type_integer:type_float); + if (pr_token_type == tt_name) + name = QCC_PR_ParseName(); + else + name = NULL; + if (QCC_PR_CheckToken(":")) + basetype = QCC_PR_ParseType(false, false); + else if (strictenum) + QCC_PR_Expect(":"); + } + QCC_PR_Expect("{"); + } + + if (flags && basetype->type != ev_float && basetype->type != ev_integer && basetype->type != ev_vector) + QCC_PR_ParseError(ERR_NOTANUMBER, "enumflags - must be numeric type"); + + if (name) + { + enumtype = QCC_TypeForName(name); + if (!enumtype) + { + if (strictenum) + { + enumtype = QCC_PR_NewType(name, basetype->type, true); + enumtype->aux_type = basetype; + enumtype->type = ev_enum; + } + else + enumtype = QCC_PR_NewType(name, basetype->type, true); + enumtype->size = basetype->size; + } + } - QCC_PR_Expect("{"); while(1) { name = QCC_PR_ParseName(); @@ -14586,28 +14702,48 @@ void QCC_PR_ParseEnum(pbool flags) { const QCC_eval_t *eval; sref = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + sref = QCC_SupplyConversion(sref, basetype->type, true); eval = QCC_SRef_EvalConst(sref); if (eval) { if (sref.cast->type == ev_float) - iv = fv = eval->_float; - else - fv = iv = eval->_int; + next_i = next_f = eval->_float; + else if (sref.cast->type == ev_integer) + next_f = next_i = eval->_int; } else if (sref.sym) QCC_PR_ParseError(ERR_NOTANUMBER, "enum - %s is not a constant", sref.sym->name); else QCC_PR_ParseError(ERR_NOTANUMBER, "enum - not a number"); - QCC_FreeTemp(sref); + //do this, because we can. with any luck we'll just hit the same const anyway, and if not then we may have managed to avoid hitting a global. + if (basetype->type==ev_integer) + { + QCC_FreeTemp(sref); + sref = QCC_MakeIntConst(next_i); + } + else if (basetype->type==ev_float) + { + QCC_FreeTemp(sref); + sref = QCC_MakeFloatConst(next_i); + } } } + else + { + if (basetype->type==ev_integer) + sref = QCC_MakeIntConst(next_i); + else if (basetype->type==ev_float) + sref = QCC_MakeFloatConst(next_f); + else + QCC_PR_ParseError(ERR_NOTANUMBER, "values for enums of this type must be initialised"); + } if (flags) { int bits = 0; - int i = wantint?iv:(int)fv; - if (!wantint && i != fv) - QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, "enumflags - %f not an integer value", fv); + int i = (basetype->type==ev_integer)?next_i:(int)next_f; + if (basetype->type!=ev_integer && i != next_f) + QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, "enumflags - %f not an integer value", next_f); else { while(i) @@ -14617,25 +14753,42 @@ void QCC_PR_ParseEnum(pbool flags) i>>=1; } if (bits > 1) - QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, "enumflags - %f has multiple bits set", fv); + QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, "enumflags - %x(%f) has multiple bits set", next_i, next_f); } } - if (wantint) - sref = QCC_MakeIntConst(iv); + if (enumtype) + { + for (acc = enumtype->accessors; acc; acc = acc->next) + if (!strcmp(acc->fieldname, name)) + { + QCC_Error(ERR_TOOMANYINITIALISERS, "%s::%s already declared", enumtype->name, name); + break; + } + acc = qccHunkAlloc(sizeof(*acc)); + acc->fieldname = (char*)name; + acc->next = enumtype->accessors; + acc->type = enumtype;//sref.cast; + acc->indexertype = NULL; + enumtype->accessors = acc; + + acc->staticval = sref; + acc->staticval.cast = enumtype; + } else - sref = QCC_MakeFloatConst(fv); - pHash_Add(&globalstable, name, sref.sym, qccHunkAlloc(sizeof(bucket_t))); + { //value gets added to global pool + pHash_Add(&globalstable, name, sref.sym, qccHunkAlloc(sizeof(bucket_t))); + } QCC_FreeTemp(sref); if (flags) { - fv *= 2; - iv *= 2; + next_f *= 2; + next_i *= 2; } else { - fv++; - iv++; + next_f++; + next_i++; } if (QCC_PR_CheckToken("}")) @@ -14644,6 +14797,7 @@ void QCC_PR_ParseEnum(pbool flags) if (QCC_PR_CheckToken("}")) break; // accept trailing comma } + return enumtype?enumtype:basetype; } /* ================ @@ -14685,20 +14839,6 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) while (QCC_PR_CheckToken(";")) fatal = false; - //FIXME: these should be moved into parsetype - if (QCC_PR_CheckKeyword(keyword_enum, "enum")) - { - QCC_PR_ParseEnum(false); - QCC_PR_Expect(";"); - return; - } - if (QCC_PR_CheckKeyword(keyword_enumflags, "enumflags")) - { - QCC_PR_ParseEnum(true); - QCC_PR_Expect(";"); - return; - } - if (QCC_PR_CheckKeyword (keyword_typedef, "typedef")) { type = QCC_PR_ParseType(false, false); diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index a37a2a887..61fb147a7 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -103,7 +103,7 @@ QCC_def_t def_ret, def_parms[MAX_PARMS]; void QCC_PR_LexWhitespace (pbool inhibitpreprocessor); - +QCC_type_t *QCC_PR_ParseEnum(pbool flags); //for compiler constants and file includes. @@ -4500,6 +4500,7 @@ QCC_type_t *QCC_PR_DuplicateType(QCC_type_t *in, pbool recurse) out->num_parms = in->num_parms; out->params = qccHunkAlloc(sizeof(*out->params) * out->num_parms); memcpy(out->params, in->params, sizeof(*out->params) * out->num_parms); + out->accessors = in->accessors; out->size = in->size; out->num_parms = in->num_parms; out->name = in->name; @@ -4659,7 +4660,7 @@ QCC_type_t *QCC_PR_NextSubType(QCC_type_t *type, QCC_type_t *prev) } */ -QCC_type_t *QCC_TypeForName(char *name) +QCC_type_t *QCC_TypeForName(const char *name) { return pHash_Get(&typedeftable, name); /* @@ -4953,8 +4954,7 @@ QCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto) { char name[128]; QC_snprintfz(name, sizeof(name), "ptr to %s", pointsto->name); - e->name = qccHunkAlloc(strlen(name)+1); - strcpy(e->name, name); + e->name = strcpy(qccHunkAlloc(strlen(name)+1), name); } pointsto->ptrto = e; return e; @@ -5681,6 +5681,22 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) return NULL; } + //FIXME: these should be moved into parsetype + if (QCC_PR_CheckKeyword(keyword_enum, "enum")) + { + newt = QCC_PR_ParseEnum(false); + if (QCC_PR_CheckToken(";")) + return NULL; + return newt; + } + if (QCC_PR_CheckKeyword(keyword_enumflags, "enumflags")) + { + newt = QCC_PR_ParseEnum(true); + if (QCC_PR_CheckToken(";")) + return NULL; + return newt; + } + structtype = ev_void; if (QCC_PR_CheckKeyword (keyword_union, "union")) structtype = ev_union; diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 6cdf1b47d..b0a6724c8 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -3039,7 +3039,7 @@ PR_PrintDefs QCC_PR_PrintOfs (d->ofs); }*/ -QCC_type_t *QCC_PR_NewType (char *name, int basictype, pbool typedefed) +QCC_type_t *QCC_PR_NewType (const char *name, int basictype, pbool typedefed) { if (numtypeinfos>= maxtypeinfos) QCC_Error(ERR_TOOMANYTYPES, "Too many types");