- allow functions in structs. This is needed for several internal data types like players, sectors, lines, etc.

- added a new type 'NativeStruct'. This will be used for types that cannot be instantiated, and is also needed to cleanly handle many internal types that only can exist as reference.
This commit is contained in:
Christoph Oelckers 2016-11-19 12:12:29 +01:00
commit f11f020b6c
11 changed files with 418 additions and 335 deletions

View file

@ -278,16 +278,21 @@ class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ }
%type struct_body{ZCC_TreeNode *}
%type struct_member{ZCC_TreeNode *}
struct_def(X) ::= STRUCT(T) IDENTIFIER(A) LBRACE opt_struct_body(B) RBRACE opt_semicolon.
struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body(B) RBRACE opt_semicolon.
{
NEW_AST_NODE(Struct,def,T);
def->NodeName = A.Name();
def->Body = B;
def->Type = nullptr;
def->Symbol = nullptr;
def->Flags = S.Flags;
X = def;
}
%type struct_flags{ClassFlagsBlock}
struct_flags(X) ::= . { X.Flags = 0; }
struct_flags(X) ::= NATIVE. { X.Flags = ZCC_Native; }
opt_struct_body(X) ::= . { X = NULL; }
opt_struct_body(X) ::= struct_body(X).
@ -295,7 +300,7 @@ struct_body(X) ::= error. { X = NULL; }
struct_body(X) ::= struct_member(X).
struct_body(X) ::= struct_member(A) struct_body(B). { X = A; /*X-overwrites-A*/ X->AppendSibling(B); }
struct_member(X) ::= declarator_no_fun(A). { X = A; /*X-overwrites-A*/ }
struct_member(X) ::= declarator(A). { X = A; /*X-overwrites-A*/ }
struct_member(X) ::= enum_def(A). { X = A; /*X-overwrites-A*/ }
struct_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ }
@ -844,14 +849,6 @@ declarator(X) ::= decl_flags(A) type_list_or_void(B) variables_or_function(C).
X = NULL;
}
}
declarator_no_fun(X) ::= decl_flags(A) type(B) variable_list(C) SEMICOLON.
{
NEW_AST_NODE(VarDeclarator, decl, A != nullptr? A->SourceLoc : B->SourceLoc);
decl->Type = B;
decl->Names = C;
decl->Flags = A == nullptr? 0 : A->Flags;
X = decl;
}
// Need to split it up like this to avoid parsing conflicts.
variables_or_function(X) ::= IDENTIFIER(A) LPAREN func_params(B) RPAREN func_const(C) opt_func_body(D). /* Function */

View file

