- added property definitions to the ZScript parser. This will allow defining custom properties for the default block in custom base classes. See 'Health' for an example.

- support transient object member variables for information that does not need to be put in a savegame.
- fixed: special initialization of objects needs to pass the proper defaults along, otherwise the parent classes will use their own, inappropriate one.
This commit is contained in:
Christoph Oelckers 2017-01-15 16:55:30 +01:00
parent 0c3aab794f
commit 179b6e1a39
21 changed files with 313 additions and 109 deletions

View file

@ -360,7 +360,7 @@ void DBot::WhatToGet (AActor *item)
} }
else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere) else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere)
return; return;
else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && player->mo->health >= player->mo->GetMaxHealth() + player->mo->stamina) else if (item->IsKindOf (PClass::FindActor(NAME_Health)) && player->mo->health >= player->mo->GetMaxHealth() + player->mo->stamina)
return; return;
if ((dest == NULL || if ((dest == NULL ||

View file

@ -1971,21 +1971,21 @@ static int PatchMisc (int dummy)
barmor->MaxSaveAmount = deh.MaxArmor; barmor->MaxSaveAmount = deh.MaxArmor;
} }
AHealth *health; AInventory *health;
health = static_cast<AHealth *> (GetDefaultByName ("HealthBonus")); health = static_cast<AInventory *> (GetDefaultByName ("HealthBonus"));
if (health!=NULL) if (health!=NULL)
{ {
health->MaxAmount = 2 * deh.MaxHealth; health->MaxAmount = 2 * deh.MaxHealth;
} }
health = static_cast<AHealth *> (GetDefaultByName ("Soulsphere")); health = static_cast<AInventory *> (GetDefaultByName ("Soulsphere"));
if (health!=NULL) if (health!=NULL)
{ {
health->Amount = deh.SoulsphereHealth; health->Amount = deh.SoulsphereHealth;
health->MaxAmount = deh.MaxSoulsphere; health->MaxAmount = deh.MaxSoulsphere;
} }
health = static_cast<AHealth *> (GetDefaultByName ("MegasphereHealth")); health = static_cast<AInventory *> (GetDefaultByName ("MegasphereHealth"));
if (health!=NULL) if (health!=NULL)
{ {
health->Amount = health->MaxAmount = deh.MegasphereHealth; health->Amount = health->MaxAmount = deh.MegasphereHealth;

View file

@ -566,3 +566,16 @@ DEFINE_ACTION_FUNCTION(DObject, GetClassName)
PARAM_SELF_PROLOGUE(DObject); PARAM_SELF_PROLOGUE(DObject);
ACTION_RETURN_INT(self->GetClass()->TypeName); ACTION_RETURN_INT(self->GetClass()->TypeName);
} }
void *DObject::ScriptVar(FName field, PType *type)
{
auto sym = dyn_cast<PField>(GetClass()->Symbols.FindSymbol(field, true));
if (sym && sym->Type == type)
{
return (((char*)this) + sym->Offset);
}
// This is only for internal use so I_Error is fine.
I_Error("Variable %s not found in %s\n", field.GetChars(), GetClass()->TypeName.GetChars());
return nullptr;
}

View file

@ -39,7 +39,7 @@
#include "i_system.h" #include "i_system.h"
class PClass; class PClass;
class PType;
class FSerializer; class FSerializer;
class DObject; class DObject;
@ -94,7 +94,6 @@ enum
CLASSREG_PClass, CLASSREG_PClass,
CLASSREG_PClassActor, CLASSREG_PClassActor,
CLASSREG_PClassInventory, CLASSREG_PClassInventory,
CLASSREG_PClassHealth,
CLASSREG_PClassPuzzleItem, CLASSREG_PClassPuzzleItem,
CLASSREG_PClassWeapon, CLASSREG_PClassWeapon,
CLASSREG_PClassPlayerPawn, CLASSREG_PClassPlayerPawn,
@ -453,6 +452,8 @@ public:
DObject *GCNext; // Next object in this collection list DObject *GCNext; // Next object in this collection list
uint32 ObjectFlags; // Flags for this object uint32 ObjectFlags; // Flags for this object
void *ScriptVar(FName field, PType *type);
public: public:
DObject (); DObject ();
DObject (PClass *inClass); DObject (PClass *inClass);
@ -476,6 +477,10 @@ public:
virtual void OnDestroy() {} virtual void OnDestroy() {}
void Destroy(); void Destroy();
// Add other types as needed.
int &IntVar(FName field);
double &FloatVar(FName field);
// If you need to replace one object with another and want to // If you need to replace one object with another and want to
// change any pointers from the old object to the new object, // change any pointers from the old object to the new object,
// use this method. // use this method.

View file

@ -2609,6 +2609,27 @@ PField::PField(FName name, PType *type, DWORD flags, size_t offset, int bitvalue
else BitValue = -1; else BitValue = -1;
} }
/* PProperty *****************************************************************/
IMPLEMENT_CLASS(PProperty, false, false)
//==========================================================================
//
// PField - Default Constructor
//
//==========================================================================
PProperty::PProperty()
: PSymbol(NAME_None)
{
}
PProperty::PProperty(FName name, TArray<PField *> &fields)
: PSymbol(name)
{
Variables = std::move(fields);
}
/* PPrototype *************************************************************/ /* PPrototype *************************************************************/
IMPLEMENT_CLASS(PPrototype, false, false) IMPLEMENT_CLASS(PPrototype, false, false)
@ -3094,7 +3115,6 @@ PClass *ClassReg::RegisterClass()
&PClass::RegistrationInfo, &PClass::RegistrationInfo,
&PClassActor::RegistrationInfo, &PClassActor::RegistrationInfo,
&PClassInventory::RegistrationInfo, &PClassInventory::RegistrationInfo,
&PClassHealth::RegistrationInfo,
&PClassPuzzleItem::RegistrationInfo, &PClassPuzzleItem::RegistrationInfo,
&PClassWeapon::RegistrationInfo, &PClassWeapon::RegistrationInfo,
&PClassPlayerPawn::RegistrationInfo, &PClassPlayerPawn::RegistrationInfo,
@ -3234,7 +3254,7 @@ DObject *PClass::CreateNew() const
ConstructNative (mem); ConstructNative (mem);
((DObject *)mem)->SetClass (const_cast<PClass *>(this)); ((DObject *)mem)->SetClass (const_cast<PClass *>(this));
InitializeSpecials(mem); InitializeSpecials(mem, Defaults);
return (DObject *)mem; return (DObject *)mem;
} }
@ -3246,7 +3266,7 @@ DObject *PClass::CreateNew() const
// //
//========================================================================== //==========================================================================
void PClass::InitializeSpecials(void *addr) const void PClass::InitializeSpecials(void *addr, void *defaults) const
{ {
// Once we reach a native class, we can stop going up the family tree, // Once we reach a native class, we can stop going up the family tree,
// since native classes handle initialization natively. // since native classes handle initialization natively.
@ -3255,10 +3275,10 @@ void PClass::InitializeSpecials(void *addr) const
return; return;
} }
assert(ParentClass != NULL); assert(ParentClass != NULL);
ParentClass->InitializeSpecials(addr); ParentClass->InitializeSpecials(addr, defaults);
for (auto tao : SpecialInits) for (auto tao : SpecialInits)
{ {
tao.first->InitializeValue((BYTE*)addr + tao.second, Defaults == nullptr? nullptr : Defaults + tao.second); tao.first->InitializeValue((char*)addr + tao.second, defaults == nullptr? nullptr : ((char*)defaults) + tao.second);
} }
} }
@ -3332,7 +3352,7 @@ void PClass::InitializeDefaults()
{ {
// Copy parent values from the parent defaults. // Copy parent values from the parent defaults.
assert(ParentClass != NULL); assert(ParentClass != NULL);
ParentClass->InitializeSpecials(Defaults); ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults);
for (const PField *field : Fields) for (const PField *field : Fields)
{ {
@ -3923,6 +3943,13 @@ PSymbol *PSymbolTable::AddSymbol (PSymbol *sym)
return sym; return sym;
} }
void PSymbolTable::RemoveSymbol(PSymbol *sym)
{
auto mysym = Symbols.CheckKey(sym->SymbolName);
if (mysym == nullptr || *mysym != sym) return;
Symbols.Remove(sym->SymbolName);
}
PSymbol *PSymbolTable::ReplaceSymbol(PSymbol *newsym) PSymbol *PSymbolTable::ReplaceSymbol(PSymbol *newsym)
{ {
// If a symbol with a matching name exists, take its place and return it. // If a symbol with a matching name exists, take its place and return it.

View file

@ -29,11 +29,12 @@ enum
VARF_In = (1<<10), VARF_In = (1<<10),
VARF_Out = (1<<11), VARF_Out = (1<<11),
VARF_Implicit = (1<<12), // implicitly created parameters (i.e. do not compare types when checking function signatures) VARF_Implicit = (1<<12), // implicitly created parameters (i.e. do not compare types when checking function signatures)
VARF_Static = (1<<13), // static class data (by necessity read only.) VARF_Static = (1<<13),
VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code. VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code.
VARF_Override = (1<<15), // overrides a virtual function from the parent class. VARF_Override = (1<<15), // overrides a virtual function from the parent class.
VARF_Ref = (1<<16), // argument is passed by reference. VARF_Ref = (1<<16), // argument is passed by reference.
VARF_Transient = (1<<17) // don't auto serialize field. VARF_Transient = (1<<17), // don't auto serialize field.
VARF_Meta = (1<<18), // static class data (by necessity read only.)
}; };
// Symbol information ------------------------------------------------------- // Symbol information -------------------------------------------------------
@ -137,6 +138,8 @@ struct PSymbolTable
// to be in the table with this name, if any. // to be in the table with this name, if any.
PSymbol *ReplaceSymbol(PSymbol *sym); PSymbol *ReplaceSymbol(PSymbol *sym);
void RemoveSymbol(PSymbol *sym);
// Frees all symbols from this table. // Frees all symbols from this table.
void ReleaseSymbols(); void ReleaseSymbols();
@ -621,6 +624,21 @@ protected:
PField(); PField();
}; };
// Struct/class fields ------------------------------------------------------
// A PField describes a symbol that takes up physical space in the struct.
class PProperty : public PSymbol
{
DECLARE_CLASS(PProperty, PSymbol);
public:
PProperty(FName name, TArray<PField *> &variables);
TArray<PField *> Variables;
protected:
PProperty();
};
// Compound types ----------------------------------------------------------- // Compound types -----------------------------------------------------------
class PEnum : public PNamedType class PEnum : public PNamedType
@ -807,7 +825,7 @@ protected:
enum { MetaClassNum = CLASSREG_PClassClass }; enum { MetaClassNum = CLASSREG_PClassClass };
TArray<FTypeAndOffset> SpecialInits; TArray<FTypeAndOffset> SpecialInits;
void Derive(PClass *newclass, FName name); void Derive(PClass *newclass, FName name);
void InitializeSpecials(void *addr) const; void InitializeSpecials(void *addr, void *defaults) const;
void SetSuper(); void SetSuper();
public: public:
typedef PClassClass MetaClass; typedef PClassClass MetaClass;
@ -1041,4 +1059,15 @@ enum ETypeVal : BYTE
VAL_Class, VAL_Class,
}; };
inline int &DObject::IntVar(FName field)
{
return *(int*)ScriptVar(field, TypeSInt32);
}
inline double &DObject::FloatVar(FName field)
{
return *(double*)ScriptVar(field, TypeFloat64);
}
#endif #endif

