- 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
commit 179b6e1a39
21 changed files with 313 additions and 109 deletions

View file

@ -336,6 +336,16 @@ static void PrintStruct(FLispString &out, ZCC_TreeNode *node)
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)
{
ZCC_Enum *enode = (ZCC_Enum *)node;
@ -934,6 +944,7 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode *
PrintVectorInitializer,
PrintDeclFlags,
PrintExprClassCast,
PrintProperty,
};
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) ::= 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) 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; }
/*----- 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) ::= class_innards(X) class_member(B). { SAFE_APPEND(X,B); }
%type property_def{ZCC_Property *}
%type struct_def{ZCC_Struct *}
%type enum_def {ZCC_Enum *}
%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) ::= default_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 -----*/
/* 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 struct_body{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.
{
@ -747,7 +772,7 @@ type_name(X) ::= DOT dottable_id(A).
/* Type names can also be used as identifiers in contexts where type names
* are not normally allowed. */
%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 */
%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) ::= 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) ::= 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) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; 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;
case AST_Property:
cls->Properties.Push(static_cast<ZCC_Property *>(node));
break;
case AST_VarDeclarator:
cls->Fields.Push(static_cast<ZCC_VarDeclarator *>(node));
break;
@ -396,6 +400,7 @@ int ZCCCompiler::Compile()
CreateStructTypes();
CompileAllConstants();
CompileAllFields();
CompileAllProperties();
InitDefaults();
InitFunctions();
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_Deprecated) varflags |= VARF_Deprecated;
if (field->Flags & ZCC_ReadOnly) varflags |= VARF_ReadOnly;
if (field->Flags & ZCC_Transient) varflags |= VARF_Transient;
if (field->Flags & ZCC_Native)
{
@ -1277,7 +1283,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
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))
{
// 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)
{
auto querytype = (varflags & VARF_Static) ? type->GetClass() : type;
auto querytype = (varflags & VARF_Meta) ? type->GetClass() : type;
fd = FindField(querytype, FName(name->Name).GetChars());
if (fd == nullptr)
{
@ -1337,6 +1343,68 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
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
@ -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
@ -1893,6 +2028,17 @@ void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *pro
}
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());
}
}

View file

@ -51,6 +51,7 @@ struct ZCC_ClassWork : public ZCC_StructWork
ZCC_Class *cls;
TArray<ZCC_Default *> Defaults;
TArray<ZCC_States *> States;
TArray<ZCC_Property *> Properties;
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
{
ZCC_ConstantDef *node;
@ -92,6 +99,8 @@ private:
void CompileAllFields();
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);
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);
@ -101,6 +110,7 @@ private:
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);
void DispatchScriptProperty(PProperty *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);
@ -114,6 +124,7 @@ private:
TArray<ZCC_ConstantDef *> Constants;
TArray<ZCC_StructWork *> Structs;
TArray<ZCC_ClassWork *> Classes;
TArray<ZCC_PropertyWork *> Properties;
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_RBRACE);
TOKENDEF (TK_Struct, ZCC_STRUCT);
TOKENDEF (TK_Property, ZCC_PROPERTY);
TOKENDEF (TK_Transient, ZCC_TRANSIENT);
TOKENDEF (TK_Enum, ZCC_ENUM);
TOKENDEF2(TK_SByte, ZCC_SBYTE, NAME_sByte);
TOKENDEF2(TK_Byte, ZCC_BYTE, NAME_Byte);

View file

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