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
This commit is contained in:
Spoike 2018-07-14 18:50:20 +00:00
parent adbb1a1124
commit a450035e80
6 changed files with 235 additions and 76 deletions

View File

@ -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

View File

@ -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

View File

@ -47,7 +47,8 @@ char *basictypenames[] = {
"variant",
"struct",
"union",
"accessor"
"accessor",
"enum"
};
/*

View File

@ -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);

View File

@ -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;

View File

@ -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");