- 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
parent df4e9324c9
commit f11f020b6c
11 changed files with 453 additions and 370 deletions

View File

@ -2334,6 +2334,24 @@ PStruct *NewStruct(FName name, PTypeBase *outer)
return static_cast<PStruct *>(stype);
}
/* PNativeStruct ****************************************************************/
IMPLEMENT_CLASS(PNativeStruct, false, false, false, false)
//==========================================================================
//
// PNativeStruct - Parameterized Constructor
//
//==========================================================================
PNativeStruct::PNativeStruct(FName name)
: PStruct(name, nullptr)
{
mDescriptiveName.Format("NativeStruct<%s>", name.GetChars());
Size = 0;
HasNativeFields = true;
}
/* PField *****************************************************************/
IMPLEMENT_CLASS(PField, false, false, false, false)

View File

@ -694,6 +694,15 @@ protected:
PStruct();
};
// a native struct will always be abstract and cannot be instantiated. All variables are references.
// In addition, native structs can have methods (but no virtual ones.)
class PNativeStruct : public PStruct
{
DECLARE_CLASS(PNativeStruct, PStruct);
public:
PNativeStruct(FName name = NAME_None);
};
class PPrototype : public PCompoundType
{
DECLARE_CLASS(PPrototype, PCompoundType);
@ -723,10 +732,10 @@ public:
TArray<FName> ArgNames; // we need the names to access them later when the function gets compiled.
uint32_t Flags;
int UseFlags;
PClass *SelfClass;
PStruct *SelfClass;
};
TArray<Variant> Variants;
PClass *OwningClass = nullptr;
PStruct *OwningClass = nullptr;
unsigned AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags, int useflags);
int GetImplicitArgs()
@ -738,7 +747,7 @@ public:
size_t PropagateMark();
PFunction(PClass *owner = nullptr, FName name = NAME_None) : PSymbol(name), OwningClass(owner) {}
PFunction(PStruct *owner = nullptr, FName name = NAME_None) : PSymbol(name), OwningClass(owner) {}
};
// Meta-info for every class derived from DObject ---------------------------
@ -749,9 +758,9 @@ enum
};
class PClassClass;
class PClass : public PStruct
class PClass : public PNativeStruct
{
DECLARE_CLASS(PClass, PStruct);
DECLARE_CLASS(PClass, PNativeStruct);
HAS_OBJECT_POINTERS;
protected:
// We unravel _WITH_META here just as we did for PType.

View File

@ -101,7 +101,7 @@ FCompileContext::FCompileContext(PFunction *fnc, PPrototype *ret, bool fromdecor
if (fnc != nullptr) Class = fnc->OwningClass;
}
FCompileContext::FCompileContext(PClass *cls, bool fromdecorate)
FCompileContext::FCompileContext(PStruct *cls, bool fromdecorate)
: ReturnProto(nullptr), Function(nullptr), Class(cls), FromDecorate(fromdecorate), StateIndex(-1), StateCount(0), Lump(-1)
{
}
@ -5335,7 +5335,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
delete this;
return nullptr;
}
if (!ctx.Function->Variants[0].SelfClass->IsDescendantOf(RUNTIME_CLASS(AActor)))
if (!ctx.Function->Variants[0].SelfClass->IsKindOf(RUNTIME_CLASS(PClassActor)))
{
ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type.");
delete this;
@ -5441,7 +5441,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
//
//==========================================================================
FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PClass *classctx, FxExpression *&object, PStruct *objtype)
FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classctx, FxExpression *&object, PStruct *objtype)
{
PSymbol *sym;
PSymbolTable *symtbl;
@ -6379,7 +6379,15 @@ static bool CheckFunctionCompatiblity(FScriptPosition &ScriptPosition, PFunction
{
auto callingself = caller->Variants[0].SelfClass;
auto calledself = callee->Variants[0].SelfClass;
if (!callingself->IsDescendantOf(calledself))
bool match = (callingself == calledself);
if (!match)
{
auto callingselfcls = dyn_cast<PClass>(caller->Variants[0].SelfClass);
auto calledselfcls = dyn_cast<PClass>(callee->Variants[0].SelfClass);
match = callingselfcls != nullptr && calledselfcls != nullptr && callingselfcls->IsDescendantOf(calledselfcls);
}
if (!match)
{
ScriptPosition.Message(MSG_ERROR, "Call to member function %s with incompatible self pointer.", callee->SymbolName.GetChars());
return false;
@ -6717,11 +6725,19 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
if (Self->ExprType == EFX_Super)
{
// give the node the proper value type now that we know it's properly used.
cls = ctx.Function->Variants[0].SelfClass->ParentClass;
Self->ValueType = NewPointer(cls);
Self->ExprType = EFX_Self;
novirtual = true; // super calls are always non-virtual
auto clstype = dyn_cast<PClass>(ctx.Function->Variants[0].SelfClass);
if (clstype != nullptr)
{
// give the node the proper value type now that we know it's properly used.
cls = clstype->ParentClass;
Self->ValueType = NewPointer(cls);
Self->ExprType = EFX_Self;
novirtual = true; // super calls are always non-virtual
}
else
{
ScriptPosition.Message(MSG_ERROR, "Super requires a class type");
}
}
if (Self->IsVector())
@ -6775,7 +6791,8 @@ isresolved:
if (staticonly && (afd->Variants[0].Flags & VARF_Method))
{
if (!ctx.Class->IsDescendantOf(cls))
auto clstype = dyn_cast<PClass>(ctx.Class);
if (clstype == nullptr || !clstype->IsDescendantOf(cls))
{
ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here\n", cls->TypeName.GetChars(), MethodName.GetChars());
delete this;
@ -8876,14 +8893,22 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
CHECKRESOLVED();
ABORT(ctx.Class);
int symlabel;
auto clstype = dyn_cast<PClassActor>(ctx.Class);
if (names[0] == NAME_None)
{
scope = nullptr;
}
else if (clstype == nullptr)
{
// not in an actor, so any further checks are pointless.
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars());
delete this;
return nullptr;
}
else if (names[0] == NAME_Super)
{
scope = dyn_cast<PClassActor>(ctx.Class->ParentClass);
scope = dyn_cast<PClassActor>(clstype->ParentClass);
}
else
{
@ -8894,7 +8919,7 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
delete this;
return nullptr;
}
else if (!scope->IsAncestorOf(ctx.Class))
else if (!scope->IsAncestorOf(clstype))
{
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars());
delete this;

View File

@ -75,7 +75,7 @@ struct FCompileContext
FxCompoundStatement *Block = nullptr;
PPrototype *ReturnProto;
PFunction *Function; // The function that is currently being compiled (or nullptr for constant evaluation.)
PClass *Class; // The type of the owning class.
PStruct *Class; // The type of the owning class.
bool FromDecorate; // DECORATE must silence some warnings and demote some errors.
int StateIndex; // index in actor's state table for anonymous functions, otherwise -1 (not used by DECORATE which pre-resolves state indices)
int StateCount; // amount of states an anoymous function is being used on (must be 1 for state indices to be allowed.)
@ -84,7 +84,7 @@ struct FCompileContext
TDeletingArray<FxLocalVariableDeclaration *> FunctionArgs;
FCompileContext(PFunction *func, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump);
FCompileContext(PClass *cls, bool fromdecorate); // only to be used to resolve constants!
FCompileContext(PStruct *cls, bool fromdecorate); // only to be used to resolve constants!
PSymbol *FindInClass(FName identifier, PSymbolTable *&symt);
PSymbol *FindInSelfClass(FName identifier, PSymbolTable *&symt);
@ -346,7 +346,7 @@ public:
FxIdentifier(FName i, const FScriptPosition &p);
FxExpression *Resolve(FCompileContext&);
FxExpression *ResolveMember(FCompileContext&, PClass*, FxExpression*&, PStruct*);
FxExpression *ResolveMember(FCompileContext&, PStruct*, FxExpression*&, PStruct*);
};

View File

@ -104,7 +104,7 @@ FScriptPosition & GetStateSource(FState *state)
//
//==========================================================================
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PClass *cls, DWORD funcflags, int useflags)
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PStruct *cls, DWORD funcflags, int useflags)
{
// Must be called before adding any other arguments.
assert(args == nullptr || args->Size() == 0);
@ -182,7 +182,7 @@ PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, i
//
//==========================================================================
PFunction *FindClassMemberFunction(PClass *selfcls, PClass *funccls, FName name, FScriptPosition &sc, bool *error)
PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName name, FScriptPosition &sc, bool *error)
{
// Skip ACS_NamedExecuteWithResult. Anything calling this should use the builtin instead.
if (name == NAME_ACS_NamedExecuteWithResult) return nullptr;

View File

@ -146,7 +146,7 @@ inline void ResetBaggage (Baggage *bag, PClassActor *stateclass)
//
//==========================================================================
AFuncDesc *FindFunction(PClass *cls, const char * string);
AFuncDesc *FindFunction(PStruct *cls, const char * string);
FxExpression *ParseExpression(FScanner &sc, PClassActor *cls, bool mustresolve = false);
@ -156,9 +156,9 @@ void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression
FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret);
class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag);
FName CheckCastKludges(FName in);
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PClass *cls, DWORD funcflags, int useflags);
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PStruct *cls, DWORD funcflags, int useflags);
PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags);
PFunction *FindClassMemberFunction(PClass *cls, PClass *funccls, FName name, FScriptPosition &sc, bool *error);
PFunction *FindClassMemberFunction(PStruct *cls, PStruct *funccls, FName name, FScriptPosition &sc, bool *error);
void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate);
//==========================================================================

View File

@ -577,11 +577,14 @@ FPropertyInfo *FindProperty(const char * string)
//
//==========================================================================
AFuncDesc *FindFunction(PClass *cls, const char * string)
AFuncDesc *FindFunction(PStruct *cls, const char * string)
{
for (int i = 0; i < 2; i++)
{
if (i == 1 && !cls->IsDescendantOf(RUNTIME_CLASS(AActor))) break;
// Since many functions have been declared with Actor as owning class, despited being members of something else, let's hack around this until they have been fixed or exported.
// Since most of these are expected to be scriptified anyway, there's no point fixing them all before they get exported.
if (i == 1 && !cls->IsKindOf(RUNTIME_CLASS(PClassActor))) break;
FStringf fullname("%s_%s", i == 0 ? cls->TypeName.GetChars() : "Actor", string);
int min = 0, max = AFTable.Size() - 1;

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