From f11f020b6cb361ee7a8e88d38f9feb6e95583676 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 19 Nov 2016 12:12:29 +0100 Subject: [PATCH] - 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. --- src/dobjtype.cpp | 18 + src/dobjtype.h | 19 +- src/scripting/codegeneration/codegen.cpp | 49 +- src/scripting/codegeneration/codegen.h | 6 +- src/scripting/thingdef.cpp | 4 +- src/scripting/thingdef.h | 6 +- src/scripting/thingdef_data.cpp | 7 +- src/scripting/zscript/zcc-parse.lemon | 17 +- src/scripting/zscript/zcc_compile.cpp | 650 ++++++++++++----------- src/scripting/zscript/zcc_compile.h | 29 +- src/scripting/zscript/zcc_parser.h | 18 +- 11 files changed, 453 insertions(+), 370 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 32fd30c67..682cc0fd5 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -2334,6 +2334,24 @@ PStruct *NewStruct(FName name, PTypeBase *outer) return static_cast(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) diff --git a/src/dobjtype.h b/src/dobjtype.h index bd6483258..aeb042ebb 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -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 ArgNames; // we need the names to access them later when the function gets compiled. uint32_t Flags; int UseFlags; - PClass *SelfClass; + PStruct *SelfClass; }; TArray Variants; - PClass *OwningClass = nullptr; + PStruct *OwningClass = nullptr; unsigned AddVariant(PPrototype *proto, TArray &argflags, TArray &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. diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index c58167bab..a7be26f29 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -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(caller->Variants[0].SelfClass); + auto calledselfcls = dyn_cast(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(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(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(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(ctx.Class->ParentClass); + scope = dyn_cast(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; diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index 66a0137b1..5ad351d6b 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -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 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*); }; diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 7f0d98846..9dcf3a16f 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -104,7 +104,7 @@ FScriptPosition & GetStateSource(FState *state) // //========================================================================== -void SetImplicitArgs(TArray *args, TArray *argflags, TArray *argnames, PClass *cls, DWORD funcflags, int useflags) +void SetImplicitArgs(TArray *args, TArray *argflags, TArray *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; diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index a2c224558..5bad10c36 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -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 *args, TArray *argflags, TArray *argnames, PClass *cls, DWORD funcflags, int useflags); +void SetImplicitArgs(TArray *args, TArray *argflags, TArray *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); //========================================================================== diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index f2fec2972..76770272d 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -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; diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index c88cab481..2d3605532 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -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 */ diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 5dd430f9a..e50e8b822 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -196,6 +196,10 @@ void ZCCCompiler::ProcessStruct(ZCC_Struct *cnode, PSymbolTreeNode *treenode, ZC cls->Fields.Push(static_cast(node)); break; + case AST_FuncDeclarator: + cls->Functions.Push(static_cast(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 rets(1); + TArray args; + TArray argflags; + TArray argdefaults; + TArray 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(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(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(x)->isConstVector(2)) + { + auto vx = static_cast(x); + vmval[0] = static_cast(vx->xyz[0])->GetValue().GetFloat(); + vmval[1] = static_cast(vx->xyz[1])->GetValue().GetFloat(); + } + else if (type == TypeVector3 && x->ExprType == EFX_VectorValue && static_cast(x)->isConstVector(3)) + { + auto vx = static_cast(x); + vmval[0] = static_cast(vx->xyz[0])->GetValue().GetFloat(); + vmval[1] = static_cast(vx->xyz[1])->GetValue().GetFloat(); + vmval[2] = static_cast(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(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(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(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 rets(1); - TArray args; - TArray argflags; - TArray argdefaults; - TArray 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(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(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(x)->isConstVector(2)) - { - auto vx = static_cast(x); - vmval[0] = static_cast(vx->xyz[0])->GetValue().GetFloat(); - vmval[1] = static_cast(vx->xyz[1])->GetValue().GetFloat(); - } - else if (type == TypeVector3 && x->ExprType == EFX_VectorValue && static_cast(x)->isConstVector(3)) - { - auto vx = static_cast(x); - vmval[0] = static_cast(vx->xyz[0])->GetValue().GetFloat(); - vmval[1] = static_cast(vx->xyz[1])->GetValue().GetFloat(); - vmval[2] = static_cast(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(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(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(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. diff --git a/src/scripting/zscript/zcc_compile.h b/src/scripting/zscript/zcc_compile.h index 2e8840a46..84e314353 100644 --- a/src/scripting/zscript/zcc_compile.h +++ b/src/scripting/zscript/zcc_compile.h @@ -20,6 +20,11 @@ struct ZCC_StructWork TArray Enums; TArray Constants; TArray Fields; + TArray 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 Enums; - TArray Constants; - TArray Fields; TArray Defaults; - TArray Functions; TArray 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(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; diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 070bef449..dd777eb6c 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -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(Type); } }; struct ZCC_Enum : ZCC_NamedNode