View file

@ -45,42 +45,7 @@
// //
//=========================================================================== //===========================================================================
IMPLEMENT_CLASS(PClassHealth, false, false)
IMPLEMENT_CLASS(AHealth, false, false)
DEFINE_FIELD(AHealth, PrevHealth)
DEFINE_FIELD(PClassHealth, LowHealth)
DEFINE_FIELD(PClassHealth, LowHealthMessage)
//===========================================================================
//
// PClassHealth Constructor
//
//===========================================================================
PClassHealth::PClassHealth()
{
LowHealth = 0;
}
//===========================================================================
//
// PClassHealth :: DeriveData
//
//===========================================================================
void PClassHealth::DeriveData(PClass *newclass)
{
assert(newclass->IsKindOf(RUNTIME_CLASS(PClassHealth)));
Super::DeriveData(newclass);
PClassHealth *newc = static_cast<PClassHealth *>(newclass);
newc->LowHealth = LowHealth;
newc->LowHealthMessage = LowHealthMessage;
}
IMPLEMENT_CLASS(AHealthPickup, false, false) IMPLEMENT_CLASS(AHealthPickup, false, false)
DEFINE_FIELD(AHealthPickup, autousemode) DEFINE_FIELD(AHealthPickup, autousemode)
//=========================================================================== //===========================================================================

