diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 87ed45940..9095cc3d2 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -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); diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 932924c02..70e169a3b 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -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:"", 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)