From 466ed4e8f2782fe2cff6c1c04d04602be165f68e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 11 Apr 2020 19:37:15 +0200 Subject: [PATCH] - separated the Doom specific parts of the ZScript parser from the core into a subclass. --- src/CMakeLists.txt | 16 +- src/scripting/thingdef.cpp | 30 + src/scripting/thingdef.h | 4 - src/scripting/vmthunks.cpp | 18 - src/scripting/zscript/zcc_compile.cpp | 1021 +----------------- src/scripting/zscript/zcc_compile.h | 9 +- src/scripting/zscript/zcc_compile_doom.cpp | 1100 ++++++++++++++++++++ src/scripting/zscript/zcc_compile_doom.h | 34 + src/scripting/zscript/zcc_parser.cpp | 41 +- src/scripting/zscript/zcc_parser.h | 3 + 10 files changed, 1216 insertions(+), 1060 deletions(-) create mode 100644 src/scripting/zscript/zcc_compile_doom.cpp create mode 100644 src/scripting/zscript/zcc_compile_doom.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 48a0a99f02e..c06ae3eed5a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -207,24 +207,17 @@ endif() # Decide on SSE setup -set( SSE_MATTERS NO ) - -# with global use of SSE 2 we do not need special handling for selected files -if (NOT ZDOOM_USE_SSE2) - # SSE only matters on 32-bit targets. We check compiler flags to know if we can do it. - if( CMAKE_SIZEOF_VOID_P MATCHES "4" AND NOT CMAKE_OSX_ARCHITECTURES MATCHES ppc ) +# SSE only matters on 32-bit targets. We check compiler flags to know if we can do it. +if( CMAKE_SIZEOF_VOID_P MATCHES "4" AND NOT CMAKE_OSX_ARCHITECTURES MATCHES ppc ) CHECK_CXX_COMPILER_FLAG( "-msse2 -mfpmath=sse" CAN_DO_MFPMATH ) CHECK_CXX_COMPILER_FLAG( -arch:SSE2 CAN_DO_ARCHSSE2 ) if( CAN_DO_MFPMATH ) set( SSE1_ENABLE "-msse -mfpmath=sse" ) set( SSE2_ENABLE "-msse2 -mfpmath=sse" ) - set( SSE_MATTERS YES ) elseif( CAN_DO_ARCHSSE2 ) set( SSE1_ENABLE -arch:SSE ) set( SSE2_ENABLE -arch:SSE2 ) - set( SSE_MATTERS YES ) endif() - endif() endif() if( X64 ) @@ -1051,6 +1044,7 @@ set (PCH_SOURCES scripting/decorate/thingdef_states.cpp scripting/zscript/ast.cpp scripting/zscript/zcc_compile.cpp + scripting/zscript/zcc_compile_doom.cpp scripting/zscript/zcc_parser.cpp rendering/swrenderer/textures/r_swtexture.cpp rendering/swrenderer/textures/warptexture.cpp @@ -1254,6 +1248,7 @@ include_directories( . common/scripting/jit common/scripting/core common/scripting/interface + common/scripting/frontend common/scripting/backend g_statusbar console @@ -1272,6 +1267,7 @@ include_directories( . utility utility/nodebuilder scripting + scripting/zscript rendering rendering/vulkan/thirdparty ../libraries/gdtoa @@ -1453,6 +1449,8 @@ source_group("Common\\Objects" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/ source_group("Common\\Fonts" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/fonts/.+") source_group("Common\\File System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/.+") source_group("Common\\Scripting" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/.+") +source_group("Common\\Scripting\\Interface" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/interface/.+") +source_group("Common\\Scripting\\Backend" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/backend/.+") source_group("Common\\Scripting\\Core" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/core/.+") source_group("Common\\Scripting\\JIT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/jit/.+") source_group("Common\\Scripting\\VM" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/vm/.+") diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 0a5089ddff6..17811949b02 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -53,6 +53,8 @@ #include "stats.h" #include "info.h" #include "thingdef.h" +#include "zcc_parser.h" +#include "zcc_compile_doom.h" // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- void InitThingdef(); @@ -408,6 +410,34 @@ void ParseAllDecorate(); void SynthesizeFlagFields(); void SetDoomCompileEnvironment(); +void ParseScripts() +{ + int lump, lastlump = 0; + FScriptPosition::ResetErrorCounter(); + + while ((lump = fileSystem.FindLump("ZSCRIPT", &lastlump)) != -1) + { + ZCCParseState state; + auto newns = ParseOneScript(lump, state); + PSymbolTable symtable; + + ZCCDoomCompiler cc(state, NULL, symtable, newns, lump, state.ParseVersion); + cc.Compile(); + + if (FScriptPosition::ErrorCounter > 0) + { + // Abort if the compiler produced any errors. Also do not compile further lumps, because they very likely miss some stuff. + I_Error("%d errors, %d warnings while compiling %s", FScriptPosition::ErrorCounter, FScriptPosition::WarnCounter, fileSystem.GetFileFullPath(lump).GetChars()); + } + else if (FScriptPosition::WarnCounter > 0) + { + // If we got warnings, but no errors, print the information but continue. + Printf(TEXTCOLOR_ORANGE "%d warnings while compiling %s\n", FScriptPosition::WarnCounter, fileSystem.GetFileFullPath(lump).GetChars()); + } + + } +} + void LoadActors() { cycle_t timer; diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index 4ab496c4152..027d832fa81 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -191,10 +191,6 @@ inline void ResetBaggage (Baggage *bag, PClassActor *stateclass) // //========================================================================== -AFuncDesc *FindFunction(PContainerType *cls, const char * string); -FieldDesc *FindField(PContainerType *cls, const char * string); - - FxExpression *ParseExpression(FScanner &sc, PClassActor *cls, PNamespace *resolvenspc = nullptr); void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag); void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray &out_params, diff --git a/src/scripting/vmthunks.cpp b/src/scripting/vmthunks.cpp index b412273249f..c175312d248 100644 --- a/src/scripting/vmthunks.cpp +++ b/src/scripting/vmthunks.cpp @@ -1606,24 +1606,6 @@ DEFINE_ACTION_FUNCTION_NATIVE(FLevelLocals, ReplaceTextures, ReplaceTextures) return 0; } -//===================================================================================== -// -// textures -// -//===================================================================================== - -void SetCameraToTexture(AActor *viewpoint, const FString &texturename, double fov); - -DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, SetCameraToTexture, SetCameraToTexture) -{ - PARAM_PROLOGUE; - PARAM_OBJECT(viewpoint, AActor); - PARAM_STRING(texturename); // [ZZ] there is no point in having this as FTextureID because it's easier to refer to a cameratexture by name and it isn't executed too often to cache it. - PARAM_FLOAT(fov); - SetCameraToTexture(viewpoint, texturename, fov); - return 0; -} - static int CheckForTexture(const FString& name, int type, int flags) { return TexMan.CheckForTexture(name, static_cast(type), flags).GetIndex(); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 4ad4c8ac2e3..25ca97aac80 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -32,29 +32,28 @@ ** */ -#include "a_pickups.h" -#include "thingdef.h" #include "c_console.h" #include "filesystem.h" #include "zcc_parser.h" #include "zcc-parse.h" #include "zcc_compile.h" -#include "v_text.h" -#include "p_lnspec.h" -#include "v_video.h" +#include "printf.h" +#include "symbols.h" FSharedStringArena VMStringConstants; -bool isActor(PContainerType *type); +FieldDesc* FindField(PContainerType* cls, const char* string); +AFuncDesc* FindFunction(PContainerType* cls, const char* string); -static int GetIntConst(FxExpression *ex, FCompileContext &ctx) + +int GetIntConst(FxExpression *ex, FCompileContext &ctx) { ex = new FxIntCast(ex, false); ex = ex->Resolve(ctx); return ex ? static_cast(ex)->GetValue().GetInt() : 0; } -static double GetFloatConst(FxExpression *ex, FCompileContext &ctx) +double GetFloatConst(FxExpression *ex, FCompileContext &ctx) { ex = new FxFloatCast(ex); ex = ex->Resolve(ctx); @@ -644,6 +643,7 @@ void ZCCCompiler::MessageV(ZCC_TreeNode *node, const char *txtcolor, const char // ZCCCompiler :: Compile // // Compile everything defined at this level. +// This can be overridden to add custom content. // //========================================================================== @@ -653,10 +653,7 @@ int ZCCCompiler::Compile() CreateStructTypes(); CompileAllConstants(); CompileAllFields(); - CompileAllProperties(); - InitDefaults(); InitFunctions(); - CompileStates(); return FScriptPosition::ErrorCounter; } @@ -1295,7 +1292,6 @@ ZCC_ExprTypeRef *ZCCCompiler::NodeFromSymbolType(PSymbolType *sym, ZCC_Expressio // builds the internal structure of all classes and structs // //========================================================================== -void AddActorInfo(PClass *cls); void ZCCCompiler::CompileAllFields() { @@ -1354,15 +1350,13 @@ void ZCCCompiler::CompileAllFields() type->Size = Classes[i]->ClassType()->ParentClass->Size; } } - if (type->TypeName == NAME_Actor) + if (!PrepareMetaData(type)) { - assert(type->MetaSize == 0); - AddActorInfo(type); // AActor needs the actor info manually added to its meta data before adding any scripted fields. + if (Classes[i]->ClassType()->ParentClass) + type->MetaSize = Classes[i]->ClassType()->ParentClass->MetaSize; + else + type->MetaSize = 0; } - else if (Classes[i]->ClassType()->ParentClass) - type->MetaSize = Classes[i]->ClassType()->ParentClass->MetaSize; - else - type->MetaSize = 0; if (CompileFields(type->VMType, Classes[i]->Fields, nullptr, &Classes[i]->TreeNodes, false, !!HasNativeChildren.CheckKey(type->TypeName))) { @@ -1576,150 +1570,6 @@ bool ZCCCompiler::CompileFields(PContainerType *type, TArrayProperties.Size() > 0) - CompileProperties(c->ClassType(), c->Properties, c->Type()->TypeName); - - if (c->FlagDefs.Size() > 0) - CompileFlagDefs(c->ClassType(), c->FlagDefs, c->Type()->TypeName); - - } -} - -//========================================================================== -// -// ZCCCompiler :: CompileProperties -// -// builds the internal structure of a single class or struct -// -//========================================================================== - -bool ZCCCompiler::CompileProperties(PClass *type, TArray &Properties, FName prefix) -{ - if (!type->IsDescendantOf(RUNTIME_CLASS(AActor))) - { - Error(Properties[0], "Properties can only be defined for actors"); - return false; - } - for(auto p : Properties) - { - TArray fields; - ZCC_Identifier *id = (ZCC_Identifier *)p->Body; - - if (FName(p->NodeName) == FName("prefix") && fileSystem.GetFileContainer(Lump) == 0) - { - // only for internal definitions: Allow setting a prefix. This is only for compatiblity with the old DECORATE property parser, but not for general use. - prefix = id->Id; - } - else - { - do - { - auto f = dyn_cast(type->FindSymbol(id->Id, true)); - if (f == nullptr) - { - Error(id, "Variable %s not found in %s", FName(id->Id).GetChars(), type->TypeName.GetChars()); - } - fields.Push(f); - id = (ZCC_Identifier*)id->SiblingNext; - } while (id != p->Body); - - FString qualifiedname; - // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. - // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. - FName name = FName(p->NodeName); - if (prefix == NAME_None) qualifiedname.Format("@property@%s", name.GetChars()); - else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars()); - - fields.ShrinkToFit(); - if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, fields))) - { - Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); - } - } - } - return true; -} - -//========================================================================== -// -// ZCCCompiler :: CompileProperties -// -// builds the internal structure of a single class or struct -// -//========================================================================== - -bool ZCCCompiler::CompileFlagDefs(PClass *type, TArray &Properties, FName prefix) -{ - if (!type->IsDescendantOf(RUNTIME_CLASS(AActor))) - { - Error(Properties[0], "Flags can only be defined for actors"); - return false; - } - for (auto p : Properties) - { - PField *field; - FName referenced = FName(p->RefName); - - if (FName(p->NodeName) == FName("prefix") && fileSystem.GetFileContainer(Lump) == 0) - { - // only for internal definitions: Allow setting a prefix. This is only for compatiblity with the old DECORATE property parser, but not for general use. - prefix = referenced; - } - else - { - if (referenced != NAME_None) - { - field = dyn_cast(type->FindSymbol(referenced, true)); - if (field == nullptr) - { - Error(p, "Variable %s not found in %s", referenced.GetChars(), type->TypeName.GetChars()); - } - else if (!field->Type->isInt() || field->Type->Size != 4) - { - Error(p, "Variable %s in %s must have a size of 4 bytes for use as flag storage", referenced.GetChars(), type->TypeName.GetChars()); - } - } - else field = nullptr; - - - FString qualifiedname; - // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. - // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. - FName name = FName(p->NodeName); - for (int i = 0; i < 2; i++) - { - if (i == 0) qualifiedname.Format("@flagdef@%s", name.GetChars()); - else - { - if (prefix == NAME_None) continue; - qualifiedname.Format("@flagdef@%s.%s", prefix.GetChars(), name.GetChars()); - } - - if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, field, p->BitValue, i == 0 && prefix != NAME_None))) - { - Error(p, "Unable to add flag definition %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); - } - } - - if (field != nullptr) - type->VMType->AddNativeField(FStringf("b%s", name.GetChars()), TypeSInt32, field->Offset, 0, 1 << p->BitValue); - } - } - return true; -} - //========================================================================== // // ZCCCompiler :: FieldFlagsToString @@ -2073,520 +1923,34 @@ PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, //========================================================================== // -// Parses an actor property's parameters and calls the handler -// -//========================================================================== - -void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag) -{ - static TArray params; - static TArray strings; - - params.Clear(); - strings.Clear(); - params.Reserve(1); - params[0].i = 0; - if (prop->params[0] != '0') - { - if (property->Values == nullptr) - { - Error(property, "%s: arguments missing", prop->name); - return; - } - const char * p = prop->params; - auto exp = property->Values; - - FCompileContext ctx(OutNamespace, bag.Info->VMType, false); - while (true) - { - FPropParam conv; - FPropParam pref; - - FxExpression *ex = ConvertNode(exp); - ex = ex->Resolve(ctx); - if (ex == nullptr) - { - return; - } - else if (!ex->isConstant()) - { - // If we get TypeError, there has already been a message from deeper down so do not print another one. - if (exp->Type != TypeError) Error(exp, "%s: non-constant parameter", prop->name); - return; - } - conv.s = nullptr; - pref.s = nullptr; - pref.i = -1; - switch ((*p) & 223) - { - - case 'X': // Expression in parentheses or number. We only support the constant here. The function will have to be handled by a separate property to get past the parser. - conv.i = GetIntConst(ex, ctx); - params.Push(conv); - conv.exp = nullptr; - break; - - case 'I': - case 'M': // special case for morph styles in DECORATE . This expression-aware parser will not need this. - case 'N': // special case for thing activations in DECORATE. This expression-aware parser will not need this. - conv.i = GetIntConst(ex, ctx); - break; - - case 'F': - conv.d = GetFloatConst(ex, ctx); - break; - - case 'Z': // an optional string. Does not allow any numeric value. - if (ex->ValueType != TypeString) - { - // apply this expression to the next argument on the list. - params.Push(conv); - params[0].i++; - p++; - continue; - } - conv.s = GetStringConst(ex, ctx); - break; - - case 'C': // this parser accepts colors only in string form. - pref.i = 1; - case 'S': - case 'T': // a filtered string (ZScript only parses filtered strings so there's nothing to do here.) - conv.s = GetStringConst(ex, ctx); - break; - - case 'L': // Either a number or a list of strings - if (ex->ValueType != TypeString) - { - pref.i = 0; - conv.i = GetIntConst(ex, ctx); - } - else - { - pref.i = 1; - params.Push(pref); - params[0].i++; - - do - { - conv.s = GetStringConst(ex, ctx); - if (conv.s != nullptr) - { - params.Push(conv); - params[0].i++; - } - exp = static_cast(exp->SiblingNext); - if (exp != property->Values) - { - ex = ConvertNode(exp); - ex = ex->Resolve(ctx); - if (ex == nullptr) return; - } - } while (exp != property->Values); - goto endofparm; - } - break; - - default: - assert(false); - break; - - } - if (pref.i != -1) - { - params.Push(pref); - params[0].i++; - } - params.Push(conv); - params[0].i++; - exp = static_cast(exp->SiblingNext); - endofparm: - p++; - // Skip the DECORATE 'no comma' marker - if (*p == '_') p++; - - if (*p == 0) - { - if (exp != property->Values) - { - Error(property, "Too many values for '%s'", prop->name); - return; - } - break; - } - else if (exp == property->Values) - { - if (*p < 'a') - { - Error(property, "Insufficient parameters for %s", prop->name); - return; - } - break; - } - } - } - // call the handler - try - { - prop->Handler(defaults, bag.Info, bag, ¶ms[0]); - } - catch (CRecoverableError &error) - { - Error(property, "%s", error.GetMessage()); - } -} - - -//========================================================================== +// SetImplicitArgs // -// Parses an actor property's parameters and calls the handler +// Adds the parameters implied by the function flags. // //========================================================================== -void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag) +void ZCCCompiler::SetImplicitArgs(TArray* args, TArray* argflags, TArray* argnames, PContainerType* cls, uint32_t funcflags, int useflags) { - ZCC_ExprConstant one; - unsigned parmcount = 1; - ZCC_TreeNode *x = property->Values; - if (x == nullptr) - { - parmcount = 0; - } - else - { - while (x->SiblingNext != property->Values) - { - x = x->SiblingNext; - parmcount++; - } - } - if (parmcount == 0 && prop->Variables.Size() == 1 && prop->Variables[0]->Type == TypeBool) - { - // allow boolean properties to have the parameter omitted - memset(&one, 0, sizeof(one)); - one.SourceName = property->SourceName; // This may not be null! - one.Operation = PEX_ConstValue; - one.NodeType = AST_ExprConstant; - one.Type = TypeBool; - one.IntVal = 1; - property->Values = &one; - } - else if (parmcount != prop->Variables.Size()) - { - Error(x == nullptr? property : x, "Argument count mismatch: Got %u, expected %u", parmcount, prop->Variables.Size()); - return; - } + // Must be called before adding any other arguments. + assert(args == nullptr || args->Size() == 0); + assert(argflags == nullptr || argflags->Size() == 0); - auto exp = property->Values; - FCompileContext ctx(OutNamespace, bag.Info->VMType, false); - for (auto f : prop->Variables) + if (funcflags & VARF_Method) { - void *addr; - if (f == nullptr) - { - // This variable was missing. The error had been reported for the property itself already. - return; - } - - if (f->Flags & VARF_Meta) - { - addr = ((char*)bag.Info->Meta) + f->Offset; - } - else - { - addr = ((char*)defaults) + f->Offset; - } - - FxExpression *ex = ConvertNode(exp); - ex = ex->Resolve(ctx); - if (ex == nullptr) - { - return; - } - else if (!ex->isConstant()) - { - // If we get TypeError, there has already been a message from deeper down so do not print another one. - if (exp->Type != TypeError) Error(exp, "%s: non-constant parameter", prop->SymbolName.GetChars()); - return; - } - - if (f->Type == TypeBool) - { - static_cast(f->Type)->SetValue(addr, !!GetIntConst(ex, ctx)); - } - else if (f->Type == TypeName) - { - *(FName*)addr = GetStringConst(ex, ctx); - } - else if (f->Type == TypeSound) - { - *(FSoundID*)addr = GetStringConst(ex, ctx); - } - else if (f->Type == TypeColor && ex->ValueType == TypeString) // colors can also be specified as ints. - { - *(PalEntry*)addr = V_GetColor(nullptr, GetStringConst(ex, ctx), &ex->ScriptPosition); - } - else if (f->Type->isIntCompatible()) - { - static_cast(f->Type)->SetValue(addr, GetIntConst(ex, ctx)); - } - else if (f->Type->isFloat()) - { - static_cast(f->Type)->SetValue(addr, GetFloatConst(ex, ctx)); - } - else if (f->Type == TypeString) - { - *(FString*)addr = GetStringConst(ex, ctx); - } - else if (f->Type->isClassPointer()) - { - auto clsname = GetStringConst(ex, ctx); - if (*clsname == 0 || !stricmp(clsname, "none")) - { - *(PClass**)addr = nullptr; - } - else - { - auto cls = PClass::FindClass(clsname); - auto cp = static_cast(f->Type); - if (cls == nullptr) - { - cls = cp->ClassRestriction->FindClassTentative(clsname); - } - else if (!cls->IsDescendantOf(cp->ClassRestriction)) - { - Error(property, "class %s is not compatible with property type %s", clsname, cp->ClassRestriction->TypeName.GetChars()); - } - *(PClass**)addr = cls; - } - } - else - { - Error(property, "unhandled property type %s", f->Type->DescriptiveName()); - } - exp->ToErrorNode(); // invalidate after processing. - exp = static_cast(exp->SiblingNext); + // implied self pointer + if (args != nullptr) args->Push(NewPointer(cls, !!(funcflags & VARF_ReadOnly))); + if (argflags != nullptr) argflags->Push(VARF_Implicit | VARF_ReadOnly); + if (argnames != nullptr) argnames->Push(NAME_self); } } -//========================================================================== -// -// Parses an actor property -// -//========================================================================== - -void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *prop, Baggage &bag) -{ - auto namenode = prop->Prop; - FString propname; - - if (namenode->SiblingNext == namenode) - { - if (namenode->Id == NAME_DamageFunction) - { - auto x = ConvertNode(prop->Values); - CreateDamageFunction(OutNamespace, mVersion, cls, (AActor *)bag.Info->Defaults, x, false, Lump); - ((AActor *)bag.Info->Defaults)->DamageVal = -1; - return; - } - - // a one-name property - propname = FName(namenode->Id).GetChars(); - - } - else if (namenode->SiblingNext->SiblingNext == namenode) - { - // a two-name property - propname << FName(namenode->Id).GetChars() << "." << FName(static_cast(namenode->SiblingNext)->Id).GetChars(); - } - else - { - Error(prop, "Property name may at most contain two parts"); - return; - } - - - FPropertyInfo *property = FindProperty(propname); - - if (property != nullptr && property->category != CAT_INFO) - { - auto pcls = PClass::FindActor(property->clsname); - if (cls->IsDescendantOf(pcls)) - { - DispatchProperty(property, prop, (AActor *)bag.Info->Defaults, bag); - } - else - { - Error(prop, "'%s' requires an actor of type '%s'\n", propname.GetChars(), pcls->TypeName.GetChars()); - } - } - else - { - propname.Insert(0, "@property@"); - FName name(propname, true); - if (name != NAME_None) - { - auto propp = dyn_cast(cls->FindSymbol(name, true)); - if (propp != nullptr) - { - DispatchScriptProperty(propp, prop, (AActor *)bag.Info->Defaults, bag); - return; - } - } - Error(prop, "'%s' is an unknown actor property\n", propname.GetChars()); - } -} //========================================================================== // -// Finds a flag and sets or clears it -// -//========================================================================== - -void ZCCCompiler::ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg) -{ - auto namenode = flg->name; - const char *n1 = FName(namenode->Id).GetChars(), *n2; - - if (namenode->SiblingNext == namenode) - { - // a one-name flag - n2 = nullptr; - } - else if (namenode->SiblingNext->SiblingNext == namenode) - { - // a two-name flag - n2 = FName(static_cast(namenode->SiblingNext)->Id).GetChars(); - } - else - { - Error(flg, "Flag name may at most contain two parts"); - return; - } - - auto fd = FindFlag(cls, n1, n2, true); - if (fd != nullptr) - { - if (fd->varflags & VARF_Deprecated) - { - Warn(flg, "Deprecated flag '%s%s%s' used", n1, n2 ? "." : "", n2 ? n2 : ""); - } - if (fd->structoffset == -1) - { - HandleDeprecatedFlags((AActor*)cls->Defaults, cls, flg->set, fd->flagbit); - } - else - { - ModActorFlag((AActor*)cls->Defaults, fd, flg->set); - } - } - else - { - Error(flg, "Unknown flag '%s%s%s'", n1, n2 ? "." : "", n2 ? n2 : ""); - } -} - -//========================================================================== // -// Parses the default list // //========================================================================== -void ZCCCompiler::InitDefaults() -{ - for (auto c : Classes) - { - // This may be removed if the conditions change, but right now only subclasses of Actor can define a Default block. - if (!c->ClassType()->IsDescendantOf(RUNTIME_CLASS(AActor))) - { - if (c->Defaults.Size()) Error(c->cls, "%s: Non-actor classes may not have defaults", c->ClassType()->TypeName.GetChars()); - if (c->ClassType()->ParentClass) - { - auto ti = static_cast(c->ClassType()); - ti->InitializeDefaults(); - } - } - else - { - auto cls = c->ClassType(); - // This should never happen. - if (cls->Defaults != nullptr) - { - Error(c->cls, "%s already has defaults", cls->TypeName.GetChars()); - } - // This can only occur if a native parent is not initialized. In all other cases the sorting of the class list should prevent this from ever happening. - else if (cls->ParentClass->Defaults == nullptr && cls != RUNTIME_CLASS(AActor)) - { - Error(c->cls, "Parent class %s of %s is not initialized", cls->ParentClass->TypeName.GetChars(), cls->TypeName.GetChars()); - } - else - { - // Copy the parent's defaults and meta data. - auto ti = static_cast(cls); - - ti->InitializeDefaults(); - - // Replacements require that the meta data has been allocated by InitializeDefaults. - if (c->cls->Replaces != nullptr && !ti->SetReplacement(c->cls->Replaces->Id)) - { - Warn(c->cls, "Replaced type '%s' not found for %s", FName(c->cls->Replaces->Id).GetChars(), ti->TypeName.GetChars()); - } - - // We need special treatment for this one field in AActor's defaults which cannot be made visible to DECORATE as a property. - // It's better to do this here under controlled conditions than deeper down in the class type classes. - if (ti == RUNTIME_CLASS(AActor)) - { - ((AActor*)ti->Defaults)->ConversationRoot = 1; - } - - Baggage bag; - #ifdef _DEBUG - bag.ClassName = cls->TypeName.GetChars(); - #endif - bag.Version = mVersion; - bag.Namespace = OutNamespace; - bag.Info = ti; - bag.DropItemSet = false; - bag.StateSet = false; - bag.fromDecorate = false; - bag.CurrentState = 0; - bag.Lumpnum = c->cls->SourceLump; - bag.DropItemList = nullptr; - // The actual script position needs to be set per property. - - for (auto d : c->Defaults) - { - auto content = d->Content; - if (content != nullptr) do - { - switch (content->NodeType) - { - case AST_PropertyStmt: - bag.ScriptPosition.FileName = *content->SourceName; - bag.ScriptPosition.ScriptLine = content->SourceLoc; - ProcessDefaultProperty(ti, static_cast(content), bag); - break; - - case AST_FlagStmt: - ProcessDefaultFlag(ti, static_cast(content)); - break; - - default: - break; - } - content = static_cast(content->SiblingNext); - } while (content != d->Content); - } - if (bag.DropItemSet) - { - bag.Info->SetDropItems(bag.DropItemList); - } - } - } - } -} - - void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool forclass) { TArray rets(1); @@ -2700,29 +2064,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool } if (f->Flags & ZCC_Action) { - if (varflags & VARF_ReadOnly) + implicitargs = CheckActionKeyword(f,varflags, useflags, c); + if (implicitargs < 0) { - Error(f, "Action functions cannot be declared const"); + Error(f, "'Action' not allowed as a function qualifier"); + // Set state to allow continued compilation to find more errors. varflags &= ~VARF_ReadOnly; - } - if (varflags & VARF_UI) - { - Error(f, "Action functions cannot be declared UI"); - } - // Non-Actors cannot have action functions. - if (!isActor(c->Type())) - { - 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; } } @@ -3127,318 +2474,6 @@ void ZCCCompiler::InitFunctions() } } -//========================================================================== -// -// very complicated check for random duration. -// -//========================================================================== - -static bool CheckRandom(ZCC_Expression *duration) -{ - if (duration->NodeType != AST_ExprFuncCall) return false; - auto func = static_cast(duration); - if (func->Function == nullptr) return false; - if (func->Function->NodeType != AST_ExprID) return false; - auto f2 = static_cast(func->Function); - return f2->Identifier == NAME_Random; -} - -//========================================================================== -// -// Sets up the action function call -// -//========================================================================== -FxExpression *ZCCCompiler::SetupActionFunction(PClass *cls, ZCC_TreeNode *af, int StateFlags) -{ - // We have 3 cases to consider here: - // 1. A function without parameters. This can be called directly - // 2. A functon with parameters. This needs to be wrapped into a helper function to set everything up. - // 3. An anonymous function. - - // 1. and 2. are exposed through AST_ExprFunctionCall - if (af->NodeType == AST_ExprFuncCall) - { - auto fc = static_cast(af); - assert(fc->Function->NodeType == AST_ExprID); - auto id = static_cast(fc->Function); - - // We must skip ACS_NamedExecuteWithResult here, because this name both exists as an action function and as a builtin. - // The code which gets called from here can easily make use of the builtin, so let's just pass this name without any checks. - // The actual action function is still needed by DECORATE: - if (id->Identifier != NAME_ACS_NamedExecuteWithResult) - { - PFunction *afd = dyn_cast(cls->VMType->Symbols.FindSymbol(id->Identifier, true)); - if (afd != nullptr) - { - if (fc->Parameters == nullptr && !(afd->Variants[0].Flags & VARF_Virtual)) - { - FArgumentList argumentlist; - // We can use this function directly without wrapping it in a caller. - assert(PType::toClass(afd->Variants[0].SelfClass) != nullptr); // non classes are not supposed to get here. - - int comboflags = afd->Variants[0].UseFlags & StateFlags; - if (comboflags == StateFlags) // the function must satisfy all the flags the state requires - { - if ((afd->Variants[0].Flags & VARF_Private) && afd->OwningClass != cls->VMType) - { - Error(af, "%s is declared private and not accessible", FName(id->Identifier).GetChars()); - } - - return new FxVMFunctionCall(new FxSelf(*af), afd, argumentlist, *af, false); - } - else - { - Error(af, "Cannot use non-action function %s here.", FName(id->Identifier).GetChars()); - } - } - } - else - { - // it may also be an action special so check that first before printing an error. - if (!P_FindLineSpecial(FName(id->Identifier).GetChars())) - { - Error(af, "%s: action function not found in %s", FName(id->Identifier).GetChars(), cls->TypeName.GetChars()); - return nullptr; - } - // Action specials fall through to the code generator. - } - } - } - return ConvertAST(cls->VMType, af); -} - -//========================================================================== -// -// Compile the states -// -//========================================================================== - -void ZCCCompiler::CompileStates() -{ - for (auto c : Classes) - { - - if (!c->ClassType()->IsDescendantOf(RUNTIME_CLASS(AActor))) - { - if (c->States.Size()) Error(c->cls, "%s: States can only be defined for actors.", c->Type()->TypeName.GetChars()); - continue; - } - - FString statename; // The state builder wants the label as one complete string, not separated into tokens. - FStateDefinitions statedef; - - if (static_cast(c->ClassType())->ActorInfo()->SkipSuperSet) - { - // SKIP_SUPER'ed actors only get the base states from AActor. - statedef.MakeStateDefines(RUNTIME_CLASS(AActor)); - } - else - { - statedef.MakeStateDefines(ValidateActor(c->ClassType()->ParentClass)); - } - - - int numframes = 0; - - for (auto s : c->States) - { - int flags; - if (s->Flags != nullptr) - { - flags = 0; - auto p = s->Flags; - do - { - switch (p->Id) - { - case NAME_Actor: - flags |= SUF_ACTOR; - break; - case NAME_Overlay: - flags |= SUF_OVERLAY; - break; - case NAME_Weapon: - flags |= SUF_WEAPON; - break; - case NAME_Item: - flags |= SUF_ITEM; - break; - default: - Error(p, "Unknown States qualifier %s", FName(p->Id).GetChars()); - break; - } - - p = static_cast(p->SiblingNext); - } while (p != s->Flags); - } - else - { - flags = static_cast(c->ClassType())->ActorInfo()->DefaultStateUsage; - } - auto st = s->Body; - if (st != nullptr) do - { - switch (st->NodeType) - { - case AST_StateLabel: - { - auto sl = static_cast(st); - statename = FName(sl->Label).GetChars(); - statedef.AddStateLabel(statename); - break; - } - case AST_StateLine: - { - auto sl = static_cast(st); - FState state; - memset(&state, 0, sizeof(state)); - state.UseFlags = flags; - if (sl->Sprite->Len() != 4) - { - Error(sl, "Sprite name must be exactly 4 characters. Found '%s'", sl->Sprite->GetChars()); - } - else - { - state.sprite = GetSpriteIndex(sl->Sprite->GetChars()); - } - FCompileContext ctx(OutNamespace, c->Type(), false); - if (CheckRandom(sl->Duration)) - { - auto func = static_cast(sl->Duration); - if (func->Parameters == func->Parameters->SiblingNext || func->Parameters != func->Parameters->SiblingNext->SiblingNext) - { - Error(sl, "Random duration requires exactly 2 parameters"); - } - int v1 = IntConstFromNode(func->Parameters->Value, c->Type()); - int v2 = IntConstFromNode(static_cast(func->Parameters->SiblingNext)->Value, c->Type()); - if (v1 > v2) std::swap(v1, v2); - state.Tics = (int16_t)clamp(v1, 0, INT16_MAX); - state.TicRange = (uint16_t)clamp(v2 - v1, 0, UINT16_MAX); - } - else - { - state.Tics = (int16_t)IntConstFromNode(sl->Duration, c->Type()); - state.TicRange = 0; - } - if (sl->bBright) state.StateFlags |= STF_FULLBRIGHT; - if (sl->bFast) state.StateFlags |= STF_FAST; - if (sl->bSlow) state.StateFlags |= STF_SLOW; - if (sl->bCanRaise) state.StateFlags |= STF_CANRAISE; - if (sl->bNoDelay) state.StateFlags |= STF_NODELAY; - if (sl->bNoDelay) - { - if (statedef.GetStateLabelIndex(NAME_Spawn) != statedef.GetStateCount()) - { - Warn(sl, "NODELAY only has an effect on the first state after 'Spawn:'"); - } - } - if (sl->Offset != nullptr) - { - state.Misc1 = IntConstFromNode(sl->Offset, c->Type()); - state.Misc2 = IntConstFromNode(static_cast(sl->Offset->SiblingNext), c->Type()); - } - - if (sl->Lights != nullptr) - { - auto l = sl->Lights; - do - { - AddStateLight(&state, StringConstFromNode(l, c->Type())); - l = static_cast(l->SiblingNext); - } while (l != sl->Lights); - } - - if (sl->Action != nullptr) - { - auto code = SetupActionFunction(static_cast(c->ClassType()), sl->Action, state.UseFlags); - if (code != nullptr) - { - auto funcsym = CreateAnonymousFunction(c->Type(), nullptr, state.UseFlags); - state.ActionFunc = FunctionBuildList.AddFunction(OutNamespace, mVersion, funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), false, statedef.GetStateCount(), (int)sl->Frames->Len(), Lump); - } - } - - int count = statedef.AddStates(&state, sl->Frames->GetChars(), *sl); - if (count < 0) - { - Error(sl, "Invalid frame character string '%s'", sl->Frames->GetChars()); - count = -count; - } - break; - } - case AST_StateGoto: - { - auto sg = static_cast(st); - statename = ""; - if (sg->Qualifier != nullptr) - { - statename << FName(sg->Qualifier->Id).GetChars() << "::"; - } - auto part = sg->Label; - do - { - statename << FName(part->Id).GetChars() << '.'; - part = static_cast(part->SiblingNext); - } while (part != sg->Label); - statename.Truncate(statename.Len() - 1); // remove the last '.' in the label name - if (sg->Offset != nullptr) - { - int offset = IntConstFromNode(sg->Offset, c->Type()); - if (offset < 0) - { - Error(sg, "GOTO offset must be positive"); - offset = 0; - } - if (offset > 0) - { - statename.AppendFormat("+%d", offset); - } - } - if (!statedef.SetGotoLabel(statename)) - { - Error(sg, "GOTO before first state"); - } - break; - } - case AST_StateFail: - case AST_StateWait: - if (!statedef.SetWait()) - { - Error(st, "%s before first state", st->NodeType == AST_StateFail ? "Fail" : "Wait"); - } - break; - - case AST_StateLoop: - if (!statedef.SetLoop()) - { - Error(st, "LOOP before first state"); - } - break; - - case AST_StateStop: - if (!statedef.SetStop()) - { - Error(st, "STOP before first state"); - } - break; - - default: - assert(0 && "Bad AST node in state"); - } - st = static_cast(st->SiblingNext); - } while (st != s->Body); - } - try - { - FinalizeClass(c->ClassType(), statedef); - } - catch (CRecoverableError &err) - { - Error(c->cls, "%s", err.GetMessage()); - } - } -} - //========================================================================== // // Convert the AST data for the code generator. diff --git a/src/scripting/zscript/zcc_compile.h b/src/scripting/zscript/zcc_compile.h index 2235b7796ab..c8c72552fd1 100644 --- a/src/scripting/zscript/zcc_compile.h +++ b/src/scripting/zscript/zcc_compile.h @@ -106,11 +106,14 @@ class ZCCCompiler { public: ZCCCompiler(ZCC_AST &tree, DObject *outer, PSymbolTable &symbols, PNamespace *outnamespace, int lumpnum, const VersionInfo & ver); - ~ZCCCompiler(); - int Compile(); + virtual ~ZCCCompiler(); + virtual int Compile(); -private: +protected: const char * GetStringConst(FxExpression *ex, FCompileContext &ctx); + virtual int CheckActionKeyword(ZCC_FuncDeclarator* f, uint32_t &varflags, int useflags, ZCC_StructWork *c) { return -1; } // stock implementation does not support this. + virtual bool PrepareMetaData(PClass *type) { return false; } + virtual void SetImplicitArgs(TArray* args, TArray* argflags, TArray* argnames, PContainerType* cls, uint32_t funcflags, int useflags); int IntConstFromNode(ZCC_TreeNode *node, PContainerType *cls); FString StringConstFromNode(ZCC_TreeNode *node, PContainerType *cls); diff --git a/src/scripting/zscript/zcc_compile_doom.cpp b/src/scripting/zscript/zcc_compile_doom.cpp new file mode 100644 index 00000000000..350253c0466 --- /dev/null +++ b/src/scripting/zscript/zcc_compile_doom.cpp @@ -0,0 +1,1100 @@ +/* +** zcc_compile_doom.cpp +** +** contains the Doom specific parts of the script parser, i.e. +** actor property definitions and associated content. +** +**--------------------------------------------------------------------------- +** Copyright 2016-2020 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "a_pickups.h" +#include "thingdef.h" +#include "c_console.h" +#include "filesystem.h" +#include "zcc_parser.h" +#include "zcc-parse.h" +#include "zcc_compile_doom.h" +#include "v_text.h" +#include "p_lnspec.h" +#include "v_video.h" + + +bool isActor(PContainerType *type); +void AddActorInfo(PClass *cls); +int GetIntConst(FxExpression* ex, FCompileContext& ctx); +double GetFloatConst(FxExpression* ex, FCompileContext& ctx); + +//========================================================================== +// +// ZCCCompiler :: Compile +// +// Compile everything defined at this level. +// +//========================================================================== + +int ZCCDoomCompiler::Compile() +{ + CreateClassTypes(); + CreateStructTypes(); + CompileAllConstants(); + CompileAllFields(); + CompileAllProperties(); + InitDefaults(); + InitFunctions(); + CompileStates(); + return FScriptPosition::ErrorCounter; +} + + +//========================================================================== +// +// ZCCCompiler :: CompileAllProperties +// +// builds the property lists of all actor classes +// +//========================================================================== + +void ZCCDoomCompiler::CompileAllProperties() +{ + for (auto c : Classes) + { + if (c->Properties.Size() > 0) + CompileProperties(c->ClassType(), c->Properties, c->Type()->TypeName); + + if (c->FlagDefs.Size() > 0) + CompileFlagDefs(c->ClassType(), c->FlagDefs, c->Type()->TypeName); + + } +} + +//========================================================================== +// +// ZCCCompiler :: CompileProperties +// +// builds the internal structure of a single class or struct +// +//========================================================================== + +bool ZCCDoomCompiler::CompileProperties(PClass *type, TArray &Properties, FName prefix) +{ + if (!type->IsDescendantOf(RUNTIME_CLASS(AActor))) + { + Error(Properties[0], "Properties can only be defined for actors"); + return false; + } + for(auto p : Properties) + { + TArray fields; + ZCC_Identifier *id = (ZCC_Identifier *)p->Body; + + if (FName(p->NodeName) == FName("prefix") && fileSystem.GetFileContainer(Lump) == 0) + { + // only for internal definitions: Allow setting a prefix. This is only for compatiblity with the old DECORATE property parser, but not for general use. + prefix = id->Id; + } + else + { + do + { + auto f = dyn_cast(type->FindSymbol(id->Id, true)); + if (f == nullptr) + { + Error(id, "Variable %s not found in %s", FName(id->Id).GetChars(), type->TypeName.GetChars()); + } + fields.Push(f); + id = (ZCC_Identifier*)id->SiblingNext; + } while (id != p->Body); + + FString qualifiedname; + // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. + // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. + FName name = FName(p->NodeName); + if (prefix == NAME_None) qualifiedname.Format("@property@%s", name.GetChars()); + else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars()); + + fields.ShrinkToFit(); + if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, fields))) + { + Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); + } + } + } + return true; +} + +//========================================================================== +// +// ZCCCompiler :: CompileProperties +// +// builds the internal structure of a single class or struct +// +//========================================================================== + +bool ZCCDoomCompiler::CompileFlagDefs(PClass *type, TArray &Properties, FName prefix) +{ + if (!type->IsDescendantOf(RUNTIME_CLASS(AActor))) + { + Error(Properties[0], "Flags can only be defined for actors"); + return false; + } + for (auto p : Properties) + { + PField *field; + FName referenced = FName(p->RefName); + + if (FName(p->NodeName) == FName("prefix") && fileSystem.GetFileContainer(Lump) == 0) + { + // only for internal definitions: Allow setting a prefix. This is only for compatiblity with the old DECORATE property parser, but not for general use. + prefix = referenced; + } + else + { + if (referenced != NAME_None) + { + field = dyn_cast(type->FindSymbol(referenced, true)); + if (field == nullptr) + { + Error(p, "Variable %s not found in %s", referenced.GetChars(), type->TypeName.GetChars()); + } + else if (!field->Type->isInt() || field->Type->Size != 4) + { + Error(p, "Variable %s in %s must have a size of 4 bytes for use as flag storage", referenced.GetChars(), type->TypeName.GetChars()); + } + } + else field = nullptr; + + + FString qualifiedname; + // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. + // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. + FName name = FName(p->NodeName); + for (int i = 0; i < 2; i++) + { + if (i == 0) qualifiedname.Format("@flagdef@%s", name.GetChars()); + else + { + if (prefix == NAME_None) continue; + qualifiedname.Format("@flagdef@%s.%s", prefix.GetChars(), name.GetChars()); + } + + if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, field, p->BitValue, i == 0 && prefix != NAME_None))) + { + Error(p, "Unable to add flag definition %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); + } + } + + if (field != nullptr) + type->VMType->AddNativeField(FStringf("b%s", name.GetChars()), TypeSInt32, field->Offset, 0, 1 << p->BitValue); + } + } + return true; +} + +//========================================================================== +// +// Parses an actor property's parameters and calls the handler +// +//========================================================================== + +void ZCCDoomCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag) +{ + static TArray params; + static TArray strings; + + params.Clear(); + strings.Clear(); + params.Reserve(1); + params[0].i = 0; + if (prop->params[0] != '0') + { + if (property->Values == nullptr) + { + Error(property, "%s: arguments missing", prop->name); + return; + } + const char * p = prop->params; + auto exp = property->Values; + + FCompileContext ctx(OutNamespace, bag.Info->VMType, false); + while (true) + { + FPropParam conv; + FPropParam pref; + + FxExpression *ex = ConvertNode(exp); + ex = ex->Resolve(ctx); + if (ex == nullptr) + { + return; + } + else if (!ex->isConstant()) + { + // If we get TypeError, there has already been a message from deeper down so do not print another one. + if (exp->Type != TypeError) Error(exp, "%s: non-constant parameter", prop->name); + return; + } + conv.s = nullptr; + pref.s = nullptr; + pref.i = -1; + switch ((*p) & 223) + { + + case 'X': // Expression in parentheses or number. We only support the constant here. The function will have to be handled by a separate property to get past the parser. + conv.i = GetIntConst(ex, ctx); + params.Push(conv); + conv.exp = nullptr; + break; + + case 'I': + case 'M': // special case for morph styles in DECORATE . This expression-aware parser will not need this. + case 'N': // special case for thing activations in DECORATE. This expression-aware parser will not need this. + conv.i = GetIntConst(ex, ctx); + break; + + case 'F': + conv.d = GetFloatConst(ex, ctx); + break; + + case 'Z': // an optional string. Does not allow any numeric value. + if (ex->ValueType != TypeString) + { + // apply this expression to the next argument on the list. + params.Push(conv); + params[0].i++; + p++; + continue; + } + conv.s = GetStringConst(ex, ctx); + break; + + case 'C': // this parser accepts colors only in string form. + pref.i = 1; + case 'S': + case 'T': // a filtered string (ZScript only parses filtered strings so there's nothing to do here.) + conv.s = GetStringConst(ex, ctx); + break; + + case 'L': // Either a number or a list of strings + if (ex->ValueType != TypeString) + { + pref.i = 0; + conv.i = GetIntConst(ex, ctx); + } + else + { + pref.i = 1; + params.Push(pref); + params[0].i++; + + do + { + conv.s = GetStringConst(ex, ctx); + if (conv.s != nullptr) + { + params.Push(conv); + params[0].i++; + } + exp = static_cast(exp->SiblingNext); + if (exp != property->Values) + { + ex = ConvertNode(exp); + ex = ex->Resolve(ctx); + if (ex == nullptr) return; + } + } while (exp != property->Values); + goto endofparm; + } + break; + + default: + assert(false); + break; + + } + if (pref.i != -1) + { + params.Push(pref); + params[0].i++; + } + params.Push(conv); + params[0].i++; + exp = static_cast(exp->SiblingNext); + endofparm: + p++; + // Skip the DECORATE 'no comma' marker + if (*p == '_') p++; + + if (*p == 0) + { + if (exp != property->Values) + { + Error(property, "Too many values for '%s'", prop->name); + return; + } + break; + } + else if (exp == property->Values) + { + if (*p < 'a') + { + Error(property, "Insufficient parameters for %s", prop->name); + return; + } + break; + } + } + } + // call the handler + try + { + prop->Handler(defaults, bag.Info, bag, ¶ms[0]); + } + catch (CRecoverableError &error) + { + Error(property, "%s", error.GetMessage()); + } +} + + +//========================================================================== +// +// Parses an actor property's parameters and calls the handler +// +//========================================================================== + +void ZCCDoomCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag) +{ + ZCC_ExprConstant one; + unsigned parmcount = 1; + ZCC_TreeNode *x = property->Values; + if (x == nullptr) + { + parmcount = 0; + } + else + { + while (x->SiblingNext != property->Values) + { + x = x->SiblingNext; + parmcount++; + } + } + if (parmcount == 0 && prop->Variables.Size() == 1 && prop->Variables[0]->Type == TypeBool) + { + // allow boolean properties to have the parameter omitted + memset(&one, 0, sizeof(one)); + one.SourceName = property->SourceName; // This may not be null! + one.Operation = PEX_ConstValue; + one.NodeType = AST_ExprConstant; + one.Type = TypeBool; + one.IntVal = 1; + property->Values = &one; + } + else if (parmcount != prop->Variables.Size()) + { + Error(x == nullptr? property : x, "Argument count mismatch: Got %u, expected %u", parmcount, prop->Variables.Size()); + return; + } + + auto exp = property->Values; + FCompileContext ctx(OutNamespace, bag.Info->VMType, false); + for (auto f : prop->Variables) + { + void *addr; + if (f == nullptr) + { + // This variable was missing. The error had been reported for the property itself already. + return; + } + + if (f->Flags & VARF_Meta) + { + addr = ((char*)bag.Info->Meta) + f->Offset; + } + else + { + addr = ((char*)defaults) + f->Offset; + } + + FxExpression *ex = ConvertNode(exp); + ex = ex->Resolve(ctx); + if (ex == nullptr) + { + return; + } + else if (!ex->isConstant()) + { + // If we get TypeError, there has already been a message from deeper down so do not print another one. + if (exp->Type != TypeError) Error(exp, "%s: non-constant parameter", prop->SymbolName.GetChars()); + return; + } + + if (f->Type == TypeBool) + { + static_cast(f->Type)->SetValue(addr, !!GetIntConst(ex, ctx)); + } + else if (f->Type == TypeName) + { + *(FName*)addr = GetStringConst(ex, ctx); + } + else if (f->Type == TypeSound) + { + *(FSoundID*)addr = GetStringConst(ex, ctx); + } + else if (f->Type == TypeColor && ex->ValueType == TypeString) // colors can also be specified as ints. + { + *(PalEntry*)addr = V_GetColor(nullptr, GetStringConst(ex, ctx), &ex->ScriptPosition); + } + else if (f->Type->isIntCompatible()) + { + static_cast(f->Type)->SetValue(addr, GetIntConst(ex, ctx)); + } + else if (f->Type->isFloat()) + { + static_cast(f->Type)->SetValue(addr, GetFloatConst(ex, ctx)); + } + else if (f->Type == TypeString) + { + *(FString*)addr = GetStringConst(ex, ctx); + } + else if (f->Type->isClassPointer()) + { + auto clsname = GetStringConst(ex, ctx); + if (*clsname == 0 || !stricmp(clsname, "none")) + { + *(PClass**)addr = nullptr; + } + else + { + auto cls = PClass::FindClass(clsname); + auto cp = static_cast(f->Type); + if (cls == nullptr) + { + cls = cp->ClassRestriction->FindClassTentative(clsname); + } + else if (!cls->IsDescendantOf(cp->ClassRestriction)) + { + Error(property, "class %s is not compatible with property type %s", clsname, cp->ClassRestriction->TypeName.GetChars()); + } + *(PClass**)addr = cls; + } + } + else + { + Error(property, "unhandled property type %s", f->Type->DescriptiveName()); + } + exp->ToErrorNode(); // invalidate after processing. + exp = static_cast(exp->SiblingNext); + } +} + +//========================================================================== +// +// Parses an actor property +// +//========================================================================== + +void ZCCDoomCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *prop, Baggage &bag) +{ + auto namenode = prop->Prop; + FString propname; + + if (namenode->SiblingNext == namenode) + { + if (namenode->Id == NAME_DamageFunction) + { + auto x = ConvertNode(prop->Values); + CreateDamageFunction(OutNamespace, mVersion, cls, (AActor *)bag.Info->Defaults, x, false, Lump); + ((AActor *)bag.Info->Defaults)->DamageVal = -1; + return; + } + + // a one-name property + propname = FName(namenode->Id).GetChars(); + + } + else if (namenode->SiblingNext->SiblingNext == namenode) + { + // a two-name property + propname << FName(namenode->Id).GetChars() << "." << FName(static_cast(namenode->SiblingNext)->Id).GetChars(); + } + else + { + Error(prop, "Property name may at most contain two parts"); + return; + } + + + FPropertyInfo *property = FindProperty(propname); + + if (property != nullptr && property->category != CAT_INFO) + { + auto pcls = PClass::FindActor(property->clsname); + if (cls->IsDescendantOf(pcls)) + { + DispatchProperty(property, prop, (AActor *)bag.Info->Defaults, bag); + } + else + { + Error(prop, "'%s' requires an actor of type '%s'\n", propname.GetChars(), pcls->TypeName.GetChars()); + } + } + else + { + propname.Insert(0, "@property@"); + FName name(propname, true); + if (name != NAME_None) + { + auto propp = dyn_cast(cls->FindSymbol(name, true)); + if (propp != nullptr) + { + DispatchScriptProperty(propp, prop, (AActor *)bag.Info->Defaults, bag); + return; + } + } + Error(prop, "'%s' is an unknown actor property\n", propname.GetChars()); + } +} + +//========================================================================== +// +// Finds a flag and sets or clears it +// +//========================================================================== + +void ZCCDoomCompiler::ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg) +{ + auto namenode = flg->name; + const char *n1 = FName(namenode->Id).GetChars(), *n2; + + if (namenode->SiblingNext == namenode) + { + // a one-name flag + n2 = nullptr; + } + else if (namenode->SiblingNext->SiblingNext == namenode) + { + // a two-name flag + n2 = FName(static_cast(namenode->SiblingNext)->Id).GetChars(); + } + else + { + Error(flg, "Flag name may at most contain two parts"); + return; + } + + auto fd = FindFlag(cls, n1, n2, true); + if (fd != nullptr) + { + if (fd->varflags & VARF_Deprecated) + { + Warn(flg, "Deprecated flag '%s%s%s' used", n1, n2 ? "." : "", n2 ? n2 : ""); + } + if (fd->structoffset == -1) + { + HandleDeprecatedFlags((AActor*)cls->Defaults, cls, flg->set, fd->flagbit); + } + else + { + ModActorFlag((AActor*)cls->Defaults, fd, flg->set); + } + } + else + { + Error(flg, "Unknown flag '%s%s%s'", n1, n2 ? "." : "", n2 ? n2 : ""); + } +} + +//========================================================================== +// +// Parses the default list +// +//========================================================================== + +void ZCCDoomCompiler::InitDefaults() +{ + for (auto c : Classes) + { + // This may be removed if the conditions change, but right now only subclasses of Actor can define a Default block. + if (!c->ClassType()->IsDescendantOf(RUNTIME_CLASS(AActor))) + { + if (c->Defaults.Size()) Error(c->cls, "%s: Non-actor classes may not have defaults", c->ClassType()->TypeName.GetChars()); + if (c->ClassType()->ParentClass) + { + auto ti = static_cast(c->ClassType()); + ti->InitializeDefaults(); + } + } + else + { + auto cls = c->ClassType(); + // This should never happen. + if (cls->Defaults != nullptr) + { + Error(c->cls, "%s already has defaults", cls->TypeName.GetChars()); + } + // This can only occur if a native parent is not initialized. In all other cases the sorting of the class list should prevent this from ever happening. + else if (cls->ParentClass->Defaults == nullptr && cls != RUNTIME_CLASS(AActor)) + { + Error(c->cls, "Parent class %s of %s is not initialized", cls->ParentClass->TypeName.GetChars(), cls->TypeName.GetChars()); + } + else + { + // Copy the parent's defaults and meta data. + auto ti = static_cast(cls); + + ti->InitializeDefaults(); + + // Replacements require that the meta data has been allocated by InitializeDefaults. + if (c->cls->Replaces != nullptr && !ti->SetReplacement(c->cls->Replaces->Id)) + { + Warn(c->cls, "Replaced type '%s' not found for %s", FName(c->cls->Replaces->Id).GetChars(), ti->TypeName.GetChars()); + } + + // We need special treatment for this one field in AActor's defaults which cannot be made visible to DECORATE as a property. + // It's better to do this here under controlled conditions than deeper down in the class type classes. + if (ti == RUNTIME_CLASS(AActor)) + { + ((AActor*)ti->Defaults)->ConversationRoot = 1; + } + + Baggage bag; + #ifdef _DEBUG + bag.ClassName = cls->TypeName.GetChars(); + #endif + bag.Version = mVersion; + bag.Namespace = OutNamespace; + bag.Info = ti; + bag.DropItemSet = false; + bag.StateSet = false; + bag.fromDecorate = false; + bag.CurrentState = 0; + bag.Lumpnum = c->cls->SourceLump; + bag.DropItemList = nullptr; + // The actual script position needs to be set per property. + + for (auto d : c->Defaults) + { + auto content = d->Content; + if (content != nullptr) do + { + switch (content->NodeType) + { + case AST_PropertyStmt: + bag.ScriptPosition.FileName = *content->SourceName; + bag.ScriptPosition.ScriptLine = content->SourceLoc; + ProcessDefaultProperty(ti, static_cast(content), bag); + break; + + case AST_FlagStmt: + ProcessDefaultFlag(ti, static_cast(content)); + break; + + default: + break; + } + content = static_cast(content->SiblingNext); + } while (content != d->Content); + } + if (bag.DropItemSet) + { + bag.Info->SetDropItems(bag.DropItemList); + } + } + } + } +} + +//========================================================================== +// +// very complicated check for random duration. +// +//========================================================================== + +static bool CheckRandom(ZCC_Expression *duration) +{ + if (duration->NodeType != AST_ExprFuncCall) return false; + auto func = static_cast(duration); + if (func->Function == nullptr) return false; + if (func->Function->NodeType != AST_ExprID) return false; + auto f2 = static_cast(func->Function); + return f2->Identifier == NAME_Random; +} + +//========================================================================== +// +// Sets up the action function call +// +//========================================================================== +FxExpression *ZCCDoomCompiler::SetupActionFunction(PClass *cls, ZCC_TreeNode *af, int StateFlags) +{ + // We have 3 cases to consider here: + // 1. A function without parameters. This can be called directly + // 2. A functon with parameters. This needs to be wrapped into a helper function to set everything up. + // 3. An anonymous function. + + // 1. and 2. are exposed through AST_ExprFunctionCall + if (af->NodeType == AST_ExprFuncCall) + { + auto fc = static_cast(af); + assert(fc->Function->NodeType == AST_ExprID); + auto id = static_cast(fc->Function); + + // We must skip ACS_NamedExecuteWithResult here, because this name both exists as an action function and as a builtin. + // The code which gets called from here can easily make use of the builtin, so let's just pass this name without any checks. + // The actual action function is still needed by DECORATE: + if (id->Identifier != NAME_ACS_NamedExecuteWithResult) + { + PFunction *afd = dyn_cast(cls->VMType->Symbols.FindSymbol(id->Identifier, true)); + if (afd != nullptr) + { + if (fc->Parameters == nullptr && !(afd->Variants[0].Flags & VARF_Virtual)) + { + FArgumentList argumentlist; + // We can use this function directly without wrapping it in a caller. + assert(PType::toClass(afd->Variants[0].SelfClass) != nullptr); // non classes are not supposed to get here. + + int comboflags = afd->Variants[0].UseFlags & StateFlags; + if (comboflags == StateFlags) // the function must satisfy all the flags the state requires + { + if ((afd->Variants[0].Flags & VARF_Private) && afd->OwningClass != cls->VMType) + { + Error(af, "%s is declared private and not accessible", FName(id->Identifier).GetChars()); + } + + return new FxVMFunctionCall(new FxSelf(*af), afd, argumentlist, *af, false); + } + else + { + Error(af, "Cannot use non-action function %s here.", FName(id->Identifier).GetChars()); + } + } + } + else + { + // it may also be an action special so check that first before printing an error. + if (!P_FindLineSpecial(FName(id->Identifier).GetChars())) + { + Error(af, "%s: action function not found in %s", FName(id->Identifier).GetChars(), cls->TypeName.GetChars()); + return nullptr; + } + // Action specials fall through to the code generator. + } + } + } + return ConvertAST(cls->VMType, af); +} + +//========================================================================== +// +// Compile the states +// +//========================================================================== + +void ZCCDoomCompiler::CompileStates() +{ + for (auto c : Classes) + { + + if (!c->ClassType()->IsDescendantOf(RUNTIME_CLASS(AActor))) + { + if (c->States.Size()) Error(c->cls, "%s: States can only be defined for actors.", c->Type()->TypeName.GetChars()); + continue; + } + + FString statename; // The state builder wants the label as one complete string, not separated into tokens. + FStateDefinitions statedef; + + if (static_cast(c->ClassType())->ActorInfo()->SkipSuperSet) + { + // SKIP_SUPER'ed actors only get the base states from AActor. + statedef.MakeStateDefines(RUNTIME_CLASS(AActor)); + } + else + { + statedef.MakeStateDefines(ValidateActor(c->ClassType()->ParentClass)); + } + + + int numframes = 0; + + for (auto s : c->States) + { + int flags; + if (s->Flags != nullptr) + { + flags = 0; + auto p = s->Flags; + do + { + switch (p->Id) + { + case NAME_Actor: + flags |= SUF_ACTOR; + break; + case NAME_Overlay: + flags |= SUF_OVERLAY; + break; + case NAME_Weapon: + flags |= SUF_WEAPON; + break; + case NAME_Item: + flags |= SUF_ITEM; + break; + default: + Error(p, "Unknown States qualifier %s", FName(p->Id).GetChars()); + break; + } + + p = static_cast(p->SiblingNext); + } while (p != s->Flags); + } + else + { + flags = static_cast(c->ClassType())->ActorInfo()->DefaultStateUsage; + } + auto st = s->Body; + if (st != nullptr) do + { + switch (st->NodeType) + { + case AST_StateLabel: + { + auto sl = static_cast(st); + statename = FName(sl->Label).GetChars(); + statedef.AddStateLabel(statename); + break; + } + case AST_StateLine: + { + auto sl = static_cast(st); + FState state; + memset(&state, 0, sizeof(state)); + state.UseFlags = flags; + if (sl->Sprite->Len() != 4) + { + Error(sl, "Sprite name must be exactly 4 characters. Found '%s'", sl->Sprite->GetChars()); + } + else + { + state.sprite = GetSpriteIndex(sl->Sprite->GetChars()); + } + FCompileContext ctx(OutNamespace, c->Type(), false); + if (CheckRandom(sl->Duration)) + { + auto func = static_cast(sl->Duration); + if (func->Parameters == func->Parameters->SiblingNext || func->Parameters != func->Parameters->SiblingNext->SiblingNext) + { + Error(sl, "Random duration requires exactly 2 parameters"); + } + int v1 = IntConstFromNode(func->Parameters->Value, c->Type()); + int v2 = IntConstFromNode(static_cast(func->Parameters->SiblingNext)->Value, c->Type()); + if (v1 > v2) std::swap(v1, v2); + state.Tics = (int16_t)clamp(v1, 0, INT16_MAX); + state.TicRange = (uint16_t)clamp(v2 - v1, 0, UINT16_MAX); + } + else + { + state.Tics = (int16_t)IntConstFromNode(sl->Duration, c->Type()); + state.TicRange = 0; + } + if (sl->bBright) state.StateFlags |= STF_FULLBRIGHT; + if (sl->bFast) state.StateFlags |= STF_FAST; + if (sl->bSlow) state.StateFlags |= STF_SLOW; + if (sl->bCanRaise) state.StateFlags |= STF_CANRAISE; + if (sl->bNoDelay) state.StateFlags |= STF_NODELAY; + if (sl->bNoDelay) + { + if (statedef.GetStateLabelIndex(NAME_Spawn) != statedef.GetStateCount()) + { + Warn(sl, "NODELAY only has an effect on the first state after 'Spawn:'"); + } + } + if (sl->Offset != nullptr) + { + state.Misc1 = IntConstFromNode(sl->Offset, c->Type()); + state.Misc2 = IntConstFromNode(static_cast(sl->Offset->SiblingNext), c->Type()); + } + + if (sl->Lights != nullptr) + { + auto l = sl->Lights; + do + { + AddStateLight(&state, StringConstFromNode(l, c->Type())); + l = static_cast(l->SiblingNext); + } while (l != sl->Lights); + } + + if (sl->Action != nullptr) + { + auto code = SetupActionFunction(static_cast(c->ClassType()), sl->Action, state.UseFlags); + if (code != nullptr) + { + auto funcsym = CreateAnonymousFunction(c->Type(), nullptr, state.UseFlags); + state.ActionFunc = FunctionBuildList.AddFunction(OutNamespace, mVersion, funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), false, statedef.GetStateCount(), (int)sl->Frames->Len(), Lump); + } + } + + int count = statedef.AddStates(&state, sl->Frames->GetChars(), *sl); + if (count < 0) + { + Error(sl, "Invalid frame character string '%s'", sl->Frames->GetChars()); + count = -count; + } + break; + } + case AST_StateGoto: + { + auto sg = static_cast(st); + statename = ""; + if (sg->Qualifier != nullptr) + { + statename << FName(sg->Qualifier->Id).GetChars() << "::"; + } + auto part = sg->Label; + do + { + statename << FName(part->Id).GetChars() << '.'; + part = static_cast(part->SiblingNext); + } while (part != sg->Label); + statename.Truncate(statename.Len() - 1); // remove the last '.' in the label name + if (sg->Offset != nullptr) + { + int offset = IntConstFromNode(sg->Offset, c->Type()); + if (offset < 0) + { + Error(sg, "GOTO offset must be positive"); + offset = 0; + } + if (offset > 0) + { + statename.AppendFormat("+%d", offset); + } + } + if (!statedef.SetGotoLabel(statename)) + { + Error(sg, "GOTO before first state"); + } + break; + } + case AST_StateFail: + case AST_StateWait: + if (!statedef.SetWait()) + { + Error(st, "%s before first state", st->NodeType == AST_StateFail ? "Fail" : "Wait"); + } + break; + + case AST_StateLoop: + if (!statedef.SetLoop()) + { + Error(st, "LOOP before first state"); + } + break; + + case AST_StateStop: + if (!statedef.SetStop()) + { + Error(st, "STOP before first state"); + } + break; + + default: + assert(0 && "Bad AST node in state"); + } + st = static_cast(st->SiblingNext); + } while (st != s->Body); + } + try + { + FinalizeClass(c->ClassType(), statedef); + } + catch (CRecoverableError &err) + { + Error(c->cls, "%s", err.GetMessage()); + } + } +} + +//========================================================================== +// +// 'action' is a Doom specific keyword. The default implementation just +// errors out on it +// +//========================================================================== + +int ZCCDoomCompiler::CheckActionKeyword(ZCC_FuncDeclarator* f, uint32_t &varflags, int useflags, ZCC_StructWork *c) +{ + if (varflags & VARF_ReadOnly) + { + Error(f, "Action functions cannot be declared const"); + varflags &= ~VARF_ReadOnly; + } + if (varflags & VARF_UI) + { + Error(f, "Action functions cannot be declared UI"); + } + // Non-Actors cannot have action functions. + if (!isActor(c->Type())) + { + 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; + return 3; + } + else + { + return 1; + } +} + +//========================================================================== +// +// AActor needs the actor info manually added to its meta data +// before adding any scripted fields. +// +//========================================================================== + +bool ZCCDoomCompiler::PrepareMetaData(PClass *type) +{ + if (type->TypeName == NAME_Actor) + { + assert(type->MetaSize == 0); + AddActorInfo(type); + return true; + } + return false; +} + diff --git a/src/scripting/zscript/zcc_compile_doom.h b/src/scripting/zscript/zcc_compile_doom.h new file mode 100644 index 00000000000..24f1350c9b5 --- /dev/null +++ b/src/scripting/zscript/zcc_compile_doom.h @@ -0,0 +1,34 @@ +#pragma once +#include "zcc_compile.h" + +void SetImplicitArgs(TArray* args, TArray* argflags, TArray* argnames, PContainerType* cls, uint32_t funcflags, int useflags); + +class ZCCDoomCompiler : public ZCCCompiler +{ +public: + ZCCDoomCompiler(ZCC_AST &tree, DObject *outer, PSymbolTable &symbols, PNamespace *outnamespace, int lumpnum, const VersionInfo & ver) + : ZCCCompiler(tree, outer, symbols, outnamespace, lumpnum, ver) + {} + int Compile() override; +protected: + bool PrepareMetaData(PClass *type) override; + void SetImplicitArgs(TArray* args, TArray* argflags, TArray* argnames, PContainerType* cls, uint32_t funcflags, int useflags) override + { + ::SetImplicitArgs(args, argflags, argnames, cls, funcflags, useflags); + } +private: + void CompileAllProperties(); + bool CompileProperties(PClass *type, TArray &Properties, FName prefix); + bool CompileFlagDefs(PClass *type, TArray &Properties, FName prefix); + void DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag); + void DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag); + void ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *prop, Baggage &bag); + void ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg); + void InitDefaults(); + FxExpression *SetupActionFunction(PClass *cls, ZCC_TreeNode *af, int StateFlags); + void CompileStates(); + int CheckActionKeyword(ZCC_FuncDeclarator *f, uint32_t &varflags, int useflags, ZCC_StructWork *c); + +}; + + diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 91facfedce9..e176c646e3b 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -350,16 +350,20 @@ static void ParseSingleFile(FScanner *pSC, const char *filename, int lump, void //**-------------------------------------------------------------------------- -static void DoParse(int lumpnum) +PNamespace *ParseOneScript(const int baselump, ZCCParseState &state) { FScanner sc; void *parser; ZCCToken value; - auto baselump = lumpnum; + int lumpnum = baselump; auto fileno = fileSystem.GetFileContainer(lumpnum); + if (TokenMap.CountUsed() == 0) + { + InitTokenMap(); + } + parser = ZCCParseAlloc(malloc); - ZCCParseState state; #ifndef NDEBUG FILE *f = nullptr; @@ -471,37 +475,8 @@ static void DoParse(int lumpnum) } } - PSymbolTable symtable; auto newns = fileSystem.GetFileContainer(baselump) == 0 ? Namespaces.GlobalNamespace : Namespaces.NewNamespace(fileSystem.GetFileContainer(baselump)); - ZCCCompiler cc(state, NULL, symtable, newns, baselump, state.ParseVersion); - cc.Compile(); - - if (FScriptPosition::ErrorCounter > 0) - { - // Abort if the compiler produced any errors. Also do not compile further lumps, because they very likely miss some stuff. - I_Error("%d errors, %d warnings while compiling %s", FScriptPosition::ErrorCounter, FScriptPosition::WarnCounter, fileSystem.GetFileFullPath(baselump).GetChars()); - } - else if (FScriptPosition::WarnCounter > 0) - { - // If we got warnings, but no errors, print the information but continue. - Printf(TEXTCOLOR_ORANGE "%d warnings while compiling %s\n", FScriptPosition::WarnCounter, fileSystem.GetFileFullPath(baselump).GetChars()); - } - -} - -void ParseScripts() -{ - if (TokenMap.CountUsed() == 0) - { - InitTokenMap(); - } - int lump, lastlump = 0; - FScriptPosition::ResetErrorCounter(); - - while ((lump = fileSystem.FindLump("ZSCRIPT", &lastlump)) != -1) - { - DoParse(lump); - } + return newns; } static FString ZCCTokenName(int terminal) diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 3b0e9eb193f..2d89d0d9473 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -618,4 +618,7 @@ const char *GetMixinTypeString(EZCCMixinType type); ZCC_TreeNode *TreeNodeDeepCopy(ZCC_AST *ast, ZCC_TreeNode *orig, bool copySiblings); +// Main entry point for the parser. Returns some data needed by the compiler. +PNamespace* ParseOneScript(const int baselump, ZCCParseState& state); + #endif