@ -196,6 +196,10 @@ void ZCCCompiler::ProcessStruct(ZCC_Struct *cnode, PSymbolTreeNode *treenode, ZC
cls->Fields.Push(static_cast<ZCC_VarDeclarator *>(node));
break;
case AST_FuncDeclarator:
cls->Functions.Push(static_cast<ZCC_FuncDeclarator *>(node));
break;
case AST_EnumTerminator:
enumType = nullptr;
break;
@ -411,7 +415,7 @@ void ZCCCompiler::CreateStructTypes()
{
for(auto s : Structs)
{
s->Outer = s->OuterDef == nullptr? nullptr : s->OuterDef->Type;
s->Outer = s->OuterDef == nullptr? nullptr : s->OuterDef->CType();
s->strct->Type = NewStruct(s->NodeName(), s->Outer);
s->strct->Symbol = new PSymbolType(s->NodeName(), s->Type());
GlobalSymbols.AddSymbol(s->strct->Symbol);
@ -1989,6 +1993,331 @@ void ZCCCompiler::InitDefaults()
}
}
void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool forclass)
{
TArray<PType *> rets(1);
TArray<PType *> args;
TArray<uint32_t> argflags;
TArray<VMValue> argdefaults;
TArray<FName> argnames;
rets.Clear();
args.Clear();
argflags.Clear();
bool hasdefault = false;
// For the time being, let's not allow overloading. This may be reconsidered later but really just adds an unnecessary amount of complexity here.
if (AddTreeNode(f->Name, f, &c->TreeNodes, false))
{
auto t = f->Type;
if (t != nullptr)
{
do
{
auto type = DetermineType(c->Type(), f, f->Name, t, false, false);
if (type->IsKindOf(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3)
{
// structs and classes only get passed by pointer.
type = NewPointer(type);
}
// TBD: disallow certain types? For now, let everything pass that isn't an array.
rets.Push(type);
t = static_cast<decltype(t)>(t->SiblingNext);
} while (t != f->Type);
}
int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_FuncConst | ZCC_Abstract;
if (f->Flags & notallowed)
{
Error(f, "Invalid qualifiers for %s (%s not allowed)", FName(f->Name).GetChars(), FlagsToString(f->Flags & notallowed).GetChars());
f->Flags &= notallowed;
}
uint32_t varflags = VARF_Method;
int implicitargs = 1;
AFuncDesc *afd = nullptr;
int useflags = SUF_ACTOR | SUF_OVERLAY | SUF_WEAPON | SUF_ITEM;
if (f->UseFlags != nullptr)
{
useflags = 0;
auto p = f->UseFlags;
do
{
switch (p->Id)
{
case NAME_Actor:
useflags |= SUF_ACTOR;
break;
case NAME_Overlay:
useflags |= SUF_OVERLAY;
break;
case NAME_Weapon:
useflags |= SUF_WEAPON;
break;
case NAME_Item:
useflags |= SUF_ITEM;
break;
default:
Error(p, "Unknown Action qualifier %s", FName(p->Id).GetChars());
break;
}
p = static_cast<decltype(p)>(p->SiblingNext);
} while (p != f->UseFlags);
}
// map to implementation flags.
if (f->Flags & ZCC_Private) varflags |= VARF_Private;
if (f->Flags & ZCC_Protected) varflags |= VARF_Protected;
if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual;
if (f->Flags & ZCC_Override) varflags |= VARF_Override;
if (f->Flags & ZCC_Action)
{
// Non-Actors cannot have action functions.
if (!c->Type()->IsKindOf(RUNTIME_CLASS(PClassActor)))
{
Error(f, "'Action' can only be used in child classes of Actor");
}
varflags |= VARF_Final; // Action implies Final.
if (useflags & (SUF_OVERLAY | SUF_WEAPON | SUF_ITEM))
{
varflags |= VARF_Action;
implicitargs = 3;
}
else
{
implicitargs = 1;
}
}
if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final.
if (varflags & VARF_Override) varflags &= ~VARF_Virtual; // allow 'virtual override'.
// Only one of these flags may be used.
static int exclude[] = { ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static };
static const char * print[] = { "virtual", "override", "action", "static" };
int fc = 0;
FString build;
for (int i = 0; i < 4; i++)
{
if (f->Flags & exclude[i])
{
fc++;
if (build.Len() > 0) build += ", ";
build += print[i];
}
}
if (fc > 1)
{
Error(f, "Invalid combination of qualifiers %s on function %s.", FName(f->Name).GetChars(), build.GetChars());
varflags |= VARF_Method;
}
if (varflags & VARF_Override) varflags |= VARF_Virtual; // Now that the flags are checked, make all override functions virtual as well.
if (f->Flags & ZCC_Native)
{
varflags |= VARF_Native;
afd = FindFunction(c->Type(), FName(f->Name).GetChars());
if (afd == nullptr)
{
Error(f, "The function '%s' has not been exported from the executable.", FName(f->Name).GetChars());
}
else
{
(*afd->VMPointer)->ImplicitArgs = BYTE(implicitargs);
}
}
SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags, useflags);
argdefaults.Resize(argnames.Size());
auto p = f->Params;
bool hasoptionals = false;
if (p != nullptr)
{
do
{
int elementcount = 1;
VMValue vmval[3]; // default is REGT_NIL which means 'no default value' here.
if (p->Type != nullptr)
{
auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false);
int flags = 0;
if (p->Flags & ZCC_In) flags |= VARF_In;
if (p->Flags & ZCC_Out) flags |= VARF_Out;
if ((type->IsA(RUNTIME_CLASS(PStruct))) || (flags & VARF_Out))
{
// 'out' parameters and all structs except vectors are passed by reference
if ((flags & VARF_Out) || (type != TypeVector2 && type != TypeVector3))
{
type = NewPointer(type);
flags |= VARF_Ref;
}
else if (type == TypeVector2)
{
elementcount = 2;
}
else if (type == TypeVector3)
{
elementcount = 3;
}
}
if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3)
{
Error(p, "Invalid type %s for function parameter", type->DescriptiveName());
}
else if (p->Default != nullptr)
{
flags |= VARF_Optional;
hasoptionals = true;
// The simplifier is not suited to convert the constant into something usable.
// All it does is reduce the expression to a constant but we still got to do proper type checking and conversion.
// It will also lose important type info about enums, once these get implemented
// The code generator can do this properly for us.
FxExpression *x = new FxTypeCast(ConvertNode(p->Default), type, false);
FCompileContext ctx(c->Type(), false);
x = x->Resolve(ctx);
if (x != nullptr)
{
// Vectors need special treatment because they use more than one entry in the Defaults and do not report as actual constants
if (type == TypeVector2 && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(2))
{
auto vx = static_cast<FxVectorValue *>(x);
vmval[0] = static_cast<FxConstant *>(vx->xyz[0])->GetValue().GetFloat();
vmval[1] = static_cast<FxConstant *>(vx->xyz[1])->GetValue().GetFloat();
}
else if (type == TypeVector3 && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(3))
{
auto vx = static_cast<FxVectorValue *>(x);
vmval[0] = static_cast<FxConstant *>(vx->xyz[0])->GetValue().GetFloat();
vmval[1] = static_cast<FxConstant *>(vx->xyz[1])->GetValue().GetFloat();
vmval[2] = static_cast<FxConstant *>(vx->xyz[2])->GetValue().GetFloat();
}
else if (!x->isConstant())
{
Error(p, "Default parameter %s is not constant in %s", FName(p->Name).GetChars(), FName(f->Name).GetChars());
}
else if (x->ValueType != type)
{
Error(p, "Default parameter %s could not be converted to target type %s", FName(p->Name).GetChars(), c->Type()->TypeName.GetChars());
}
else
{
auto cnst = static_cast<FxConstant *>(x);
hasdefault = true;
switch (type->GetRegType())
{
case REGT_INT:
vmval[0] = cnst->GetValue().GetInt();
break;
case REGT_FLOAT:
vmval[0] = cnst->GetValue().GetFloat();
break;
case REGT_POINTER:
if (type->IsKindOf(RUNTIME_CLASS(PClassPointer)))
vmval[0] = (DObject*)cnst->GetValue().GetPointer();
else
vmval[0] = cnst->GetValue().GetPointer();
break;
case REGT_STRING:
vmval[0] = cnst->GetValue().GetString();
break;
default:
assert(0 && "no valid type for constant");
break;
}
}
}
if (x != nullptr) delete x;
}
else if (hasoptionals)
{
Error(p, "All arguments after the first optional one need also be optional.");
}
// TBD: disallow certain types? For now, let everything pass that isn't an array.
args.Push(type);
argflags.Push(flags);
argnames.Push(p->Name);
}
else
{
args.Push(nullptr);
argflags.Push(0);
argnames.Push(NAME_None);
}
for (int i = 0; i<elementcount; i++) argdefaults.Push(vmval[i]);
p = static_cast<decltype(p)>(p->SiblingNext);
} while (p != f->Params);
}
PFunction *sym = new PFunction(c->Type(), f->Name);
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr ? nullptr : *(afd->VMPointer), varflags, useflags);
c->Type()->Symbols.ReplaceSymbol(sym);
if (!(f->Flags & ZCC_Native))
{
auto code = ConvertAST(c->Type(), f->Body);
if (code != nullptr)
{
sym->Variants[0].Implementation = FunctionBuildList.AddFunction(sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
}
}
if (sym->Variants[0].Implementation != nullptr && hasdefault) // do not copy empty default lists, they only waste space and processing time.
{
sym->Variants[0].Implementation->DefaultArgs = std::move(argdefaults);
}
if (varflags & VARF_Virtual)
{
if (varflags & VARF_Final)
{
sym->Variants[0].Implementation->Final = true;
}
if (forclass)
{
PClass *clstype = static_cast<PClass *>(c->Type());
int vindex = clstype->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto);
// specifying 'override' is necessary to prevent one of the biggest problem spots with virtual inheritance: Mismatching argument types.
if (varflags & VARF_Override)
{
if (vindex == -1)
{
Error(p, "Attempt to override non-existent virtual function %s", FName(f->Name).GetChars());
}
else
{
auto oldfunc = clstype->Virtuals[vindex];
if (oldfunc->Final)
{
Error(p, "Attempt to override final function %s", FName(f->Name).GetChars());
}
clstype->Virtuals[vindex] = sym->Variants[0].Implementation;
sym->Variants[0].Implementation->VirtualIndex = vindex;
}
}
else
{
if (vindex != -1)
{
Error(p, "Function %s attempts to override parent function without 'override' qualifier", FName(f->Name).GetChars());
}
sym->Variants[0].Implementation->VirtualIndex = clstype->Virtuals.Push(sym->Variants[0].Implementation);
}
}
else
{
Error(p, "Virtual functions can only be defined for classes");
}
}
}
}
//==========================================================================
//
// Parses the functions list
@ -1997,11 +2326,13 @@ void ZCCCompiler::InitDefaults()
void ZCCCompiler::InitFunctions()
{
TArray<PType *> rets(1);
TArray<PType *> args;
TArray<uint32_t> argflags;
TArray<VMValue> argdefaults;
TArray<FName> argnames;
for (auto s : Structs)
{
for (auto f : s->Functions)
{
CompileFunction(s, f, false);
}
}
for (auto c : Classes)
{
@ -2017,306 +2348,7 @@ void ZCCCompiler::InitFunctions()
}
for (auto f : c->Functions)
{
rets.Clear();
args.Clear();
argflags.Clear();
bool hasdefault = false;
// For the time being, let's not allow overloading. This may be reconsidered later but really just adds an unnecessary amount of complexity here.
if (AddTreeNode(f->Name, f, &c->TreeNodes, false))
{
auto t = f->Type;
if (t != nullptr)
{
do
{
auto type = DetermineType(c->Type(), f, f->Name, t, false, false);
if (type->IsKindOf(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3)
{
// structs and classes only get passed by pointer.
type = NewPointer(type);
}
// TBD: disallow certain types? For now, let everything pass that isn't an array.
rets.Push(type);
t = static_cast<decltype(t)>(t->SiblingNext);
} while (t != f->Type);
}
int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_FuncConst | ZCC_Abstract;
if (f->Flags & notallowed)
{
Error(f, "Invalid qualifiers for %s (%s not allowed)", FName(f->Name).GetChars(), FlagsToString(f->Flags & notallowed).GetChars());
f->Flags &= notallowed;
}
uint32_t varflags = VARF_Method;
int implicitargs = 1;
AFuncDesc *afd = nullptr;
int useflags = SUF_ACTOR | SUF_OVERLAY | SUF_WEAPON | SUF_ITEM;
if (f->UseFlags != nullptr)
{
useflags = 0;
auto p = f->UseFlags;
do
{
switch (p->Id)
{
case NAME_Actor:
useflags |= SUF_ACTOR;
break;
case NAME_Overlay:
useflags |= SUF_OVERLAY;
break;
case NAME_Weapon:
useflags |= SUF_WEAPON;
break;
case NAME_Item:
useflags |= SUF_ITEM;
break;
default:
Error(p, "Unknown Action qualifier %s", FName(p->Id).GetChars());
break;
}
p = static_cast<decltype(p)>(p->SiblingNext);
} while (p != f->UseFlags);
}
// map to implementation flags.
if (f->Flags & ZCC_Private) varflags |= VARF_Private;
if (f->Flags & ZCC_Protected) varflags |= VARF_Protected;
if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual;
if (f->Flags & ZCC_Override) varflags |= VARF_Override;
if (f->Flags & ZCC_Action)
{
varflags |= VARF_Final; // Action implies Final.
if (useflags & (SUF_OVERLAY | SUF_WEAPON | SUF_ITEM))
{
varflags |= VARF_Action;
implicitargs = 3;
}
else
{
implicitargs = 1;
}
}
if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final.
if (varflags & VARF_Override) varflags &= ~VARF_Virtual; // allow 'virtual override'.
// Only one of these flags may be used.
static int exclude[] = { ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static };
static const char * print[] = { "virtual", "override", "action", "static" };
int fc = 0;
FString build;
for (int i = 0; i < 4; i++)
{
if (f->Flags & exclude[i])
{
fc++;
if (build.Len() > 0) build += ", ";
build += print[i];
}
}
if (fc > 1)
{
Error(f, "Invalid combination of qualifiers %s on function %s.", FName(f->Name).GetChars(), build.GetChars() );
varflags |= VARF_Method;
}
if (varflags & VARF_Override) varflags |= VARF_Virtual; // Now that the flags are checked, make all override functions virtual as well.
if (f->Flags & ZCC_Native)
{
varflags |= VARF_Native;
afd = FindFunction(c->Type(), FName(f->Name).GetChars());
if (afd == nullptr)
{
Error(f, "The function '%s' has not been exported from the executable.", FName(f->Name).GetChars());
}
else
{
(*afd->VMPointer)->ImplicitArgs = BYTE(implicitargs);
}
}
SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags, useflags);
argdefaults.Resize(argnames.Size());
auto p = f->Params;
bool hasoptionals = false;
if (p != nullptr)
{
do
{
int elementcount = 1;
VMValue vmval[3]; // default is REGT_NIL which means 'no default value' here.
if (p->Type != nullptr)
{
auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false);
int flags = 0;
if (p->Flags & ZCC_In) flags |= VARF_In;
if (p->Flags & ZCC_Out) flags |= VARF_Out;
if ((type->IsA(RUNTIME_CLASS(PStruct))) || (flags & VARF_Out))
{
// 'out' parameters and all structs except vectors are passed by reference
if ((flags & VARF_Out) || (type != TypeVector2 && type != TypeVector3))
{
type = NewPointer(type);
flags |= VARF_Ref;
}
else if (type == TypeVector2)
{
elementcount = 2;
}
else if (type == TypeVector3)
{
elementcount = 3;
}
}
if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3)
{
Error(p, "Invalid type %s for function parameter", type->DescriptiveName());
}
else if (p->Default != nullptr)
{
flags |= VARF_Optional;
hasoptionals = true;
// The simplifier is not suited to convert the constant into something usable.
// All it does is reduce the expression to a constant but we still got to do proper type checking and conversion.
// It will also lose important type info about enums, once these get implemented
// The code generator can do this properly for us.
FxExpression *x = new FxTypeCast(ConvertNode(p->Default), type, false);
FCompileContext ctx(c->Type(), false);
x = x->Resolve(ctx);
if (x != nullptr)
{
// Vectors need special treatment because they use more than one entry in the Defaults and do not report as actual constants
if (type == TypeVector2 && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(2))
{
auto vx = static_cast<FxVectorValue *>(x);
vmval[0] = static_cast<FxConstant *>(vx->xyz[0])->GetValue().GetFloat();
vmval[1] = static_cast<FxConstant *>(vx->xyz[1])->GetValue().GetFloat();
}
else if (type == TypeVector3 && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(3))
{
auto vx = static_cast<FxVectorValue *>(x);
vmval[0] = static_cast<FxConstant *>(vx->xyz[0])->GetValue().GetFloat();
vmval[1] = static_cast<FxConstant *>(vx->xyz[1])->GetValue().GetFloat();
vmval[2] = static_cast<FxConstant *>(vx->xyz[2])->GetValue().GetFloat();
}
else if (!x->isConstant())
{
Error(p, "Default parameter %s is not constant in %s", FName(p->Name).GetChars(), FName(f->Name).GetChars());
}
else if (x->ValueType != type)
{
Error(p, "Default parameter %s could not be converted to target type %s", FName(p->Name).GetChars(), c->Type()->TypeName.GetChars());
}
else
{
auto cnst = static_cast<FxConstant *>(x);
hasdefault = true;
switch (type->GetRegType())
{
case REGT_INT:
vmval[0] = cnst->GetValue().GetInt();
break;
case REGT_FLOAT:
vmval[0] = cnst->GetValue().GetFloat();
break;
case REGT_POINTER:
if (type->IsKindOf(RUNTIME_CLASS(PClassPointer)))
vmval[0] = (DObject*)cnst->GetValue().GetPointer();
else
vmval[0] = cnst->GetValue().GetPointer();
break;
case REGT_STRING:
vmval[0] = cnst->GetValue().GetString();
break;
default:
assert(0 && "no valid type for constant");
break;
}
}
}
if (x != nullptr) delete x;
}
else if (hasoptionals)
{
Error(p, "All arguments after the first optional one need also be optional.");
}
// TBD: disallow certain types? For now, let everything pass that isn't an array.
args.Push(type);
argflags.Push(flags);
argnames.Push(p->Name);
}
else
{
args.Push(nullptr);
argflags.Push(0);
argnames.Push(NAME_None);
}
for(int i=0;i<elementcount;i++) argdefaults.Push(vmval[i]);
p = static_cast<decltype(p)>(p->SiblingNext);
} while (p != f->Params);
}
PFunction *sym = new PFunction(c->Type(), f->Name);
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr? nullptr : *(afd->VMPointer), varflags, useflags);
c->Type()->Symbols.ReplaceSymbol(sym);
if (!(f->Flags & ZCC_Native))
{
auto code = ConvertAST(c->Type(), f->Body);
if (code != nullptr)
{
sym->Variants[0].Implementation = FunctionBuildList.AddFunction(sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
}
}
if (sym->Variants[0].Implementation != nullptr && hasdefault) // do not copy empty default lists, they only waste space and processing time.
{
sym->Variants[0].Implementation->DefaultArgs = std::move(argdefaults);
}
if (varflags & VARF_Virtual)
{
if (varflags & VARF_Final)
{
sym->Variants[0].Implementation->Final = true;
}
int vindex = c->Type()->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto);
// specifying 'override' is necessary to prevent one of the biggest problem spots with virtual inheritance: Mismatching argument types.
if (varflags & VARF_Override)
{
if (vindex == -1)
{
Error(p, "Attempt to override non-existent virtual function %s", FName(f->Name).GetChars());
}
else
{
auto oldfunc = c->Type()->Virtuals[vindex];
if (oldfunc->Final)
{
Error(p, "Attempt to override final function %s", FName(f->Name).GetChars());
}
c->Type()->Virtuals[vindex] = sym->Variants[0].Implementation;
sym->Variants[0].Implementation->VirtualIndex = vindex;
}
}
else
{
if (vindex != -1)
{
Error(p, "Function %s attempts to override parent function without 'override' qualifier", FName(f->Name).GetChars());
}
sym->Variants[0].Implementation->VirtualIndex = c->Type()->Virtuals.Push(sym->Variants[0].Implementation);
}
}
}
CompileFunction(c, f, true);
}
}
}
@ -2368,7 +2400,9 @@ FxExpression *ZCCCompiler::SetupActionFunction(PClass *cls, ZCC_TreeNode *af)
{
FArgumentList argumentlist;
// We can use this function directly without wrapping it in a caller.
if ((afd->Variants[0].Flags & VARF_Action) || !cls->IsDescendantOf(RUNTIME_CLASS(AStateProvider)) || !afd->Variants[0].SelfClass->IsDescendantOf(RUNTIME_CLASS(AStateProvider)))
auto selfclass = dyn_cast<PClass>(afd->Variants[0].SelfClass);
assert(selfclass != nullptr); // non classes are not supposed to get here.
if ((afd->Variants[0].Flags & VARF_Action) || !cls->IsDescendantOf(RUNTIME_CLASS(AStateProvider)) || !selfclass->IsDescendantOf(RUNTIME_CLASS(AStateProvider)))
{
return new FxVMFunctionCall(new FxSelf(*af), afd, argumentlist, *af, false);
}
@ -2663,7 +2697,7 @@ void ZCCCompiler::CompileStates()
//
//==========================================================================
FxExpression *ZCCCompiler::ConvertAST(PClass *cls, ZCC_TreeNode *ast)
FxExpression *ZCCCompiler::ConvertAST(PStruct *cls, ZCC_TreeNode *ast)
{
ConvertClass = cls;
// there are two possibilities here: either a single function call or a compound statement. For a compound statement we also need to check if the last thing added was a return.

View file

@ -20,6 +20,11 @@ struct ZCC_StructWork
TArray<ZCC_Enum *> Enums;
TArray<ZCC_ConstantDef *> Constants;
TArray<ZCC_VarDeclarator *> Fields;
TArray<ZCC_FuncDeclarator *> Functions;
ZCC_StructWork()
{
}
ZCC_StructWork(ZCC_Struct * s, PSymbolTreeNode *n, ZCC_Class *outer)
{
@ -41,34 +46,25 @@ struct ZCC_StructWork
};
struct ZCC_ClassWork
struct ZCC_ClassWork : public ZCC_StructWork
{
ZCC_Class *cls;
PSymbolTable TreeNodes;
PSymbolTreeNode *node;
TArray<ZCC_Enum *> Enums;
TArray<ZCC_ConstantDef *> Constants;
TArray<ZCC_VarDeclarator *> Fields;
TArray<ZCC_Default *> Defaults;
TArray<ZCC_FuncDeclarator *> Functions;
TArray<ZCC_States *> States;
ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n)
{
strct = s;
cls = s;
node = n;
}
FName NodeName() const
{
return cls->NodeName;
OuterDef = nullptr;
Outer = nullptr;
}
PClass *Type()
{
return cls->Type;
return static_cast<PClass *>(strct->Type);
}
};
struct ZCC_ConstantWork
@ -108,6 +104,7 @@ private:
int GetInt(ZCC_Expression *expr);
double GetDouble(ZCC_Expression *expr);
const char *GetString(ZCC_Expression *expr, bool silent = false);
void CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool forclass);
void InitFunctions();
void CompileStates();
@ -142,12 +139,12 @@ private:
void Error(ZCC_TreeNode *node, const char *msg, ...);
void MessageV(ZCC_TreeNode *node, const char *txtcolor, const char *msg, va_list argptr);
FxExpression *ConvertAST(PClass *cclass, ZCC_TreeNode *ast);
FxExpression *ConvertAST(PStruct *cclass, ZCC_TreeNode *ast);
FxExpression *ConvertNode(ZCC_TreeNode *node);
FArgumentList &ConvertNodeList(FArgumentList &, ZCC_TreeNode *head);
DObject *Outer;
PClass *ConvertClass; // class type to be used when resoving symbold while converting an AST
PStruct *ConvertClass; // class type to be used when resoving symbols while converting an AST
PSymbolTable *GlobalTreeNodes;
PSymbolTable *OutputSymbols;
ZCC_AST &AST;

View file

@ -202,19 +202,19 @@ struct ZCC_NamedNode : ZCC_TreeNode
PSymbolType *Symbol;
};
struct ZCC_Class : ZCC_NamedNode
struct ZCC_Struct : ZCC_NamedNode
{
VM_UWORD Flags;
ZCC_TreeNode *Body;
PStruct *Type;
};
struct ZCC_Class : ZCC_Struct
{
ZCC_Identifier *ParentName;
ZCC_Identifier *Replaces;
VM_UWORD Flags;
ZCC_TreeNode *Body;
PClass *Type;
};
struct ZCC_Struct : ZCC_NamedNode
{
ZCC_TreeNode *Body;
PStruct *Type;
PClass *CType() { return static_cast<PClass *>(Type); }
};
struct ZCC_Enum : ZCC_NamedNode