From 3b198876372976e3dbb00002a36dd65354909194 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 11 Oct 2016 00:56:47 +0200 Subject: [PATCH] - implemented the Defaults property initializer. This uses the same property and flag tables as DECORATE with a few changes: * it sets the parse mode to strict, so that several DECORATE warnings are now errors. * trying to change a deprecated flag will print a warning. * setting of editor numbers, spawn and conversation ID id not possible. Use MAPINFO to do this. * all subclass flags must use the qualified name now (e.g. +ALWAYSPICKUP will print an error.) * the scriptable Damage property is not yet implemented. This will require a special case with a differently named property in the processing function because in the AST it is no longer possible to distinguish between a damage value and a constant damage function. --- src/dobjtype.cpp | 1 + src/g_shared/a_pickups.cpp | 2 +- src/sc_man.cpp | 3 +- src/sc_man.h | 1 + src/thingdef/thingdef.h | 5 +- src/thingdef/thingdef_data.cpp | 5 +- src/thingdef/thingdef_parse.cpp | 4 +- src/thingdef/thingdef_properties.cpp | 17 +- src/zscript/zcc-parse.lemon | 12 +- src/zscript/zcc_compile.cpp | 395 ++++++++++++++++++++++++++- src/zscript/zcc_compile.h | 18 +- 11 files changed, 438 insertions(+), 25 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index e27559902..7709bfa8f 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -3163,6 +3163,7 @@ PClass *PClass::FindClassTentative(FName name, bool fatal) type->TypeName = name; type->ParentClass = this; + type->ConstructNative = ConstructNative; type->Size = TentativeClass; type->bRuntimeClass = true; type->Symbols.SetParentTable(&Symbols); diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index e80e54818..00a6692b2 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1696,7 +1696,7 @@ PClassHealth::PClassHealth() //=========================================================================== // -// PClassHealth :: Derive +// PClassHealth :: DeriveData // //=========================================================================== diff --git a/src/sc_man.cpp b/src/sc_man.cpp index 3f5530ff3..97f384f3d 100644 --- a/src/sc_man.cpp +++ b/src/sc_man.cpp @@ -1006,6 +1006,7 @@ void FScanner::CheckOpen() // //========================================================================== int FScriptPosition::ErrorCounter; +bool FScriptPosition::StrictErrors; // makes all OPTERRPR messages real errors. FScriptPosition::FScriptPosition(const FScriptPosition &other) { @@ -1047,7 +1048,7 @@ void FScriptPosition::Message (int severity, const char *message, ...) const if ((severity == MSG_DEBUG || severity == MSG_DEBUGLOG) && developer < DMSG_NOTIFY) return; if (severity == MSG_OPTERROR) { - severity = strictdecorate ? MSG_ERROR : MSG_WARNING; + severity = StrictErrors || strictdecorate ? MSG_ERROR : MSG_WARNING; } if (message == NULL) diff --git a/src/sc_man.h b/src/sc_man.h index 359dd8140..c319a7a2f 100644 --- a/src/sc_man.h +++ b/src/sc_man.h @@ -140,6 +140,7 @@ enum struct FScriptPosition { static int ErrorCounter; + static bool StrictErrors; FString FileName; int ScriptLine; diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 3a419b112..426eddd41 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -25,7 +25,7 @@ struct FFlagDef int fieldsize; }; -FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2); +FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict = false); void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int index); bool CheckDeprecatedFlags(const AActor *actor, PClassActor *info, int index); const char *GetFlagName(unsigned int flagnum, int flagoffset); @@ -146,6 +146,7 @@ struct Baggage PClassActor *Info; bool DropItemSet; bool StateSet; + bool fromZScript; int CurrentState; int Lumpnum; FStateDefinitions statedef; @@ -272,7 +273,7 @@ typedef void (*PropHandler)(AActor *defaults, PClassActor *info, Baggage &bag, F enum ECategory { CAT_PROPERTY, // Inheritable property - CAT_INFO // non-inheritable info (spawn ID, Doomednum, game filter, conversation ID) + CAT_INFO // non-inheritable info (spawn ID, Doomednum, game filter, conversation ID, not usable in ZScript) }; struct FPropertyInfo diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index efdd64921..bdb992fba 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -436,14 +436,15 @@ static FFlagDef *FindFlag (FFlagDef *flags, int numflags, const char *flag) // //========================================================================== -FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2) +FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict) { FFlagDef *def; size_t i; if (part2 == NULL) { // Search all lists - for (i = 0; i < NUM_FLAG_LISTS; ++i) + int max = strict ? 1 : NUM_FLAG_LISTS; + for (i = 0; i < max; ++i) { if (type->IsDescendantOf (*FlagLists[i].Type)) { diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 963165ab1..5789038b7 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -1063,7 +1063,7 @@ static void ParseActorProperty(FScanner &sc, Baggage &bag) } else { - sc.ScriptMessage("\"%s\" requires an actor of type \"%s\"\n", propname.GetChars(), (*prop->cls)->TypeName.GetChars()); + sc.ScriptMessage("'%s' requires an actor of type '%s'\n", propname.GetChars(), (*prop->cls)->TypeName.GetChars()); FScriptPosition::ErrorCounter++; } } @@ -1073,7 +1073,7 @@ static void ParseActorProperty(FScanner &sc, Baggage &bag) } else { - sc.ScriptError("\"%s\" is an unknown actor property\n", propname.GetChars()); + sc.ScriptError("'%s' is an unknown actor property\n", propname.GetChars()); } } diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index d60cda823..bcae82671 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -514,10 +514,11 @@ DEFINE_INFO_PROPERTY(conversationid, IiI, Actor) //========================================================================== DEFINE_PROPERTY(skip_super, 0, Actor) { - if (info->IsDescendantOf(RUNTIME_CLASS(AInventory))) + auto actorclass = RUNTIME_CLASS(AActor); + if (info->Size != actorclass->Size) { - bag.ScriptPosition.Message(MSG_WARNING, - "'skip_super' in definition of inventory item '%s' ignored.", info->TypeName.GetChars() ); + bag.ScriptPosition.Message(MSG_OPTERROR, + "'skip_super' is only allowed in subclasses of AActor with no additional fields and will be ignored.", info->TypeName.GetChars()); return; } if (bag.StateSet) @@ -2484,7 +2485,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, face, S, PlayerPawn) ); if (!valid) { - bag.ScriptPosition.Message(MSG_WARNING, + bag.ScriptPosition.Message(MSG_OPTERROR, "Invalid face '%s' for '%s';\nSTF replacement codes must be 3 alphanumeric characters.\n", tmp.GetChars(), info->TypeName.GetChars ()); } @@ -2551,13 +2552,13 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, colorset, ISIIIiiiiiiiiiiiiiiiiiiiiiiii, Pl } if (count != 0) { - bag.ScriptPosition.Message(MSG_WARNING, "Extra ranges require 4 parameters each.\n"); + bag.ScriptPosition.Message(MSG_OPTERROR, "Extra ranges require 4 parameters each.\n"); } } if (setnum < 0) { - bag.ScriptPosition.Message(MSG_WARNING, "Color set number must not be negative.\n"); + bag.ScriptPosition.Message(MSG_OPTERROR, "Color set number must not be negative.\n"); } else { @@ -2584,7 +2585,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, colorsetfile, ISSI, PlayerPawn) if (setnum < 0) { - bag.ScriptPosition.Message(MSG_WARNING, "Color set number must not be negative.\n"); + bag.ScriptPosition.Message(MSG_OPTERROR, "Color set number must not be negative.\n"); } else if (color.Lump >= 0) { @@ -2602,7 +2603,7 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, clearcolorset, I, PlayerPawn) if (setnum < 0) { - bag.ScriptPosition.Message(MSG_WARNING, "Color set number must not be negative.\n"); + bag.ScriptPosition.Message(MSG_OPTERROR, "Color set number must not be negative.\n"); } else { diff --git a/src/zscript/zcc-parse.lemon b/src/zscript/zcc-parse.lemon index f54a4ba74..09fadedb4 100644 --- a/src/zscript/zcc-parse.lemon +++ b/src/zscript/zcc-parse.lemon @@ -528,7 +528,7 @@ default_statement_list(X) ::= default_statement_list(X) default_statement(B). default_statement(X) ::= SEMICOLON. { X = NULL; } //default_statement(X) ::= assign_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/ } -default_statement(X) ::= property_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/ } +default_statement(X) ::= property_statement(A). { X = A; /*X-overwrites-A*/ } default_statement(X) ::= flag_statement(A). { X = A; /*X-overwrites-A*/ } %type flag_statement { ZCC_FlagStmt *} @@ -550,7 +550,7 @@ flag_statement(X) ::= SUB dottable_id(A). %type property_statement{ZCC_PropertyStmt *} -property_statement(X) ::= dottable_id(A) expr_list(B). +property_statement(X) ::= dottable_id(A) expr_list(B) SEMICOLON. { NEW_AST_NODE(PropertyStmt,stmt,A); stmt->Prop = A; @@ -558,6 +558,14 @@ property_statement(X) ::= dottable_id(A) expr_list(B). X = stmt; } +property_statement(X) ::= dottable_id(A) SEMICOLON. +{ + NEW_AST_NODE(PropertyStmt,stmt,A); + stmt->Prop = A; + stmt->Values = nullptr; + X = stmt; +} + /* Type names */ diff --git a/src/zscript/zcc_compile.cpp b/src/zscript/zcc_compile.cpp index 7a8f7092d..98448069a 100644 --- a/src/zscript/zcc_compile.cpp +++ b/src/zscript/zcc_compile.cpp @@ -32,10 +32,12 @@ ** */ -#include "dobject.h" +#include "actor.h" +#include "thingdef.h" #include "sc_man.h" #include "c_console.h" #include "c_dispatch.h" +#include "doomerrors.h" #include "w_wad.h" #include "cmdlib.h" #include "m_alloc.h" @@ -110,8 +112,11 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode) // todo case AST_States: case AST_FuncDeclarator: + break; + case AST_Default: - break; + cls->Defaults.Push(static_cast(node)); + break; default: assert(0 && "Unhandled AST node type"); @@ -358,6 +363,7 @@ int ZCCCompiler::Compile() CreateStructTypes(); CompileAllConstants(); CompileAllFields(); + InitDefaults(); return ErrorCount; } @@ -1433,3 +1439,388 @@ PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, } while (val != arraysize); return baseType; } + +//========================================================================== +// +// ZCCCompiler :: GetInt - Input must be a constant expression +// +//========================================================================== + +int ZCCCompiler::GetInt(ZCC_Expression *expr) +{ + if (expr->Type == TypeError) + { + return 0; + } + const PType::Conversion *route[CONVERSION_ROUTE_SIZE]; + int routelen = expr->Type->FindConversion(TypeSInt32, route, countof(route)); + if (routelen < 0) + { + Error(expr, "Cannot convert to integer"); + return 0; + } + else + { + if (expr->Type->IsKindOf(RUNTIME_CLASS(PFloat))) + { + Warn(expr, "Truncation of floating point value"); + } + auto ex = static_cast(ApplyConversion(expr, route, routelen)); + return ex->IntVal; + } +} + +double ZCCCompiler::GetDouble(ZCC_Expression *expr) +{ + if (expr->Type == TypeError) + { + return 0; + } + const PType::Conversion *route[CONVERSION_ROUTE_SIZE]; + int routelen = expr->Type->FindConversion(TypeFloat64, route, countof(route)); + if (routelen < 0) + { + Error(expr, "Cannot convert to float"); + return 0; + } + else + { + auto ex = static_cast(ApplyConversion(expr, route, routelen)); + return ex->DoubleVal; + } +} + +const char *ZCCCompiler::GetString(ZCC_Expression *expr, bool silent) +{ + if (expr->Type == TypeError) + { + return 0; + } + else if (expr->Type->IsKindOf(RUNTIME_CLASS(PString))) + { + return static_cast(expr)->StringVal->GetChars(); + } + else if (expr->Type->IsKindOf(RUNTIME_CLASS(PName))) + { + // Ugh... What a mess... + return FName(ENamedName(static_cast(expr)->IntVal)).GetChars(); + } + else + { + if (!silent) Error(expr, "Cannot convert to string"); + return 0; + } +} + +//========================================================================== +// +// 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; + } + property->Values = Simplify(property->Values, &bag.Info->Symbols); // need to do this before the loop so that we can find the head node again. + const char * p = prop->params; + auto exp = property->Values; + + while (true) + { + FPropParam conv; + FPropParam pref; + + if (exp->NodeType != AST_ExprConstant) + { + Error(exp, "%s: non-constant parameter", prop->name); + } + conv.s = nullptr; + pref.s = nullptr; + pref.i = -1; + switch ((*p) & 223) + { + + case 'I': + 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. + 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 = GetInt(exp); + break; + + case 'F': + conv.d = GetDouble(exp); + break; + + case 'Z': // an optional string. Does not allow any numerical value. + if (!GetString(exp, true)) + { + // apply this expression to the next argument on the list. + params.Push(conv); + params[0].i++; + p++; + continue; + } + // fall through + + case 'C': // this parser accepts colors only in string form. + pref.i = 1; + case 'S': + conv.s = GetString(exp); + break; + + case 'T': // a filtered string + conv.s = strings[strings.Reserve(1)] = strbin1(GetString(exp)); + break; + + case 'L': // Either a number or a list of strings + if (!GetString(exp, true)) + { + pref.i = 0; + conv.i = GetInt(exp); + } + else + { + pref.i = 1; + params.Push(pref); + params[0].i++; + + do + { + conv.s = GetString(exp); + if (conv.s != nullptr) + { + params.Push(conv); + params[0].i++; + } + exp = Simplify(static_cast(exp->SiblingNext), &bag.Info->Symbols); + } 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 = Simplify(static_cast(exp->SiblingNext), &bag.Info->Symbols); + endofparm: + p++; + // Skip the DECORATE 'no comma' marker + if (*p == '_') p++; + + else if (*p == 0) + { + if (exp != property->Values) + { + Error(property, "Too many values for '%s'", prop->name); + } + break; + } + else if (exp == property->Values) + { + if (*p < 'a') + { + Error(property, "Insufficient parameters for %s", prop->name); + } + 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 +// +//========================================================================== + +void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *prop, Baggage &bag) +{ + auto namenode = prop->Prop; + FString propname; + + if (namenode->SiblingNext == namenode) + { + // a one-name property + propname = FName(namenode->Id); + } + else if (namenode->SiblingNext->SiblingNext == namenode) + { + // a two-name property + propname << FName(namenode->Id) << "." << FName(static_cast(namenode->SiblingNext)->Id); + } + else + { + Error(prop, "Property name may at most contain two parts"); + return; + } + + FPropertyInfo *property = FindProperty(propname); + + if (property != nullptr && property->category != CAT_INFO) + { + if (cls->IsDescendantOf(*property->cls)) + { + DispatchProperty(property, prop, (AActor *)bag.Info->Defaults, bag); + } + else + { + Error(prop, "'%s' requires an actor of type '%s'\n", propname.GetChars(), (*property->cls)->TypeName.GetChars()); + } + } + else + { + 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->structoffset == -1) + { + Warn(flg, "Deprecated flag '%s%s%s' used", n1, n2 ? "." : "", n2 ? n2 : ""); + 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->Type()->IsDescendantOf(RUNTIME_CLASS(AActor))) + { + if (c->Defaults.Size()) Error(c->cls, "%s: Non-actor classes may not have defaults", c->Type()->TypeName.GetChars()); + } + else + { + // This should never happen. + if (c->Type()->Defaults != nullptr) + { + Error(c->cls, "%s already has defaults", c->Type()->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 (c->Type()->ParentClass->Defaults == nullptr && c->Type() != RUNTIME_CLASS(AActor)) + { + Error(c->cls, "Parent class %s of %s is not initialized", c->Type()->ParentClass->TypeName.GetChars(), c->Type()->TypeName.GetChars()); + } + else + { + // Copy the parent's defaults and meta data. + auto ti = static_cast(c->Type()); + ti->InitializeNativeDefaults(); + ti->ParentClass->DeriveData(ti); + + + Baggage bag; + #ifdef _DEBUG + bag.ClassName = c->Type()->TypeName; + #endif + bag.Info = ti; + bag.DropItemSet = false; + bag.StateSet = false; + bag.fromZScript = true; + bag.CurrentState = 0; + bag.Lumpnum = Wads.CheckNumForFullName(*c->cls->SourceName, true); + bag.DropItemList = nullptr; + bag.ScriptPosition.StrictErrors = true; + // The actual script position needs to be set per property. + + for (auto d : c->Defaults) + { + auto content = d->Content; + 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; + } + content = static_cast(content->SiblingNext); + } while (content != d->Content); + } + } + } + } +} + diff --git a/src/zscript/zcc_compile.h b/src/zscript/zcc_compile.h index 2fb83d7e6..9edd65b1d 100644 --- a/src/zscript/zcc_compile.h +++ b/src/zscript/zcc_compile.h @@ -1,6 +1,10 @@ #ifndef ZCC_COMPILE_H #define ZCC_COMPILE_H +struct Baggage; +struct FPropertyInfo; +class AActor; + struct ZCC_StructWork { PSymbolTable TreeNodes; @@ -40,6 +44,7 @@ struct ZCC_ClassWork TArray Enums; TArray Constants; TArray Fields; + TArray Defaults; ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n) { @@ -89,6 +94,14 @@ private: PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PSymbolTable *sym); PType *ResolveUserType(ZCC_BasicType *type, PSymbolTable *sym); + void InitDefaults(); + void ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg); + void ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *flg, Baggage &bag); + void DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *pex, AActor *defaults, Baggage &bag); + int GetInt(ZCC_Expression *expr); + double GetDouble(ZCC_Expression *expr); + const char *GetString(ZCC_Expression *expr, bool silent = false); + TArray Constants; TArray Structs; TArray Classes; @@ -103,11 +116,6 @@ private: ZCC_OpProto *PromoteUnary(EZCCExprType op, ZCC_Expression *&expr); ZCC_OpProto *PromoteBinary(EZCCExprType op, ZCC_Expression *&left, ZCC_Expression *&right); - void PromoteToInt(ZCC_Expression *&expr); - void PromoteToUInt(ZCC_Expression *&expr); - void PromoteToDouble(ZCC_Expression *&expr); - void PromoteToString(ZCC_Expression *&expr); - ZCC_Expression *ApplyConversion(ZCC_Expression *expr, const PType::Conversion **route, int routelen); ZCC_Expression *AddCastNode(PType *type, ZCC_Expression *expr);