A quick attempt to get accessors working inside classes. Needs proper testing.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5945 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2021-07-03 23:33:52 +00:00
parent 4ac09f8a03
commit 3d1014efe4
2 changed files with 254 additions and 181 deletions

View File

@ -1886,7 +1886,20 @@ static const char *QCC_GetRefName(QCC_ref_t *ref, char *buffer, size_t buffersiz
QC_snprintfz(buffer, buffersize, "%s->%s", QCC_GetSRefName(ref->base), QCC_GetSRefName(ref->index));
return buffer;
case REF_ACCESSOR:
//FIXME
if (*ref->accessor->fieldname)
{ //not an anonymous field
if (ref->index.sym)
QC_snprintfz(buffer, buffersize, "%s.%s[%s]", QCC_GetSRefName(ref->base), ref->accessor->fieldname, QCC_GetSRefName(ref->index));
else
QC_snprintfz(buffer, buffersize, "%s.%s", QCC_GetSRefName(ref->base), ref->accessor->fieldname);
}
else
{
if (ref->index.sym)
QC_snprintfz(buffer, buffersize, "%s[%s]", QCC_GetSRefName(ref->base), QCC_GetSRefName(ref->index));
else
QC_snprintfz(buffer, buffersize, "*%s", QCC_GetSRefName(ref->base));
}
break;
case REF_ARRAYHEAD:
case REF_GLOBAL:
@ -8728,78 +8741,95 @@ static QCC_ref_t *QCC_PR_ParseField(QCC_ref_t *refbuf, QCC_ref_t *lhs)
{
QCC_type_t *t;
t = lhs->cast;
if (t->type == ev_entity && (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->")))
if ((t->accessors || t->type == ev_entity) && (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->")))
{
QCC_ref_t *field;
QCC_ref_t fieldbuf;
if (QCC_PR_CheckToken("("))
{
field = QCC_PR_RefExpression(&fieldbuf, TOP_PRIORITY, 0);
QCC_PR_Expect(")");
}
else
field = QCC_PR_ParseRefValue(&fieldbuf, t, false, false, true);
if (field->type != REF_ARRAYHEAD && (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.
//entities, like 'world' might also be consts. just ignore that fact. the def itself is not assigned, but the fields of said def.
//the engine may have a problem with this, but the qcc has no way to referenced locations as readonly separately from the def itself.
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, false);
}
else
{
if (field->type == REF_GLOBAL && strstr(QCC_GetSRefName(field->base), "::"))
{
QCC_sref_t theent = QCC_RefToDef(lhs, true);
*refbuf = *field;
refbuf->type = REF_NONVIRTUAL;
refbuf->index = theent;
return refbuf;
}
if (t->parentclass)
QCC_PR_ParseError(ERR_INTERNAL, "%s is not a field of class %s", QCC_GetSRefName(QCC_RefToDef(field, false)), t->name);
else
QCC_PR_ParseError(ERR_INTERNAL, "%s is not a field", QCC_GetSRefName(QCC_RefToDef(field, false)));
}
lhs = QCC_PR_ParseField(refbuf, lhs);
if (pr_token_type == tt_name)
{
QCC_sref_t index = nullsref;
char *fieldname = pr_token;
struct accessor_s *acc = NULL, *anon = NULL;
QCC_type_t *a;
lhs = QCC_PR_ParseRefArrayPointer (refbuf, lhs, false, false);
}
else if (t->accessors && (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->")))
{
QCC_sref_t index = nullsref;
char *fieldname = QCC_PR_ParseName();
struct accessor_s *acc;
for (acc = t->accessors; acc; acc = acc->next)
if (!strcmp(acc->fieldname, fieldname))
{
if (acc->indexertype)
for (a = t; a && !acc; a = a->parentclass)
for (acc = a->accessors; acc; acc = acc->next)
{
if (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->"))
index = QCC_MakeStringConst(QCC_PR_ParseName());
else
if (!*acc->fieldname && acc->indexertype)
{
QCC_PR_Expect("[");
index = QCC_PR_Expression (TOP_PRIORITY, 0);
QCC_PR_Expect("]");
if (!anon)
anon = acc;
}
else if (!strcmp(acc->fieldname, fieldname))
{
fieldname = QCC_PR_ParseName(); //do it for real now.
if (acc->indexertype)
{
if (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->"))
index = QCC_MakeStringConst(QCC_PR_ParseName());
else
{
QCC_PR_Expect("[");
index = QCC_PR_Expression (TOP_PRIORITY, 0);
QCC_PR_Expect("]");
}
}
break;
}
}
break;
if (!acc && anon)
{
acc = anon;
fieldname = QCC_PR_ParseName(); //do it for real now.
index = QCC_MakeStringConst(fieldname);
}
if (!acc)
for (acc = t->accessors; acc; acc = acc->next)
if (!*acc->fieldname && acc->indexertype)
{
index = QCC_MakeStringConst(fieldname);
break;
}
if (!acc)
QCC_PR_ParseError(ERR_INTERNAL, "%s is not a member of %s", fieldname, t->name);
if (acc)
{
lhs = QCC_PR_BuildAccessorRef(refbuf, QCC_RefToDef(lhs, true), index, acc, lhs->readonly);
lhs = QCC_PR_ParseField(refbuf, lhs);
return lhs;
}
}
lhs = QCC_PR_BuildAccessorRef(refbuf, QCC_RefToDef(lhs, true), index, acc, lhs->readonly);
lhs = QCC_PR_ParseField(refbuf, lhs);
if (t->type == ev_entity)
{
if (QCC_PR_CheckToken("("))
{
field = QCC_PR_RefExpression(&fieldbuf, TOP_PRIORITY, 0);
QCC_PR_Expect(")");
}
else
field = QCC_PR_ParseRefValue(&fieldbuf, t, false, false, true);
if (field->type != REF_ARRAYHEAD && (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.
//entities, like 'world' might also be consts. just ignore that fact. the def itself is not assigned, but the fields of said def.
//the engine may have a problem with this, but the qcc has no way to referenced locations as readonly separately from the def itself.
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, false);
}
else
{
if (field->type == REF_GLOBAL && strstr(QCC_GetSRefName(field->base), "::"))
{
QCC_sref_t theent = QCC_RefToDef(lhs, true);
*refbuf = *field;
refbuf->type = REF_NONVIRTUAL;
refbuf->index = theent;
return refbuf;
}
if (t->parentclass)
QCC_PR_ParseError(ERR_INTERNAL, "%s is not a field of class %s", QCC_GetSRefName(QCC_RefToDef(field, false)), t->name);
else
QCC_PR_ParseError(ERR_INTERNAL, "%s is not a field", QCC_GetSRefName(QCC_RefToDef(field, false)));
}
lhs = QCC_PR_ParseField(refbuf, lhs);
lhs = QCC_PR_ParseRefArrayPointer (refbuf, lhs, false, false);
}
else
QCC_PR_ParseError(ERR_INTERNAL, "%s is not a member of %s", QCC_PR_ParseName(), t->name);
}
else if (flag_qccx && t->type == ev_entity && QCC_PR_CheckToken("["))
{ //p[%0] gives a regular array reference. except that p is probably a float, and we're expecting OP_LOAD_F
@ -9522,6 +9552,7 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo
{
for(type = assumeclass; type && !d.cast; type = type->parentclass)
{
//look for virtual things
QC_snprintfz(membername, sizeof(membername), "%s::"MEMBERFIELDNAME, type->name, name);
d = QCC_PR_GetSRef (NULL, membername, pr_scope, false, 0, false);

View File

@ -5248,6 +5248,142 @@ QCC_type_t *QCC_PR_GenFunctionType (QCC_type_t *rettype, struct QCC_typeparam_s
return QCC_PR_FindType (ftype);
}
struct accessor_s *QCC_PR_ParseAccessorMember(QCC_type_t *classtype, pbool isinline, pbool setnotget)
{
struct accessor_s *acc, *pacc;
char *fieldtypename;
QCC_type_t *fieldtype;
QCC_type_t *indextype;
QCC_sref_t def;
QCC_type_t *functype;
QCC_type_t *parenttype;
struct QCC_typeparam_s arg[3];
int args;
char *indexname;
pbool isref;
char *accessorname;
if (QCC_PR_CheckToken("&"))
isref = 2;
else
isref = QCC_PR_CheckToken("*");
fieldtypename = QCC_PR_ParseName();
fieldtype = QCC_TypeForName(fieldtypename);
if (!fieldtype)
QCC_PR_ParseError(ERR_NOTATYPE, "Invalid type: %s", fieldtypename);
while(QCC_PR_CheckToken("*"))
fieldtype = QCC_PR_PointerType(fieldtype);
if (pr_token_type != tt_punct)
accessorname = QCC_PR_ParseName();
else
accessorname = "";
indextype = NULL;
indexname = "index";
if (QCC_PR_CheckToken("["))
{
fieldtypename = QCC_PR_ParseName();
indextype = QCC_TypeForName(fieldtypename);
if (!QCC_PR_CheckToken("]"))
{
indexname = QCC_PR_ParseName();
QCC_PR_Expect("]");
}
}
QCC_PR_Expect("=");
args = 0;
memset(arg, 0, sizeof(arg));
strcpy (pr_parm_names[args], "this");
arg[args].paramname = "this";
if (isref == 2)
{
arg[args].type = classtype;
arg[args].out = 1; //inout
}
else if (isref)
arg[args].type = QCC_PointerTypeTo(classtype);
else
arg[args].type = classtype;
args++;
if (indextype)
{
strcpy (pr_parm_names[args], indexname);
arg[args].paramname = indexname;
arg[args++].type = indextype;
}
if (setnotget)
{
strcpy (pr_parm_names[args], "value");
arg[args].paramname = "value";
arg[args++].type = fieldtype;
}
functype = QCC_PR_GenFunctionType(setnotget?type_void:fieldtype, arg, args);
if (pr_token_type != tt_name)
{
QCC_function_t *f;
char funcname[256];
QC_snprintfz(funcname, sizeof(funcname), "%s::%s_%s", classtype->name, setnotget?"set":"get", accessorname);
def = QCC_PR_GetSRef(functype, funcname, NULL, true, 0, GDF_CONST | (isinline?GDF_INLINE:0));
pr_classtype = ((classtype->type==ev_entity)?classtype:NULL);
f = QCC_PR_ParseImmediateStatements (def.sym, functype, false);
pr_classtype = NULL;
pr_scope = NULL;
def.sym->symboldata[def.ofs].function = f - functions;
f->def = def.sym;
def.sym->initialized = 1;
}
else
{
const char *funcname = QCC_PR_ParseName();
def = QCC_PR_GetSRef(functype, funcname, NULL, true, 0, GDF_CONST|(isinline?GDF_INLINE:0));
if (!def.cast)
QCC_Error(ERR_NOFUNC, "%s::set_%s: %s was not defined", classtype->name, accessorname, funcname);
}
if (!def.cast || !def.sym || def.sym->temp)
QCC_Error(ERR_NOFUNC, "%s::%s_%s function invalid", classtype->name, setnotget?"set":"get", accessorname);
for (acc = classtype->accessors; acc; acc = acc->next)
if (!strcmp(acc->fieldname, accessorname))
break;
if (!acc)
{
acc = qccHunkAlloc(sizeof(*acc));
acc->fieldname = accessorname;
acc->next = classtype->accessors;
acc->type = fieldtype;
acc->indexertype = indextype;
classtype->accessors = acc;
}
if (acc->getset_func[setnotget].cast)
QCC_Error(ERR_TOOMANYINITIALISERS, "%s::%s_%s already declared", classtype->name, setnotget?"set":"get", accessorname);
acc->getset_func[setnotget] = def;
acc->getset_isref[setnotget] = isref;
QCC_FreeTemp(def);
for (parenttype = classtype->parentclass; parenttype; parenttype = parenttype->parentclass)
{
if (!parenttype->accessors)
continue;
for (pacc = parenttype->accessors; pacc; pacc = pacc->next)
{
if (!strcmp(acc->fieldname, pacc->fieldname))
QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "%s::%s shadows parent %s", classtype->name, acc->fieldname?acc->fieldname:"<anon>", parenttype->name);
}
}
return acc;
}
extern char *basictypenames[];
extern QCC_type_t **basictypes[];
pbool type_inlinefunction;
@ -5404,20 +5540,9 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
if (QCC_PR_CheckToken("{"))
{
struct accessor_s *acc;
pbool setnotget;
char *fieldtypename;
QCC_type_t *fieldtype;
QCC_type_t *indextype;
QCC_sref_t def;
QCC_type_t *functype;
struct QCC_typeparam_s arg[3];
int args;
char *indexname;
pbool isref;
pbool isinline;
do
{
isinline = QCC_PR_CheckName("inline");
@ -5428,115 +5553,8 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
setnotget = false;
else
break;
if (QCC_PR_CheckToken("&"))
isref = 2;
else
isref = QCC_PR_CheckToken("*");
fieldtypename = QCC_PR_ParseName();
fieldtype = QCC_TypeForName(fieldtypename);
if (!fieldtype)
QCC_PR_ParseError(ERR_NOTATYPE, "Invalid type: %s", fieldtypename);
while(QCC_PR_CheckToken("*"))
fieldtype = QCC_PR_PointerType(fieldtype);
if (pr_token_type != tt_punct)
{
funcname = QCC_PR_ParseName();
accessorname = qccHunkAlloc(strlen(funcname)+1);
strcpy(accessorname, funcname);
}
else
accessorname = "";
indextype = NULL;
indexname = "index";
if (QCC_PR_CheckToken("["))
{
fieldtypename = QCC_PR_ParseName();
indextype = QCC_TypeForName(fieldtypename);
if (!QCC_PR_CheckToken("]"))
{
indexname = QCC_PR_ParseName();
QCC_PR_Expect("]");
}
}
QCC_PR_Expect("=");
args = 0;
memset(arg, 0, sizeof(arg));
strcpy (pr_parm_names[args], "this");
arg[args].paramname = "this";
if (isref == 2)
{
arg[args].type = newt;
arg[args].out = 1; //inout
}
else if (isref)
arg[args].type = QCC_PointerTypeTo(newt);
else
arg[args].type = newt;
args++;
if (indextype)
{
strcpy (pr_parm_names[args], indexname);
arg[args].paramname = indexname;
arg[args++].type = indextype;
}
if (setnotget)
{
strcpy (pr_parm_names[args], "value");
arg[args].paramname = "value";
arg[args++].type = fieldtype;
}
functype = QCC_PR_GenFunctionType(setnotget?type_void:fieldtype, arg, args);
if (pr_token_type != tt_name)
{
QCC_function_t *f;
char funcname[256];
QC_snprintfz(funcname, sizeof(funcname), "%s::%s_%s", newt->name, setnotget?"set":"get", accessorname);
def = QCC_PR_GetSRef(functype, funcname, NULL, true, 0, GDF_CONST | (isinline?GDF_INLINE:0));
//pr_classtype = newt;
f = QCC_PR_ParseImmediateStatements (def.sym, functype, false);
pr_classtype = NULL;
pr_scope = NULL;
def.sym->symboldata[def.ofs].function = f - functions;
f->def = def.sym;
def.sym->initialized = 1;
}
else
{
funcname = QCC_PR_ParseName();
def = QCC_PR_GetSRef(functype, funcname, NULL, true, 0, GDF_CONST|(isinline?GDF_INLINE:0));
if (!def.cast)
QCC_Error(ERR_NOFUNC, "%s::set_%s: %s was not defined", newt->name, accessorname, funcname);
}
if (!def.cast || !def.sym || def.sym->temp)
QCC_Error(ERR_NOFUNC, "%s::%s_%s function invalid", newt->name, setnotget?"set":"get", accessorname);
for (acc = newt->accessors; acc; acc = acc->next)
if (!strcmp(acc->fieldname, accessorname))
break;
if (!acc)
{
acc = qccHunkAlloc(sizeof(*acc));
acc->fieldname = accessorname;
acc->next = newt->accessors;
acc->type = fieldtype;
acc->indexertype = indextype;
newt->accessors = acc;
}
if (acc->getset_func[setnotget].cast)
QCC_Error(ERR_TOOMANYINITIALISERS, "%s::%s_%s already declared", newt->name, setnotget?"set":"get", accessorname);
acc->getset_func[setnotget] = def;
acc->getset_isref[setnotget] = isref;
QCC_FreeTemp(def);
QCC_PR_ParseAccessorMember(newt, isinline, setnotget);
} while (QCC_PR_CheckToken(",") || QCC_PR_CheckToken(";"));
QCC_PR_Expect("}");
}
@ -5619,6 +5637,9 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
pbool isnonvirt = false;
pbool isstatic = false;
pbool isignored = false;
pbool isinline = false;
pbool isget = false;
pbool isset = false;
// pbool ispublic = false;
// pbool isprivate = false;
// pbool isprotected = false;
@ -5634,6 +5655,12 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
isignored = true;
else if (QCC_PR_CheckKeyword(1, "strip"))
isignored = true;
else if (QCC_PR_CheckKeyword(1, "inline"))
isinline = true;
else if (QCC_PR_CheckKeyword(1, "get"))
isget = true;
else if (QCC_PR_CheckKeyword(1, "set"))
isset = true;
else if (QCC_PR_CheckKeyword(1, "public"))
/*ispublic = true*/;
else if (QCC_PR_CheckKeyword(1, "private"))
@ -5651,6 +5678,17 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
assumevirtual = -1;
continue;
}
if (isget || isset)
{
if (isvirt)
QCC_PR_ParseWarning(ERR_INTERNAL, "virtual accessors are not supported at this time");
if (isstatic)
QCC_PR_ParseError(ERR_INTERNAL, "static accessors are not supported");
QCC_PR_ParseAccessorMember(newt, isinline, isset);
QCC_PR_CheckToken(";");
continue;
}
newparm = QCC_PR_ParseType(false, false);
if (!newparm)
@ -5700,6 +5738,8 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
{
if (isvirt||isnonvirt)
QCC_Error(ERR_INTERNAL, "virtual keyword on member that is not a function");
if (isinline)
QCC_Error(ERR_INTERNAL, "inline keyword on member that is not a function");
}
if (newparm->type == ev_function)
@ -5710,6 +5750,8 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
}
else if (pr_token[0] == '{')
havebody = true;
if (isinline && (!havebody || isvirt))
QCC_Error(ERR_INTERNAL, "inline keyword on function prototype or virtual function");
}
if (havebody)