View file

@ -2,27 +2,6 @@
#include "a_pickups.h" #include "a_pickups.h"
// Health is some item that gives the player health when picked up.
class PClassHealth : public PClassInventory
{
DECLARE_CLASS(PClassHealth, PClassInventory)
protected:
public:
PClassHealth();
virtual void DeriveData(PClass *newclass);
FString LowHealthMessage;
int LowHealth;
};
class AHealth : public AInventory
{
DECLARE_CLASS_WITH_META(AHealth, AInventory, PClassHealth)
public:
int PrevHealth;
};
// HealthPickup is some item that gives the player health when used. // HealthPickup is some item that gives the player health when used.
class AHealthPickup : public AInventory class AHealthPickup : public AInventory
{ {

View file

@ -2400,7 +2400,7 @@ static bool DoGiveInventory(AActor *receiver, bool orresult, VM_ARGS)
{ {
return false; return false;
} }
if (item->IsKindOf(RUNTIME_CLASS(AHealth))) if (item->IsKindOf(PClass::FindActor(NAME_Health)))
{ {
item->Amount *= amount; item->Amount *= amount;
} }
@ -5655,7 +5655,7 @@ static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amo
if ((flags & RGF_NOSIGHT) || P_CheckSight(thing, self, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) if ((flags & RGF_NOSIGHT) || P_CheckSight(thing, self, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY))
{ // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight. { // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight.
AInventory *gift = static_cast<AInventory *>(Spawn(item)); AInventory *gift = static_cast<AInventory *>(Spawn(item));
if (gift->IsKindOf(RUNTIME_CLASS(AHealth))) if (gift->IsKindOf(PClass::FindActor(NAME_Health)))
{ {
gift->Amount *= amount; gift->Amount *= amount;
} }

View file

@ -1341,7 +1341,7 @@ bool P_GiveBody(AActor *actor, int num, int max)
if (player != NULL) if (player != NULL)
{ {
// Max is 0 by default, preserving default behavior for P_GiveBody() // Max is 0 by default, preserving default behavior for P_GiveBody()
// calls while supporting AHealth. // calls while supporting health pickups.
if (max <= 0) if (max <= 0)
{ {
max = static_cast<APlayerPawn*>(actor)->GetMaxHealth() + player->mo->stamina; max = static_cast<APlayerPawn*>(actor)->GetMaxHealth() + player->mo->stamina;
@ -1396,7 +1396,7 @@ bool P_GiveBody(AActor *actor, int num, int max)
else else
{ {
// Parameter value for max is ignored on monsters, preserving original // Parameter value for max is ignored on monsters, preserving original
// behaviour on AHealth as well as on existing calls to P_GiveBody(). // behaviour of health as well as on existing calls to P_GiveBody().
max = actor->SpawnHealth(); max = actor->SpawnHealth();
if (num < 0) if (num < 0)
{ {
@ -5663,9 +5663,10 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
// [RH] Other things that shouldn't be spawned depending on dmflags // [RH] Other things that shouldn't be spawned depending on dmflags
if (deathmatch || alwaysapplydmflags) if (deathmatch || alwaysapplydmflags)
{ {
// Fixme: This needs to be done differently, it's quite broken.
if (dmflags & DF_NO_HEALTH) if (dmflags & DF_NO_HEALTH)
{ {
if (i->IsDescendantOf (RUNTIME_CLASS(AHealth))) if (i->IsDescendantOf (PClass::FindActor(NAME_Health)))
return NULL; return NULL;
if (i->TypeName == NAME_Berserk) if (i->TypeName == NAME_Berserk)
return NULL; return NULL;

View file

@ -146,7 +146,7 @@ std2:
'instanceof' { RET(TK_InstanceOf); } 'instanceof' { RET(TK_InstanceOf); }
'auto' { RET(TK_Auto); } 'auto' { RET(TK_Auto); }
'exec' { RET(TK_Exec); } 'exec' { RET(TK_Exec); }
'defaultproperties' { RET(TK_DefaultProperties); } 'property' { RET(TK_Property); }
'native' { RET(TK_Native); } 'native' { RET(TK_Native); }
'var' { RET(TK_Var); } 'var' { RET(TK_Var); }
'out' { RET(TK_Out); } 'out' { RET(TK_Out); }

View file

@ -67,6 +67,7 @@ xx(TK_Long, "'long'")
xx(TK_ULong, "'ulong'") xx(TK_ULong, "'ulong'")
xx(TK_Void, "'void'") xx(TK_Void, "'void'")
xx(TK_Struct, "'struct'") xx(TK_Struct, "'struct'")
xx(TK_Property, "'property'")
xx(TK_Class, "'class'") xx(TK_Class, "'class'")
xx(TK_Enum, "'enum'") xx(TK_Enum, "'enum'")
xx(TK_Name, "'name'") xx(TK_Name, "'name'")

View file

@ -208,10 +208,6 @@ ExpEmit::ExpEmit(VMFunctionBuilder *build, int type, int count)
void ExpEmit::Free(VMFunctionBuilder *build) void ExpEmit::Free(VMFunctionBuilder *build)
{ {
if (RegType == REGT_INT && RegNum == 0)
{
int a = 0;
}
if (!Fixed && !Konst && RegType <= REGT_TYPE) if (!Fixed && !Konst && RegType <= REGT_TYPE)
{ {
build->Registers[RegType].Return(RegNum, RegCount); build->Registers[RegType].Return(RegNum, RegCount);
@ -4447,10 +4443,6 @@ FxExpression *FxDynamicCast::Resolve(FCompileContext& ctx)
{ {
CHECKRESOLVED(); CHECKRESOLVED();
SAFE_RESOLVE(expr, ctx); SAFE_RESOLVE(expr, ctx);
if (expr->ExprType == EFX_GetDefaultByType)
{
int a = 0;
}
bool constflag = expr->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer *>(expr->ValueType)->IsConst; bool constflag = expr->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer *>(expr->ValueType)->IsConst;
if (constflag) if (constflag)
{ {
@ -6472,7 +6464,7 @@ FxStructMember::~FxStructMember()
bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable)
{ {
// Cannot take the address of metadata variables. // Cannot take the address of metadata variables.
if (membervar->Flags & VARF_Static) if (membervar->Flags & VARF_Meta)
{ {
return false; return false;
} }
@ -6615,7 +6607,7 @@ ExpEmit FxStructMember::Emit(VMFunctionBuilder *build)
obj = newobj; obj = newobj;
} }
if (membervar->Flags & VARF_Static) if (membervar->Flags & VARF_Meta)
{ {
obj.Free(build); obj.Free(build);
ExpEmit meta(build, REGT_POINTER); ExpEmit meta(build, REGT_POINTER);

View file

@ -1969,18 +1969,6 @@ DEFINE_CLASS_PROPERTY(givequest, I, Inventory)
static_cast<PClassInventory *>(info)->GiveQuest = i; static_cast<PClassInventory *>(info)->GiveQuest = i;
} }
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(lowmessage, IT, Health)
{
PROP_INT_PARM(i, 0);
PROP_STRING_PARM(str, 1);
assert(info->IsKindOf(RUNTIME_CLASS(PClassHealth)));
static_cast<PClassHealth *>(info)->LowHealth = i;
static_cast<PClassHealth *>(info)->LowHealthMessage = str;
}
//========================================================================== //==========================================================================
// //
//========================================================================== //==========================================================================

View file

@ -336,6 +336,16 @@ static void PrintStruct(FLispString &out, ZCC_TreeNode *node)
out.Close(); out.Close();
} }
static void PrintProperty(FLispString &out, ZCC_TreeNode *node)
{
ZCC_Property *snode = (ZCC_Property *)node;
out.Break();
out.Open("property");
out.AddName(snode->NodeName);
PrintNodes(out, snode->Body, false, true);
out.Close();
}
static void PrintEnum(FLispString &out, ZCC_TreeNode *node) static void PrintEnum(FLispString &out, ZCC_TreeNode *node)
{ {
ZCC_Enum *enode = (ZCC_Enum *)node; ZCC_Enum *enode = (ZCC_Enum *)node;
@ -934,6 +944,7 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode *
PrintVectorInitializer, PrintVectorInitializer,
PrintDeclFlags, PrintDeclFlags,
PrintExprClassCast, PrintExprClassCast,
PrintProperty,
}; };
FString ZCC_PrintAST(ZCC_TreeNode *root) FString ZCC_PrintAST(ZCC_TreeNode *root)

View file

@ -209,7 +209,6 @@ class_ancestry(X) ::= COLON dottable_id(A). { X = A; /*X-overwrites-A*/ }
class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; } class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; }
class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; } class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; }
class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; } class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; }
class_flags(X) ::= class_flags(A) ACTION. { X.Flags = A.Flags | ZCC_Action; X.Replaces = A.Replaces; }
class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; } class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; }
/*----- Dottable Identifier -----*/ /*----- Dottable Identifier -----*/
@ -265,6 +264,7 @@ class_body(X) ::= LBRACE class_innards(A) RBRACE. { X = A; /*X-overwrites-A*/ }
class_innards(X) ::= . { X = NULL; } class_innards(X) ::= . { X = NULL; }
class_innards(X) ::= class_innards(X) class_member(B). { SAFE_APPEND(X,B); } class_innards(X) ::= class_innards(X) class_member(B). { SAFE_APPEND(X,B); }
%type property_def{ZCC_Property *}
%type struct_def{ZCC_Struct *} %type struct_def{ZCC_Struct *}
%type enum_def {ZCC_Enum *} %type enum_def {ZCC_Enum *}
%type states_def {ZCC_States *} %type states_def {ZCC_States *}
@ -276,6 +276,7 @@ class_member(X) ::= struct_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= states_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= states_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= default_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= default_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ }
class_member(X) ::= property_def(A). { X = A; /*X-overwrites-A*/ }
/*----- Struct Definition -----*/ /*----- Struct Definition -----*/
/* Structs can define variables and enums. */ /* Structs can define variables and enums. */
@ -283,6 +284,30 @@ class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ }
%type opt_struct_body{ZCC_TreeNode *} %type opt_struct_body{ZCC_TreeNode *}
%type struct_body{ZCC_TreeNode *} %type struct_body{ZCC_TreeNode *}
%type struct_member{ZCC_TreeNode *} %type struct_member{ZCC_TreeNode *}
%type identifier_list{ZCC_Identifier *}
property_def(X) ::= PROPERTY(T) IDENTIFIER(A) COLON identifier_list(B) SEMICOLON.
{
NEW_AST_NODE(Property,def,T);
def->NodeName = A.Name();
def->Body = B;
X = def;
}
identifier_list(X) ::= IDENTIFIER(A).
{
NEW_AST_NODE(Identifier,id,A);
id->Id = A.Name();
X = id;
}
identifier_list(X) ::= states_opt(A) COMMA IDENTIFIER(B).
{
NEW_AST_NODE(Identifier,id,B);
id->Id = B.Name();
X = A; /*X-overwrites-A*/
AppendTreeNodeSibling(X, id);
}
struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body(B) RBRACE opt_semicolon. struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body(B) RBRACE opt_semicolon.
{ {
@ -747,7 +772,7 @@ type_name(X) ::= DOT dottable_id(A).
/* Type names can also be used as identifiers in contexts where type names /* Type names can also be used as identifiers in contexts where type names
* are not normally allowed. */ * are not normally allowed. */
%fallback IDENTIFIER %fallback IDENTIFIER
SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 NAME MAP ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16. SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 NAME MAP ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16 PROPERTY.
/* Aggregate types */ /* Aggregate types */
%type aggregate_type {ZCC_Type *} %type aggregate_type {ZCC_Type *}
@ -964,6 +989,7 @@ decl_flag(X) ::= PROTECTED(T). { X.Int = ZCC_Protected; X.SourceLoc = T.SourceL
decl_flag(X) ::= LATENT(T). { X.Int = ZCC_Latent; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= LATENT(T). { X.Int = ZCC_Latent; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= FINAL(T). { X.Int = ZCC_Final; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= FINAL(T). { X.Int = ZCC_Final; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= META(T). { X.Int = ZCC_Meta; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= META(T). { X.Int = ZCC_Meta; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= TRANSIENT(T). { X.Int = ZCC_Transient; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= READONLY(T). { X.Int = ZCC_ReadOnly; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= READONLY(T). { X.Int = ZCC_ReadOnly; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.SourceLoc; }
decl_flag(X) ::= VIRTUAL(T). { X.Int = ZCC_Virtual; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= VIRTUAL(T). { X.Int = ZCC_Virtual; X.SourceLoc = T.SourceLoc; }

View file

@ -120,6 +120,10 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode)
} }
break; break;
case AST_Property:
cls->Properties.Push(static_cast<ZCC_Property *>(node));
break;
case AST_VarDeclarator: case AST_VarDeclarator:
cls->Fields.Push(static_cast<ZCC_VarDeclarator *>(node)); cls->Fields.Push(static_cast<ZCC_VarDeclarator *>(node));
break; break;
@ -396,6 +400,7 @@ int ZCCCompiler::Compile()
CreateStructTypes(); CreateStructTypes();
CompileAllConstants(); CompileAllConstants();
CompileAllFields(); CompileAllFields();
CompileAllProperties();
InitDefaults(); InitDefaults();
InitFunctions(); InitFunctions();
CompileStates(); CompileStates();
@ -1269,6 +1274,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
if (field->Flags & ZCC_Protected) varflags |= VARF_Protected; if (field->Flags & ZCC_Protected) varflags |= VARF_Protected;
if (field->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated; if (field->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
if (field->Flags & ZCC_ReadOnly) varflags |= VARF_ReadOnly; if (field->Flags & ZCC_ReadOnly) varflags |= VARF_ReadOnly;
if (field->Flags & ZCC_Transient) varflags |= VARF_Transient;
if (field->Flags & ZCC_Native) if (field->Flags & ZCC_Native)
{ {
@ -1277,7 +1283,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
if (field->Flags & ZCC_Meta) if (field->Flags & ZCC_Meta)
{ {
varflags |= VARF_Static|VARF_ReadOnly; // metadata implies readonly varflags |= VARF_Meta | VARF_Static | VARF_ReadOnly; // metadata implies readonly
if (!(field->Flags & ZCC_Native)) if (!(field->Flags & ZCC_Native))
{ {
// Non-native meta data is not implemented yet and requires some groundwork in the class copy code. // Non-native meta data is not implemented yet and requires some groundwork in the class copy code.
@ -1303,7 +1309,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
if (varflags & VARF_Native) if (varflags & VARF_Native)
{ {
auto querytype = (varflags & VARF_Static) ? type->GetClass() : type; auto querytype = (varflags & VARF_Meta) ? type->GetClass() : type;
fd = FindField(querytype, FName(name->Name).GetChars()); fd = FindField(querytype, FName(name->Name).GetChars());
if (fd == nullptr) if (fd == nullptr)
{ {
@ -1337,6 +1343,68 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
return Fields.Size() == 0; return Fields.Size() == 0;
} }
//==========================================================================
//
// ZCCCompiler :: CompileAllProperties
//
// builds the property lists of all actor classes
//
//==========================================================================
void ZCCCompiler::CompileAllProperties()
{
for (auto c : Classes)
{
if (c->Properties.Size() > 0)
CompileProperties(c->Type(), c->Properties, c->Type()->TypeName);
}
}
//==========================================================================
//
// ZCCCompiler :: CompileProperties
//
// builds the internal structure of a single class or struct
//
//==========================================================================
bool ZCCCompiler::CompileProperties(PClass *type, TArray<ZCC_Property *> &Properties, FName prefix)
{
if (!type->IsKindOf(RUNTIME_CLASS(PClassActor)))
{
Error(Properties[0], "Properties can only be defined for actors");
return false;
}
for(auto p : Properties)
{
TArray<PField *> fields;
ZCC_Identifier *id = (ZCC_Identifier *)p->Body;
do
{
auto f = dyn_cast<PField>(type->Symbols.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.
if (prefix == NAME_None) qualifiedname.Format("@property@%s", FName(p->NodeName).GetChars());
else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), FName(p->NodeName).GetChars());
fields.ShrinkToFit();
if (!type->Symbols.AddSymbol(new PProperty(qualifiedname, fields)))
{
Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars());
}
}
return true;
}
//========================================================================== //==========================================================================
// //
// ZCCCompiler :: FieldFlagsToString // ZCCCompiler :: FieldFlagsToString
@ -1841,6 +1909,73 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper
} }
} }
//==========================================================================
//
// Parses an actor property's parameters and calls the handler
//
//==========================================================================
void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag)
{
unsigned parmcount = 1;
ZCC_TreeNode *x = property->Values;
while (x->SiblingNext != property->Values)
{
x = x->SiblingNext;
parmcount++;
}
if (parmcount != prop->Variables.Size())
{
Error(x, "Argument count mismatch: Got %u, expected %u", parmcount, prop->Variables.Size());
return;
}
auto values = Simplify(property->Values, &bag.Info->Symbols, true); // need to do this before the loop so that we can find the head node again.
auto exp = values;
for (auto f : prop->Variables)
{
void *addr;
if (f->Flags & VARF_Meta)
{
addr = ((char*)bag.Info) + f->Offset;
}
else
{
addr = ((char*)defaults) + f->Offset;
}
if (f->Type->IsKindOf(RUNTIME_CLASS(PInt)))
{
static_cast<PInt*>(f->Type)->SetValue(addr, GetInt(exp));
}
else if (f->Type->IsKindOf(RUNTIME_CLASS(PFloat)))
{
static_cast<PFloat*>(f->Type)->SetValue(addr, GetDouble(exp));
}
else if (f->Type->IsKindOf(RUNTIME_CLASS(PString)))
{
*(FString*)addr = GetString(exp);
}
else if (f->Type->IsKindOf(RUNTIME_CLASS(PClassPointer)))
{
auto cls = PClass::FindClass(GetString(exp));
*(PClass**)addr = cls;
if (!cls->IsDescendantOf(static_cast<PClassPointer*>(f->Type)->ClassRestriction))
{
Error(property, "class %s is not compatible with property type %s", cls->TypeName.GetChars(), static_cast<PClassPointer*>(f->Type)->ClassRestriction->TypeName.GetChars());
}
}
else
{
Error(property, "unhandled property type %s", f->Type->DescriptiveName());
}
exp->ToErrorNode(); // invalidate after processing.
exp = static_cast<ZCC_Expression *>(exp->SiblingNext);
}
}
//========================================================================== //==========================================================================
// //
// Parses an actor property // Parses an actor property
@ -1893,6 +2028,17 @@ void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *pro
} }
else else
{ {
propname.Insert(0, "@property@");
FName name(propname, true);
if (name != NAME_None)
{
auto propp = dyn_cast<PProperty>(cls->Symbols.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()); Error(prop, "'%s' is an unknown actor property\n", propname.GetChars());
} }
} }

View file

@ -51,6 +51,7 @@ struct ZCC_ClassWork : public ZCC_StructWork
ZCC_Class *cls; ZCC_Class *cls;
TArray<ZCC_Default *> Defaults; TArray<ZCC_Default *> Defaults;
TArray<ZCC_States *> States; TArray<ZCC_States *> States;
TArray<ZCC_Property *> Properties;
ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n) ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n)
{ {
@ -67,6 +68,12 @@ struct ZCC_ClassWork : public ZCC_StructWork
} }
}; };
struct ZCC_PropertyWork
{
ZCC_Property *prop;
PSymbolTable *outputtable;
};
struct ZCC_ConstantWork struct ZCC_ConstantWork
{ {
ZCC_ConstantDef *node; ZCC_ConstantDef *node;
@ -92,6 +99,8 @@ private:
void CompileAllFields(); void CompileAllFields();
bool CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false); bool CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false);
void CompileAllProperties();
bool CompileProperties(PClass *type, TArray<ZCC_Property *> &Properties, FName prefix);
FString FlagsToString(uint32_t flags); FString FlagsToString(uint32_t flags);
PType *DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes, bool formember); PType *DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes, bool formember);
PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PSymbolTable *sym); PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PSymbolTable *sym);
@ -101,6 +110,7 @@ private:
void ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg); void ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg);
void ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *flg, Baggage &bag); void ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *flg, Baggage &bag);
void DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *pex, AActor *defaults, Baggage &bag); void DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *pex, AActor *defaults, Baggage &bag);
void DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *pex, AActor *defaults, Baggage &bag);
int GetInt(ZCC_Expression *expr); int GetInt(ZCC_Expression *expr);
double GetDouble(ZCC_Expression *expr); double GetDouble(ZCC_Expression *expr);
const char *GetString(ZCC_Expression *expr, bool silent = false); const char *GetString(ZCC_Expression *expr, bool silent = false);
@ -114,6 +124,7 @@ private:
TArray<ZCC_ConstantDef *> Constants; TArray<ZCC_ConstantDef *> Constants;
TArray<ZCC_StructWork *> Structs; TArray<ZCC_StructWork *> Structs;
TArray<ZCC_ClassWork *> Classes; TArray<ZCC_ClassWork *> Classes;
TArray<ZCC_PropertyWork *> Properties;
PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false); PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false);

View file

@ -144,6 +144,8 @@ static void InitTokenMap()
TOKENDEF ('{', ZCC_LBRACE); TOKENDEF ('{', ZCC_LBRACE);
TOKENDEF ('}', ZCC_RBRACE); TOKENDEF ('}', ZCC_RBRACE);
TOKENDEF (TK_Struct, ZCC_STRUCT); TOKENDEF (TK_Struct, ZCC_STRUCT);
TOKENDEF (TK_Property, ZCC_PROPERTY);
TOKENDEF (TK_Transient, ZCC_TRANSIENT);
TOKENDEF (TK_Enum, ZCC_ENUM); TOKENDEF (TK_Enum, ZCC_ENUM);
TOKENDEF2(TK_SByte, ZCC_SBYTE, NAME_sByte); TOKENDEF2(TK_SByte, ZCC_SBYTE, NAME_sByte);
TOKENDEF2(TK_Byte, ZCC_BYTE, NAME_Byte); TOKENDEF2(TK_Byte, ZCC_BYTE, NAME_Byte);

View file

@ -35,6 +35,7 @@ enum
ZCC_Extension = 1 << 12, ZCC_Extension = 1 << 12,
ZCC_Virtual = 1 << 13, ZCC_Virtual = 1 << 13,
ZCC_Override = 1 << 14, ZCC_Override = 1 << 14,
ZCC_Transient = 1 << 15,
}; };
// Function parameter modifiers // Function parameter modifiers
@ -104,6 +105,7 @@ enum EZCCTreeNodeType
AST_DeclFlags, AST_DeclFlags,
AST_ClassCast, AST_ClassCast,
AST_StaticArrayStatement, AST_StaticArrayStatement,
AST_Property,
NUM_AST_NODE_TYPES NUM_AST_NODE_TYPES
}; };
@ -189,6 +191,11 @@ struct ZCC_Struct : ZCC_NamedNode
PStruct *Type; PStruct *Type;
}; };
struct ZCC_Property : ZCC_NamedNode
{
ZCC_TreeNode *Body;
};
struct ZCC_Class : ZCC_Struct struct ZCC_Class : ZCC_Struct
{ {
ZCC_Identifier *ParentName; ZCC_Identifier *ParentName;

View file

@ -33,11 +33,13 @@
** **
*/ */
class Health : Inventory native class Health : Inventory
{ {
native int PrevHealth; transient int PrevHealth;
native meta int LowHealth; /*meta*/ int LowHealth;
native meta String LowHealthMessage; /*meta*/ String LowHealthMessage;
property LowMessage: LowHealth, LowHealthMessage;
Default Default
{ {
@ -56,7 +58,6 @@ class Health : Inventory native
if (PrevHealth < LowHealth) if (PrevHealth < LowHealth)
{ {
String message = LowHealthMessage; String message = LowHealthMessage;
if (message.Length() != 0) if (message.Length() != 0)
{ {
return message; return message;