From a450035e80a5d6d5403df6b34c0d762800b55afe Mon Sep 17 00:00:00 2001 From: Spoike Date: Sat, 14 Jul 2018 18:50:20 +0000 Subject: [PATCH] added scoped enum values. enum[flags] [class] NAME : TYPE {foo = (TYPE)val, bar=(TYPE)er}; NAME sym = NAME::foo; if (sym == sym.bar) etc; TYPE can be int or float for enumflags, while non-flags enums can be any type that be initialised comfortably (supposedly) including strings and vectors. If the class keyword is used, then expect errors if you store other values into the enum. switch statements should give warnings if a value has no equal case. also fix some compiler warnings. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5271 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/qclib/progslib.h | 2 +- engine/qclib/qcc.h | 10 +- engine/qclib/qcc_cmdlib.c | 3 +- engine/qclib/qcc_pr_comp.c | 270 ++++++++++++++++++++++++++++--------- engine/qclib/qcc_pr_lex.c | 24 +++- engine/qclib/qccmain.c | 2 +- 6 files changed, 235 insertions(+), 76 deletions(-) diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 1f9fe86a..22126573 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 b1142e08..ebba250c 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 4395e4ef..3ee9f705 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 4e13c293..efd19b7b 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 a37a2a88..61fb147a 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 6cdf1b47..b0a6724c 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");