mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-11 07:11:54 +00:00
- allow functions in structs. This is needed for several internal data types like players, sectors, lines, etc.
- added a new type 'NativeStruct'. This will be used for types that cannot be instantiated, and is also needed to cleanly handle many internal types that only can exist as reference.
This commit is contained in:
parent
df4e9324c9
commit
f11f020b6c
11 changed files with 453 additions and 370 deletions
|
@ -2334,6 +2334,24 @@ PStruct *NewStruct(FName name, PTypeBase *outer)
|
||||||
return static_cast<PStruct *>(stype);
|
return static_cast<PStruct *>(stype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PNativeStruct ****************************************************************/
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS(PNativeStruct, false, false, false, false)
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// PNativeStruct - Parameterized Constructor
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
PNativeStruct::PNativeStruct(FName name)
|
||||||
|
: PStruct(name, nullptr)
|
||||||
|
{
|
||||||
|
mDescriptiveName.Format("NativeStruct<%s>", name.GetChars());
|
||||||
|
Size = 0;
|
||||||
|
HasNativeFields = true;
|
||||||
|
}
|
||||||
|
|
||||||
/* PField *****************************************************************/
|
/* PField *****************************************************************/
|
||||||
|
|
||||||
IMPLEMENT_CLASS(PField, false, false, false, false)
|
IMPLEMENT_CLASS(PField, false, false, false, false)
|
||||||
|
|
|
@ -694,6 +694,15 @@ protected:
|
||||||
PStruct();
|
PStruct();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// a native struct will always be abstract and cannot be instantiated. All variables are references.
|
||||||
|
// In addition, native structs can have methods (but no virtual ones.)
|
||||||
|
class PNativeStruct : public PStruct
|
||||||
|
{
|
||||||
|
DECLARE_CLASS(PNativeStruct, PStruct);
|
||||||
|
public:
|
||||||
|
PNativeStruct(FName name = NAME_None);
|
||||||
|
};
|
||||||
|
|
||||||
class PPrototype : public PCompoundType
|
class PPrototype : public PCompoundType
|
||||||
{
|
{
|
||||||
DECLARE_CLASS(PPrototype, PCompoundType);
|
DECLARE_CLASS(PPrototype, PCompoundType);
|
||||||
|
@ -723,10 +732,10 @@ public:
|
||||||
TArray<FName> ArgNames; // we need the names to access them later when the function gets compiled.
|
TArray<FName> ArgNames; // we need the names to access them later when the function gets compiled.
|
||||||
uint32_t Flags;
|
uint32_t Flags;
|
||||||
int UseFlags;
|
int UseFlags;
|
||||||
PClass *SelfClass;
|
PStruct *SelfClass;
|
||||||
};
|
};
|
||||||
TArray<Variant> Variants;
|
TArray<Variant> Variants;
|
||||||
PClass *OwningClass = nullptr;
|
PStruct *OwningClass = nullptr;
|
||||||
|
|
||||||
unsigned AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags, int useflags);
|
unsigned AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags, int useflags);
|
||||||
int GetImplicitArgs()
|
int GetImplicitArgs()
|
||||||
|
@ -738,7 +747,7 @@ public:
|
||||||
|
|
||||||
size_t PropagateMark();
|
size_t PropagateMark();
|
||||||
|
|
||||||
PFunction(PClass *owner = nullptr, FName name = NAME_None) : PSymbol(name), OwningClass(owner) {}
|
PFunction(PStruct *owner = nullptr, FName name = NAME_None) : PSymbol(name), OwningClass(owner) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Meta-info for every class derived from DObject ---------------------------
|
// Meta-info for every class derived from DObject ---------------------------
|
||||||
|
@ -749,9 +758,9 @@ enum
|
||||||
};
|
};
|
||||||
|
|
||||||
class PClassClass;
|
class PClassClass;
|
||||||
class PClass : public PStruct
|
class PClass : public PNativeStruct
|
||||||
{
|
{
|
||||||
DECLARE_CLASS(PClass, PStruct);
|
DECLARE_CLASS(PClass, PNativeStruct);
|
||||||
HAS_OBJECT_POINTERS;
|
HAS_OBJECT_POINTERS;
|
||||||
protected:
|
protected:
|
||||||
// We unravel _WITH_META here just as we did for PType.
|
// We unravel _WITH_META here just as we did for PType.
|
||||||
|
|
|
@ -101,7 +101,7 @@ FCompileContext::FCompileContext(PFunction *fnc, PPrototype *ret, bool fromdecor
|
||||||
if (fnc != nullptr) Class = fnc->OwningClass;
|
if (fnc != nullptr) Class = fnc->OwningClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
FCompileContext::FCompileContext(PClass *cls, bool fromdecorate)
|
FCompileContext::FCompileContext(PStruct *cls, bool fromdecorate)
|
||||||
: ReturnProto(nullptr), Function(nullptr), Class(cls), FromDecorate(fromdecorate), StateIndex(-1), StateCount(0), Lump(-1)
|
: ReturnProto(nullptr), Function(nullptr), Class(cls), FromDecorate(fromdecorate), StateIndex(-1), StateCount(0), Lump(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -5335,7 +5335,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
|
||||||
delete this;
|
delete this;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (!ctx.Function->Variants[0].SelfClass->IsDescendantOf(RUNTIME_CLASS(AActor)))
|
if (!ctx.Function->Variants[0].SelfClass->IsKindOf(RUNTIME_CLASS(PClassActor)))
|
||||||
{
|
{
|
||||||
ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type.");
|
ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type.");
|
||||||
delete this;
|
delete this;
|
||||||
|
@ -5441,7 +5441,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PClass *classctx, FxExpression *&object, PStruct *objtype)
|
FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classctx, FxExpression *&object, PStruct *objtype)
|
||||||
{
|
{
|
||||||
PSymbol *sym;
|
PSymbol *sym;
|
||||||
PSymbolTable *symtbl;
|
PSymbolTable *symtbl;
|
||||||
|
@ -6379,7 +6379,15 @@ static bool CheckFunctionCompatiblity(FScriptPosition &ScriptPosition, PFunction
|
||||||
{
|
{
|
||||||
auto callingself = caller->Variants[0].SelfClass;
|
auto callingself = caller->Variants[0].SelfClass;
|
||||||
auto calledself = callee->Variants[0].SelfClass;
|
auto calledself = callee->Variants[0].SelfClass;
|
||||||
if (!callingself->IsDescendantOf(calledself))
|
bool match = (callingself == calledself);
|
||||||
|
if (!match)
|
||||||
|
{
|
||||||
|
auto callingselfcls = dyn_cast<PClass>(caller->Variants[0].SelfClass);
|
||||||
|
auto calledselfcls = dyn_cast<PClass>(callee->Variants[0].SelfClass);
|
||||||
|
match = callingselfcls != nullptr && calledselfcls != nullptr && callingselfcls->IsDescendantOf(calledselfcls);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!match)
|
||||||
{
|
{
|
||||||
ScriptPosition.Message(MSG_ERROR, "Call to member function %s with incompatible self pointer.", callee->SymbolName.GetChars());
|
ScriptPosition.Message(MSG_ERROR, "Call to member function %s with incompatible self pointer.", callee->SymbolName.GetChars());
|
||||||
return false;
|
return false;
|
||||||
|
@ -6717,11 +6725,19 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
||||||
|
|
||||||
if (Self->ExprType == EFX_Super)
|
if (Self->ExprType == EFX_Super)
|
||||||
{
|
{
|
||||||
// give the node the proper value type now that we know it's properly used.
|
auto clstype = dyn_cast<PClass>(ctx.Function->Variants[0].SelfClass);
|
||||||
cls = ctx.Function->Variants[0].SelfClass->ParentClass;
|
if (clstype != nullptr)
|
||||||
Self->ValueType = NewPointer(cls);
|
{
|
||||||
Self->ExprType = EFX_Self;
|
// give the node the proper value type now that we know it's properly used.
|
||||||
novirtual = true; // super calls are always non-virtual
|
cls = clstype->ParentClass;
|
||||||
|
Self->ValueType = NewPointer(cls);
|
||||||
|
Self->ExprType = EFX_Self;
|
||||||
|
novirtual = true; // super calls are always non-virtual
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ScriptPosition.Message(MSG_ERROR, "Super requires a class type");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Self->IsVector())
|
if (Self->IsVector())
|
||||||
|
@ -6775,7 +6791,8 @@ isresolved:
|
||||||
|
|
||||||
if (staticonly && (afd->Variants[0].Flags & VARF_Method))
|
if (staticonly && (afd->Variants[0].Flags & VARF_Method))
|
||||||
{
|
{
|
||||||
if (!ctx.Class->IsDescendantOf(cls))
|
auto clstype = dyn_cast<PClass>(ctx.Class);
|
||||||
|
if (clstype == nullptr || !clstype->IsDescendantOf(cls))
|
||||||
{
|
{
|
||||||
ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here\n", cls->TypeName.GetChars(), MethodName.GetChars());
|
ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here\n", cls->TypeName.GetChars(), MethodName.GetChars());
|
||||||
delete this;
|
delete this;
|
||||||
|
@ -8876,14 +8893,22 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
|
||||||
CHECKRESOLVED();
|
CHECKRESOLVED();
|
||||||
ABORT(ctx.Class);
|
ABORT(ctx.Class);
|
||||||
int symlabel;
|
int symlabel;
|
||||||
|
auto clstype = dyn_cast<PClassActor>(ctx.Class);
|
||||||
|
|
||||||
if (names[0] == NAME_None)
|
if (names[0] == NAME_None)
|
||||||
{
|
{
|
||||||
scope = nullptr;
|
scope = nullptr;
|
||||||
}
|
}
|
||||||
|
else if (clstype == nullptr)
|
||||||
|
{
|
||||||
|
// not in an actor, so any further checks are pointless.
|
||||||
|
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars());
|
||||||
|
delete this;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
else if (names[0] == NAME_Super)
|
else if (names[0] == NAME_Super)
|
||||||
{
|
{
|
||||||
scope = dyn_cast<PClassActor>(ctx.Class->ParentClass);
|
scope = dyn_cast<PClassActor>(clstype->ParentClass);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -8894,7 +8919,7 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
|
||||||
delete this;
|
delete this;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
else if (!scope->IsAncestorOf(ctx.Class))
|
else if (!scope->IsAncestorOf(clstype))
|
||||||
{
|
{
|
||||||
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars());
|
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars());
|
||||||
delete this;
|
delete this;
|
||||||
|
|
|
@ -75,7 +75,7 @@ struct FCompileContext
|
||||||
FxCompoundStatement *Block = nullptr;
|
FxCompoundStatement *Block = nullptr;
|
||||||
PPrototype *ReturnProto;
|
PPrototype *ReturnProto;
|
||||||
PFunction *Function; // The function that is currently being compiled (or nullptr for constant evaluation.)
|
PFunction *Function; // The function that is currently being compiled (or nullptr for constant evaluation.)
|
||||||
PClass *Class; // The type of the owning class.
|
PStruct *Class; // The type of the owning class.
|
||||||
bool FromDecorate; // DECORATE must silence some warnings and demote some errors.
|
bool FromDecorate; // DECORATE must silence some warnings and demote some errors.
|
||||||
int StateIndex; // index in actor's state table for anonymous functions, otherwise -1 (not used by DECORATE which pre-resolves state indices)
|
int StateIndex; // index in actor's state table for anonymous functions, otherwise -1 (not used by DECORATE which pre-resolves state indices)
|
||||||
int StateCount; // amount of states an anoymous function is being used on (must be 1 for state indices to be allowed.)
|
int StateCount; // amount of states an anoymous function is being used on (must be 1 for state indices to be allowed.)
|
||||||
|
@ -84,7 +84,7 @@ struct FCompileContext
|
||||||
TDeletingArray<FxLocalVariableDeclaration *> FunctionArgs;
|
TDeletingArray<FxLocalVariableDeclaration *> FunctionArgs;
|
||||||
|
|
||||||
FCompileContext(PFunction *func, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump);
|
FCompileContext(PFunction *func, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump);
|
||||||
FCompileContext(PClass *cls, bool fromdecorate); // only to be used to resolve constants!
|
FCompileContext(PStruct *cls, bool fromdecorate); // only to be used to resolve constants!
|
||||||
|
|
||||||
PSymbol *FindInClass(FName identifier, PSymbolTable *&symt);
|
PSymbol *FindInClass(FName identifier, PSymbolTable *&symt);
|
||||||
PSymbol *FindInSelfClass(FName identifier, PSymbolTable *&symt);
|
PSymbol *FindInSelfClass(FName identifier, PSymbolTable *&symt);
|
||||||
|
@ -346,7 +346,7 @@ public:
|
||||||
|
|
||||||
FxIdentifier(FName i, const FScriptPosition &p);
|
FxIdentifier(FName i, const FScriptPosition &p);
|
||||||
FxExpression *Resolve(FCompileContext&);
|
FxExpression *Resolve(FCompileContext&);
|
||||||
FxExpression *ResolveMember(FCompileContext&, PClass*, FxExpression*&, PStruct*);
|
FxExpression *ResolveMember(FCompileContext&, PStruct*, FxExpression*&, PStruct*);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@ FScriptPosition & GetStateSource(FState *state)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PClass *cls, DWORD funcflags, int useflags)
|
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PStruct *cls, DWORD funcflags, int useflags)
|
||||||
{
|
{
|
||||||
// Must be called before adding any other arguments.
|
// Must be called before adding any other arguments.
|
||||||
assert(args == nullptr || args->Size() == 0);
|
assert(args == nullptr || args->Size() == 0);
|
||||||
|
@ -182,7 +182,7 @@ PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, i
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
PFunction *FindClassMemberFunction(PClass *selfcls, PClass *funccls, FName name, FScriptPosition &sc, bool *error)
|
PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName name, FScriptPosition &sc, bool *error)
|
||||||
{
|
{
|
||||||
// Skip ACS_NamedExecuteWithResult. Anything calling this should use the builtin instead.
|
// Skip ACS_NamedExecuteWithResult. Anything calling this should use the builtin instead.
|
||||||
if (name == NAME_ACS_NamedExecuteWithResult) return nullptr;
|
if (name == NAME_ACS_NamedExecuteWithResult) return nullptr;
|
||||||
|
|
|
@ -146,7 +146,7 @@ inline void ResetBaggage (Baggage *bag, PClassActor *stateclass)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
AFuncDesc *FindFunction(PClass *cls, const char * string);
|
AFuncDesc *FindFunction(PStruct *cls, const char * string);
|
||||||
|
|
||||||
|
|
||||||
FxExpression *ParseExpression(FScanner &sc, PClassActor *cls, bool mustresolve = false);
|
FxExpression *ParseExpression(FScanner &sc, PClassActor *cls, bool mustresolve = false);
|
||||||
|
@ -156,9 +156,9 @@ void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression
|
||||||
FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret);
|
FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret);
|
||||||
class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag);
|
class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag);
|
||||||
FName CheckCastKludges(FName in);
|
FName CheckCastKludges(FName in);
|
||||||
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PClass *cls, DWORD funcflags, int useflags);
|
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PStruct *cls, DWORD funcflags, int useflags);
|
||||||
PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags);
|
PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags);
|
||||||
PFunction *FindClassMemberFunction(PClass *cls, PClass *funccls, FName name, FScriptPosition &sc, bool *error);
|
PFunction *FindClassMemberFunction(PStruct *cls, PStruct *funccls, FName name, FScriptPosition &sc, bool *error);
|
||||||
void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate);
|
void CreateDamageFunction(PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate);
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
|
@ -577,11 +577,14 @@ FPropertyInfo *FindProperty(const char * string)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
AFuncDesc *FindFunction(PClass *cls, const char * string)
|
AFuncDesc *FindFunction(PStruct *cls, const char * string)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
if (i == 1 && !cls->IsDescendantOf(RUNTIME_CLASS(AActor))) break;
|
// Since many functions have been declared with Actor as owning class, despited being members of something else, let's hack around this until they have been fixed or exported.
|
||||||
|
// Since most of these are expected to be scriptified anyway, there's no point fixing them all before they get exported.
|
||||||
|
if (i == 1 && !cls->IsKindOf(RUNTIME_CLASS(PClassActor))) break;
|
||||||
|
|
||||||
FStringf fullname("%s_%s", i == 0 ? cls->TypeName.GetChars() : "Actor", string);
|
FStringf fullname("%s_%s", i == 0 ? cls->TypeName.GetChars() : "Actor", string);
|
||||||
int min = 0, max = AFTable.Size() - 1;
|
int min = 0, max = AFTable.Size() - 1;
|
||||||
|
|
||||||
|
|
|
@ -278,16 +278,21 @@ class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ }
|
||||||
%type struct_body{ZCC_TreeNode *}
|
%type struct_body{ZCC_TreeNode *}
|
||||||
%type struct_member{ZCC_TreeNode *}
|
%type struct_member{ZCC_TreeNode *}
|
||||||
|
|
||||||
struct_def(X) ::= STRUCT(T) IDENTIFIER(A) LBRACE opt_struct_body(B) RBRACE opt_semicolon.
|
struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body(B) RBRACE opt_semicolon.
|
||||||
{
|
{
|
||||||
NEW_AST_NODE(Struct,def,T);
|
NEW_AST_NODE(Struct,def,T);
|
||||||
def->NodeName = A.Name();
|
def->NodeName = A.Name();
|
||||||
def->Body = B;
|
def->Body = B;
|
||||||
def->Type = nullptr;
|
def->Type = nullptr;
|
||||||
def->Symbol = nullptr;
|
def->Symbol = nullptr;
|
||||||
|
def->Flags = S.Flags;
|
||||||
X = def;
|
X = def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%type struct_flags{ClassFlagsBlock}
|
||||||
|
struct_flags(X) ::= . { X.Flags = 0; }
|
||||||
|
struct_flags(X) ::= NATIVE. { X.Flags = ZCC_Native; }
|
||||||
|
|
||||||
opt_struct_body(X) ::= . { X = NULL; }
|
opt_struct_body(X) ::= . { X = NULL; }
|
||||||
opt_struct_body(X) ::= struct_body(X).
|
opt_struct_body(X) ::= struct_body(X).
|
||||||
|
|
||||||
|
@ -295,7 +300,7 @@ struct_body(X) ::= error. { X = NULL; }
|
||||||
struct_body(X) ::= struct_member(X).
|
struct_body(X) ::= struct_member(X).
|
||||||
struct_body(X) ::= struct_member(A) struct_body(B). { X = A; /*X-overwrites-A*/ X->AppendSibling(B); }
|
struct_body(X) ::= struct_member(A) struct_body(B). { X = A; /*X-overwrites-A*/ X->AppendSibling(B); }
|
||||||
|
|
||||||
struct_member(X) ::= declarator_no_fun(A). { X = A; /*X-overwrites-A*/ }
|
struct_member(X) ::= declarator(A). { X = A; /*X-overwrites-A*/ }
|
||||||
struct_member(X) ::= enum_def(A). { X = A; /*X-overwrites-A*/ }
|
struct_member(X) ::= enum_def(A). { X = A; /*X-overwrites-A*/ }
|
||||||
struct_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ }
|
struct_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ }
|
||||||
|
|
||||||
|
@ -844,14 +849,6 @@ declarator(X) ::= decl_flags(A) type_list_or_void(B) variables_or_function(C).
|
||||||
X = NULL;
|
X = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
declarator_no_fun(X) ::= decl_flags(A) type(B) variable_list(C) SEMICOLON.
|
|
||||||
{
|
|
||||||
NEW_AST_NODE(VarDeclarator, decl, A != nullptr? A->SourceLoc : B->SourceLoc);
|
|
||||||
decl->Type = B;
|
|
||||||
decl->Names = C;
|
|
||||||
decl->Flags = A == nullptr? 0 : A->Flags;
|
|
||||||
X = decl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to split it up like this to avoid parsing conflicts.
|
// Need to split it up like this to avoid parsing conflicts.
|
||||||
variables_or_function(X) ::= IDENTIFIER(A) LPAREN func_params(B) RPAREN func_const(C) opt_func_body(D). /* Function */
|
variables_or_function(X) ::= IDENTIFIER(A) LPAREN func_params(B) RPAREN func_const(C) opt_func_body(D). /* Function */
|
||||||
|
|
|
@ -196,6 +196,10 @@ void ZCCCompiler::ProcessStruct(ZCC_Struct *cnode, PSymbolTreeNode *treenode, ZC
|
||||||
cls->Fields.Push(static_cast<ZCC_VarDeclarator *>(node));
|
cls->Fields.Push(static_cast<ZCC_VarDeclarator *>(node));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AST_FuncDeclarator:
|
||||||
|
cls->Functions.Push(static_cast<ZCC_FuncDeclarator *>(node));
|
||||||
|
break;
|
||||||
|
|
||||||
case AST_EnumTerminator:
|
case AST_EnumTerminator:
|
||||||
enumType = nullptr;
|
enumType = nullptr;
|
||||||
break;
|
break;
|
||||||
|
@ -411,7 +415,7 @@ void ZCCCompiler::CreateStructTypes()
|
||||||
{
|
{
|
||||||
for(auto s : Structs)
|
for(auto s : Structs)
|
||||||
{
|
{
|
||||||
s->Outer = s->OuterDef == nullptr? nullptr : s->OuterDef->Type;
|
s->Outer = s->OuterDef == nullptr? nullptr : s->OuterDef->CType();
|
||||||
s->strct->Type = NewStruct(s->NodeName(), s->Outer);
|
s->strct->Type = NewStruct(s->NodeName(), s->Outer);
|
||||||
s->strct->Symbol = new PSymbolType(s->NodeName(), s->Type());
|
s->strct->Symbol = new PSymbolType(s->NodeName(), s->Type());
|
||||||
GlobalSymbols.AddSymbol(s->strct->Symbol);
|
GlobalSymbols.AddSymbol(s->strct->Symbol);
|
||||||
|
@ -1989,6 +1993,331 @@ void ZCCCompiler::InitDefaults()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool forclass)
|
||||||
|
{
|
||||||
|
TArray<PType *> rets(1);
|
||||||
|
TArray<PType *> args;
|
||||||
|
TArray<uint32_t> argflags;
|
||||||
|
TArray<VMValue> argdefaults;
|
||||||
|
TArray<FName> argnames;
|
||||||
|
|
||||||
|
rets.Clear();
|
||||||
|
args.Clear();
|
||||||
|
argflags.Clear();
|
||||||
|
bool hasdefault = false;
|
||||||
|
// For the time being, let's not allow overloading. This may be reconsidered later but really just adds an unnecessary amount of complexity here.
|
||||||
|
if (AddTreeNode(f->Name, f, &c->TreeNodes, false))
|
||||||
|
{
|
||||||
|
auto t = f->Type;
|
||||||
|
if (t != nullptr)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
auto type = DetermineType(c->Type(), f, f->Name, t, false, false);
|
||||||
|
if (type->IsKindOf(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3)
|
||||||
|
{
|
||||||
|
// structs and classes only get passed by pointer.
|
||||||
|
type = NewPointer(type);
|
||||||
|
}
|
||||||
|
// TBD: disallow certain types? For now, let everything pass that isn't an array.
|
||||||
|
rets.Push(type);
|
||||||
|
t = static_cast<decltype(t)>(t->SiblingNext);
|
||||||
|
} while (t != f->Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_FuncConst | ZCC_Abstract;
|
||||||
|
|
||||||
|
if (f->Flags & notallowed)
|
||||||
|
{
|
||||||
|
Error(f, "Invalid qualifiers for %s (%s not allowed)", FName(f->Name).GetChars(), FlagsToString(f->Flags & notallowed).GetChars());
|
||||||
|
f->Flags &= notallowed;
|
||||||
|
}
|
||||||
|
uint32_t varflags = VARF_Method;
|
||||||
|
int implicitargs = 1;
|
||||||
|
AFuncDesc *afd = nullptr;
|
||||||
|
int useflags = SUF_ACTOR | SUF_OVERLAY | SUF_WEAPON | SUF_ITEM;
|
||||||
|
if (f->UseFlags != nullptr)
|
||||||
|
{
|
||||||
|
useflags = 0;
|
||||||
|
auto p = f->UseFlags;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
switch (p->Id)
|
||||||
|
{
|
||||||
|
case NAME_Actor:
|
||||||
|
useflags |= SUF_ACTOR;
|
||||||
|
break;
|
||||||
|
case NAME_Overlay:
|
||||||
|
useflags |= SUF_OVERLAY;
|
||||||
|
break;
|
||||||
|
case NAME_Weapon:
|
||||||
|
useflags |= SUF_WEAPON;
|
||||||
|
break;
|
||||||
|
case NAME_Item:
|
||||||
|
useflags |= SUF_ITEM;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Error(p, "Unknown Action qualifier %s", FName(p->Id).GetChars());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = static_cast<decltype(p)>(p->SiblingNext);
|
||||||
|
} while (p != f->UseFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// map to implementation flags.
|
||||||
|
if (f->Flags & ZCC_Private) varflags |= VARF_Private;
|
||||||
|
if (f->Flags & ZCC_Protected) varflags |= VARF_Protected;
|
||||||
|
if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
|
||||||
|
if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual;
|
||||||
|
if (f->Flags & ZCC_Override) varflags |= VARF_Override;
|
||||||
|
if (f->Flags & ZCC_Action)
|
||||||
|
{
|
||||||
|
// Non-Actors cannot have action functions.
|
||||||
|
if (!c->Type()->IsKindOf(RUNTIME_CLASS(PClassActor)))
|
||||||
|
{
|
||||||
|
Error(f, "'Action' can only be used in child classes of Actor");
|
||||||
|
}
|
||||||
|
|
||||||
|
varflags |= VARF_Final; // Action implies Final.
|
||||||
|
if (useflags & (SUF_OVERLAY | SUF_WEAPON | SUF_ITEM))
|
||||||
|
{
|
||||||
|
varflags |= VARF_Action;
|
||||||
|
implicitargs = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
implicitargs = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final.
|
||||||
|
|
||||||
|
|
||||||
|
if (varflags & VARF_Override) varflags &= ~VARF_Virtual; // allow 'virtual override'.
|
||||||
|
// Only one of these flags may be used.
|
||||||
|
static int exclude[] = { ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static };
|
||||||
|
static const char * print[] = { "virtual", "override", "action", "static" };
|
||||||
|
int fc = 0;
|
||||||
|
FString build;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (f->Flags & exclude[i])
|
||||||
|
{
|
||||||
|
fc++;
|
||||||
|
if (build.Len() > 0) build += ", ";
|
||||||
|
build += print[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fc > 1)
|
||||||
|
{
|
||||||
|
Error(f, "Invalid combination of qualifiers %s on function %s.", FName(f->Name).GetChars(), build.GetChars());
|
||||||
|
varflags |= VARF_Method;
|
||||||
|
}
|
||||||
|
if (varflags & VARF_Override) varflags |= VARF_Virtual; // Now that the flags are checked, make all override functions virtual as well.
|
||||||
|
|
||||||
|
if (f->Flags & ZCC_Native)
|
||||||
|
{
|
||||||
|
varflags |= VARF_Native;
|
||||||
|
afd = FindFunction(c->Type(), FName(f->Name).GetChars());
|
||||||
|
if (afd == nullptr)
|
||||||
|
{
|
||||||
|
Error(f, "The function '%s' has not been exported from the executable.", FName(f->Name).GetChars());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(*afd->VMPointer)->ImplicitArgs = BYTE(implicitargs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags, useflags);
|
||||||
|
argdefaults.Resize(argnames.Size());
|
||||||
|
auto p = f->Params;
|
||||||
|
bool hasoptionals = false;
|
||||||
|
if (p != nullptr)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int elementcount = 1;
|
||||||
|
VMValue vmval[3]; // default is REGT_NIL which means 'no default value' here.
|
||||||
|
if (p->Type != nullptr)
|
||||||
|
{
|
||||||
|
auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false);
|
||||||
|
int flags = 0;
|
||||||
|
if (p->Flags & ZCC_In) flags |= VARF_In;
|
||||||
|
if (p->Flags & ZCC_Out) flags |= VARF_Out;
|
||||||
|
if ((type->IsA(RUNTIME_CLASS(PStruct))) || (flags & VARF_Out))
|
||||||
|
{
|
||||||
|
// 'out' parameters and all structs except vectors are passed by reference
|
||||||
|
if ((flags & VARF_Out) || (type != TypeVector2 && type != TypeVector3))
|
||||||
|
{
|
||||||
|
type = NewPointer(type);
|
||||||
|
flags |= VARF_Ref;
|
||||||
|
}
|
||||||
|
else if (type == TypeVector2)
|
||||||
|
{
|
||||||
|
elementcount = 2;
|
||||||
|
}
|
||||||
|
else if (type == TypeVector3)
|
||||||
|
{
|
||||||
|
elementcount = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3)
|
||||||
|
{
|
||||||
|
Error(p, "Invalid type %s for function parameter", type->DescriptiveName());
|
||||||
|
}
|
||||||
|
else if (p->Default != nullptr)
|
||||||
|
{
|
||||||
|
flags |= VARF_Optional;
|
||||||
|
hasoptionals = true;
|
||||||
|
// The simplifier is not suited to convert the constant into something usable.
|
||||||
|
// All it does is reduce the expression to a constant but we still got to do proper type checking and conversion.
|
||||||
|
// It will also lose important type info about enums, once these get implemented
|
||||||
|
// The code generator can do this properly for us.
|
||||||
|
FxExpression *x = new FxTypeCast(ConvertNode(p->Default), type, false);
|
||||||
|
FCompileContext ctx(c->Type(), false);
|
||||||
|
x = x->Resolve(ctx);
|
||||||
|
|
||||||
|
if (x != nullptr)
|
||||||
|
{
|
||||||
|
// Vectors need special treatment because they use more than one entry in the Defaults and do not report as actual constants
|
||||||
|
if (type == TypeVector2 && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(2))
|
||||||
|
{
|
||||||
|
auto vx = static_cast<FxVectorValue *>(x);
|
||||||
|
vmval[0] = static_cast<FxConstant *>(vx->xyz[0])->GetValue().GetFloat();
|
||||||
|
vmval[1] = static_cast<FxConstant *>(vx->xyz[1])->GetValue().GetFloat();
|
||||||
|
}
|
||||||
|
else if (type == TypeVector3 && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(3))
|
||||||
|
{
|
||||||
|
auto vx = static_cast<FxVectorValue *>(x);
|
||||||
|
vmval[0] = static_cast<FxConstant *>(vx->xyz[0])->GetValue().GetFloat();
|
||||||
|
vmval[1] = static_cast<FxConstant *>(vx->xyz[1])->GetValue().GetFloat();
|
||||||
|
vmval[2] = static_cast<FxConstant *>(vx->xyz[2])->GetValue().GetFloat();
|
||||||
|
}
|
||||||
|
else if (!x->isConstant())
|
||||||
|
{
|
||||||
|
Error(p, "Default parameter %s is not constant in %s", FName(p->Name).GetChars(), FName(f->Name).GetChars());
|
||||||
|
}
|
||||||
|
else if (x->ValueType != type)
|
||||||
|
{
|
||||||
|
Error(p, "Default parameter %s could not be converted to target type %s", FName(p->Name).GetChars(), c->Type()->TypeName.GetChars());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto cnst = static_cast<FxConstant *>(x);
|
||||||
|
hasdefault = true;
|
||||||
|
switch (type->GetRegType())
|
||||||
|
{
|
||||||
|
case REGT_INT:
|
||||||
|
vmval[0] = cnst->GetValue().GetInt();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REGT_FLOAT:
|
||||||
|
vmval[0] = cnst->GetValue().GetFloat();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REGT_POINTER:
|
||||||
|
if (type->IsKindOf(RUNTIME_CLASS(PClassPointer)))
|
||||||
|
vmval[0] = (DObject*)cnst->GetValue().GetPointer();
|
||||||
|
else
|
||||||
|
vmval[0] = cnst->GetValue().GetPointer();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REGT_STRING:
|
||||||
|
vmval[0] = cnst->GetValue().GetString();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0 && "no valid type for constant");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (x != nullptr) delete x;
|
||||||
|
}
|
||||||
|
else if (hasoptionals)
|
||||||
|
{
|
||||||
|
Error(p, "All arguments after the first optional one need also be optional.");
|
||||||
|
}
|
||||||
|
// TBD: disallow certain types? For now, let everything pass that isn't an array.
|
||||||
|
args.Push(type);
|
||||||
|
argflags.Push(flags);
|
||||||
|
argnames.Push(p->Name);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
args.Push(nullptr);
|
||||||
|
argflags.Push(0);
|
||||||
|
argnames.Push(NAME_None);
|
||||||
|
}
|
||||||
|
for (int i = 0; i<elementcount; i++) argdefaults.Push(vmval[i]);
|
||||||
|
p = static_cast<decltype(p)>(p->SiblingNext);
|
||||||
|
} while (p != f->Params);
|
||||||
|
}
|
||||||
|
|
||||||
|
PFunction *sym = new PFunction(c->Type(), f->Name);
|
||||||
|
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr ? nullptr : *(afd->VMPointer), varflags, useflags);
|
||||||
|
c->Type()->Symbols.ReplaceSymbol(sym);
|
||||||
|
|
||||||
|
if (!(f->Flags & ZCC_Native))
|
||||||
|
{
|
||||||
|
auto code = ConvertAST(c->Type(), f->Body);
|
||||||
|
if (code != nullptr)
|
||||||
|
{
|
||||||
|
sym->Variants[0].Implementation = FunctionBuildList.AddFunction(sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sym->Variants[0].Implementation != nullptr && hasdefault) // do not copy empty default lists, they only waste space and processing time.
|
||||||
|
{
|
||||||
|
sym->Variants[0].Implementation->DefaultArgs = std::move(argdefaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (varflags & VARF_Virtual)
|
||||||
|
{
|
||||||
|
if (varflags & VARF_Final)
|
||||||
|
{
|
||||||
|
sym->Variants[0].Implementation->Final = true;
|
||||||
|
}
|
||||||
|
if (forclass)
|
||||||
|
{
|
||||||
|
PClass *clstype = static_cast<PClass *>(c->Type());
|
||||||
|
int vindex = clstype->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto);
|
||||||
|
// specifying 'override' is necessary to prevent one of the biggest problem spots with virtual inheritance: Mismatching argument types.
|
||||||
|
if (varflags & VARF_Override)
|
||||||
|
{
|
||||||
|
if (vindex == -1)
|
||||||
|
{
|
||||||
|
Error(p, "Attempt to override non-existent virtual function %s", FName(f->Name).GetChars());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto oldfunc = clstype->Virtuals[vindex];
|
||||||
|
if (oldfunc->Final)
|
||||||
|
{
|
||||||
|
Error(p, "Attempt to override final function %s", FName(f->Name).GetChars());
|
||||||
|
}
|
||||||
|
clstype->Virtuals[vindex] = sym->Variants[0].Implementation;
|
||||||
|
sym->Variants[0].Implementation->VirtualIndex = vindex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (vindex != -1)
|
||||||
|
{
|
||||||
|
Error(p, "Function %s attempts to override parent function without 'override' qualifier", FName(f->Name).GetChars());
|
||||||
|
}
|
||||||
|
sym->Variants[0].Implementation->VirtualIndex = clstype->Virtuals.Push(sym->Variants[0].Implementation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(p, "Virtual functions can only be defined for classes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// Parses the functions list
|
// Parses the functions list
|
||||||
|
@ -1997,11 +2326,13 @@ void ZCCCompiler::InitDefaults()
|
||||||
|
|
||||||
void ZCCCompiler::InitFunctions()
|
void ZCCCompiler::InitFunctions()
|
||||||
{
|
{
|
||||||
TArray<PType *> rets(1);
|
for (auto s : Structs)
|
||||||
TArray<PType *> args;
|
{
|
||||||
TArray<uint32_t> argflags;
|
for (auto f : s->Functions)
|
||||||
TArray<VMValue> argdefaults;
|
{
|
||||||
TArray<FName> argnames;
|
CompileFunction(s, f, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto c : Classes)
|
for (auto c : Classes)
|
||||||
{
|
{
|
||||||
|
@ -2017,306 +2348,7 @@ void ZCCCompiler::InitFunctions()
|
||||||
}
|
}
|
||||||
for (auto f : c->Functions)
|
for (auto f : c->Functions)
|
||||||
{
|
{
|
||||||
rets.Clear();
|
CompileFunction(c, f, true);
|
||||||
args.Clear();
|
|
||||||
argflags.Clear();
|
|
||||||
bool hasdefault = false;
|
|
||||||
// For the time being, let's not allow overloading. This may be reconsidered later but really just adds an unnecessary amount of complexity here.
|
|
||||||
if (AddTreeNode(f->Name, f, &c->TreeNodes, false))
|
|
||||||
{
|
|
||||||
auto t = f->Type;
|
|
||||||
if (t != nullptr)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
auto type = DetermineType(c->Type(), f, f->Name, t, false, false);
|
|
||||||
if (type->IsKindOf(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3)
|
|
||||||
{
|
|
||||||
// structs and classes only get passed by pointer.
|
|
||||||
type = NewPointer(type);
|
|
||||||
}
|
|
||||||
// TBD: disallow certain types? For now, let everything pass that isn't an array.
|
|
||||||
rets.Push(type);
|
|
||||||
t = static_cast<decltype(t)>(t->SiblingNext);
|
|
||||||
} while (t != f->Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_FuncConst | ZCC_Abstract;
|
|
||||||
|
|
||||||
if (f->Flags & notallowed)
|
|
||||||
{
|
|
||||||
Error(f, "Invalid qualifiers for %s (%s not allowed)", FName(f->Name).GetChars(), FlagsToString(f->Flags & notallowed).GetChars());
|
|
||||||
f->Flags &= notallowed;
|
|
||||||
}
|
|
||||||
uint32_t varflags = VARF_Method;
|
|
||||||
int implicitargs = 1;
|
|
||||||
AFuncDesc *afd = nullptr;
|
|
||||||
int useflags = SUF_ACTOR | SUF_OVERLAY | SUF_WEAPON | SUF_ITEM;
|
|
||||||
if (f->UseFlags != nullptr)
|
|
||||||
{
|
|
||||||
useflags = 0;
|
|
||||||
auto p = f->UseFlags;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
switch (p->Id)
|
|
||||||
{
|
|
||||||
case NAME_Actor:
|
|
||||||
useflags |= SUF_ACTOR;
|
|
||||||
break;
|
|
||||||
case NAME_Overlay:
|
|
||||||
useflags |= SUF_OVERLAY;
|
|
||||||
break;
|
|
||||||
case NAME_Weapon:
|
|
||||||
useflags |= SUF_WEAPON;
|
|
||||||
break;
|
|
||||||
case NAME_Item:
|
|
||||||
useflags |= SUF_ITEM;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Error(p, "Unknown Action qualifier %s", FName(p->Id).GetChars());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = static_cast<decltype(p)>(p->SiblingNext);
|
|
||||||
} while (p != f->UseFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
// map to implementation flags.
|
|
||||||
if (f->Flags & ZCC_Private) varflags |= VARF_Private;
|
|
||||||
if (f->Flags & ZCC_Protected) varflags |= VARF_Protected;
|
|
||||||
if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
|
|
||||||
if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual;
|
|
||||||
if (f->Flags & ZCC_Override) varflags |= VARF_Override;
|
|
||||||
if (f->Flags & ZCC_Action)
|
|
||||||
{
|
|
||||||
varflags |= VARF_Final; // Action implies Final.
|
|
||||||
if (useflags & (SUF_OVERLAY | SUF_WEAPON | SUF_ITEM))
|
|
||||||
{
|
|
||||||
varflags |= VARF_Action;
|
|
||||||
implicitargs = 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
implicitargs = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final.
|
|
||||||
|
|
||||||
|
|
||||||
if (varflags & VARF_Override) varflags &= ~VARF_Virtual; // allow 'virtual override'.
|
|
||||||
// Only one of these flags may be used.
|
|
||||||
static int exclude[] = { ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static };
|
|
||||||
static const char * print[] = { "virtual", "override", "action", "static" };
|
|
||||||
int fc = 0;
|
|
||||||
FString build;
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
if (f->Flags & exclude[i])
|
|
||||||
{
|
|
||||||
fc++;
|
|
||||||
if (build.Len() > 0) build += ", ";
|
|
||||||
build += print[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fc > 1)
|
|
||||||
{
|
|
||||||
Error(f, "Invalid combination of qualifiers %s on function %s.", FName(f->Name).GetChars(), build.GetChars() );
|
|
||||||
varflags |= VARF_Method;
|
|
||||||
}
|
|
||||||
if (varflags & VARF_Override) varflags |= VARF_Virtual; // Now that the flags are checked, make all override functions virtual as well.
|
|
||||||
|
|
||||||
if (f->Flags & ZCC_Native)
|
|
||||||
{
|
|
||||||
varflags |= VARF_Native;
|
|
||||||
afd = FindFunction(c->Type(), FName(f->Name).GetChars());
|
|
||||||
if (afd == nullptr)
|
|
||||||
{
|
|
||||||
Error(f, "The function '%s' has not been exported from the executable.", FName(f->Name).GetChars());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
(*afd->VMPointer)->ImplicitArgs = BYTE(implicitargs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags, useflags);
|
|
||||||
argdefaults.Resize(argnames.Size());
|
|
||||||
auto p = f->Params;
|
|
||||||
bool hasoptionals = false;
|
|
||||||
if (p != nullptr)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
int elementcount = 1;
|
|
||||||
VMValue vmval[3]; // default is REGT_NIL which means 'no default value' here.
|
|
||||||
if (p->Type != nullptr)
|
|
||||||
{
|
|
||||||
auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false);
|
|
||||||
int flags = 0;
|
|
||||||
if (p->Flags & ZCC_In) flags |= VARF_In;
|
|
||||||
if (p->Flags & ZCC_Out) flags |= VARF_Out;
|
|
||||||
if ((type->IsA(RUNTIME_CLASS(PStruct))) || (flags & VARF_Out))
|
|
||||||
{
|
|
||||||
// 'out' parameters and all structs except vectors are passed by reference
|
|
||||||
if ((flags & VARF_Out) || (type != TypeVector2 && type != TypeVector3))
|
|
||||||
{
|
|
||||||
type = NewPointer(type);
|
|
||||||
flags |= VARF_Ref;
|
|
||||||
}
|
|
||||||
else if (type == TypeVector2)
|
|
||||||
{
|
|
||||||
elementcount = 2;
|
|
||||||
}
|
|
||||||
else if (type == TypeVector3)
|
|
||||||
{
|
|
||||||
elementcount = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3)
|
|
||||||
{
|
|
||||||
Error(p, "Invalid type %s for function parameter", type->DescriptiveName());
|
|
||||||
}
|
|
||||||
else if (p->Default != nullptr)
|
|
||||||
{
|
|
||||||
flags |= VARF_Optional;
|
|
||||||
hasoptionals = true;
|
|
||||||
// The simplifier is not suited to convert the constant into something usable.
|
|
||||||
// All it does is reduce the expression to a constant but we still got to do proper type checking and conversion.
|
|
||||||
// It will also lose important type info about enums, once these get implemented
|
|
||||||
// The code generator can do this properly for us.
|
|
||||||
FxExpression *x = new FxTypeCast(ConvertNode(p->Default), type, false);
|
|
||||||
FCompileContext ctx(c->Type(), false);
|
|
||||||
x = x->Resolve(ctx);
|
|
||||||
|
|
||||||
if (x != nullptr)
|
|
||||||
{
|
|
||||||
// Vectors need special treatment because they use more than one entry in the Defaults and do not report as actual constants
|
|
||||||
if (type == TypeVector2 && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(2))
|
|
||||||
{
|
|
||||||
auto vx = static_cast<FxVectorValue *>(x);
|
|
||||||
vmval[0] = static_cast<FxConstant *>(vx->xyz[0])->GetValue().GetFloat();
|
|
||||||
vmval[1] = static_cast<FxConstant *>(vx->xyz[1])->GetValue().GetFloat();
|
|
||||||
}
|
|
||||||
else if (type == TypeVector3 && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(3))
|
|
||||||
{
|
|
||||||
auto vx = static_cast<FxVectorValue *>(x);
|
|
||||||
vmval[0] = static_cast<FxConstant *>(vx->xyz[0])->GetValue().GetFloat();
|
|
||||||
vmval[1] = static_cast<FxConstant *>(vx->xyz[1])->GetValue().GetFloat();
|
|
||||||
vmval[2] = static_cast<FxConstant *>(vx->xyz[2])->GetValue().GetFloat();
|
|
||||||
}
|
|
||||||
else if (!x->isConstant())
|
|
||||||
{
|
|
||||||
Error(p, "Default parameter %s is not constant in %s", FName(p->Name).GetChars(), FName(f->Name).GetChars());
|
|
||||||
}
|
|
||||||
else if (x->ValueType != type)
|
|
||||||
{
|
|
||||||
Error(p, "Default parameter %s could not be converted to target type %s", FName(p->Name).GetChars(), c->Type()->TypeName.GetChars());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto cnst = static_cast<FxConstant *>(x);
|
|
||||||
hasdefault = true;
|
|
||||||
switch (type->GetRegType())
|
|
||||||
{
|
|
||||||
case REGT_INT:
|
|
||||||
vmval[0] = cnst->GetValue().GetInt();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REGT_FLOAT:
|
|
||||||
vmval[0] = cnst->GetValue().GetFloat();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REGT_POINTER:
|
|
||||||
if (type->IsKindOf(RUNTIME_CLASS(PClassPointer)))
|
|
||||||
vmval[0] = (DObject*)cnst->GetValue().GetPointer();
|
|
||||||
else
|
|
||||||
vmval[0] = cnst->GetValue().GetPointer();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REGT_STRING:
|
|
||||||
vmval[0] = cnst->GetValue().GetString();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(0 && "no valid type for constant");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (x != nullptr) delete x;
|
|
||||||
}
|
|
||||||
else if (hasoptionals)
|
|
||||||
{
|
|
||||||
Error(p, "All arguments after the first optional one need also be optional.");
|
|
||||||
}
|
|
||||||
// TBD: disallow certain types? For now, let everything pass that isn't an array.
|
|
||||||
args.Push(type);
|
|
||||||
argflags.Push(flags);
|
|
||||||
argnames.Push(p->Name);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
args.Push(nullptr);
|
|
||||||
argflags.Push(0);
|
|
||||||
argnames.Push(NAME_None);
|
|
||||||
}
|
|
||||||
for(int i=0;i<elementcount;i++) argdefaults.Push(vmval[i]);
|
|
||||||
p = static_cast<decltype(p)>(p->SiblingNext);
|
|
||||||
} while (p != f->Params);
|
|
||||||
}
|
|
||||||
|
|
||||||
PFunction *sym = new PFunction(c->Type(), f->Name);
|
|
||||||
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr? nullptr : *(afd->VMPointer), varflags, useflags);
|
|
||||||
c->Type()->Symbols.ReplaceSymbol(sym);
|
|
||||||
|
|
||||||
if (!(f->Flags & ZCC_Native))
|
|
||||||
{
|
|
||||||
auto code = ConvertAST(c->Type(), f->Body);
|
|
||||||
if (code != nullptr)
|
|
||||||
{
|
|
||||||
sym->Variants[0].Implementation = FunctionBuildList.AddFunction(sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sym->Variants[0].Implementation != nullptr && hasdefault) // do not copy empty default lists, they only waste space and processing time.
|
|
||||||
{
|
|
||||||
sym->Variants[0].Implementation->DefaultArgs = std::move(argdefaults);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (varflags & VARF_Virtual)
|
|
||||||
{
|
|
||||||
if (varflags & VARF_Final)
|
|
||||||
{
|
|
||||||
sym->Variants[0].Implementation->Final = true;
|
|
||||||
}
|
|
||||||
int vindex = c->Type()->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto);
|
|
||||||
// specifying 'override' is necessary to prevent one of the biggest problem spots with virtual inheritance: Mismatching argument types.
|
|
||||||
if (varflags & VARF_Override)
|
|
||||||
{
|
|
||||||
if (vindex == -1)
|
|
||||||
{
|
|
||||||
Error(p, "Attempt to override non-existent virtual function %s", FName(f->Name).GetChars());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto oldfunc = c->Type()->Virtuals[vindex];
|
|
||||||
if (oldfunc->Final)
|
|
||||||
{
|
|
||||||
Error(p, "Attempt to override final function %s", FName(f->Name).GetChars());
|
|
||||||
}
|
|
||||||
c->Type()->Virtuals[vindex] = sym->Variants[0].Implementation;
|
|
||||||
sym->Variants[0].Implementation->VirtualIndex = vindex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (vindex != -1)
|
|
||||||
{
|
|
||||||
Error(p, "Function %s attempts to override parent function without 'override' qualifier", FName(f->Name).GetChars());
|
|
||||||
}
|
|
||||||
sym->Variants[0].Implementation->VirtualIndex = c->Type()->Virtuals.Push(sym->Variants[0].Implementation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2368,7 +2400,9 @@ FxExpression *ZCCCompiler::SetupActionFunction(PClass *cls, ZCC_TreeNode *af)
|
||||||
{
|
{
|
||||||
FArgumentList argumentlist;
|
FArgumentList argumentlist;
|
||||||
// We can use this function directly without wrapping it in a caller.
|
// We can use this function directly without wrapping it in a caller.
|
||||||
if ((afd->Variants[0].Flags & VARF_Action) || !cls->IsDescendantOf(RUNTIME_CLASS(AStateProvider)) || !afd->Variants[0].SelfClass->IsDescendantOf(RUNTIME_CLASS(AStateProvider)))
|
auto selfclass = dyn_cast<PClass>(afd->Variants[0].SelfClass);
|
||||||
|
assert(selfclass != nullptr); // non classes are not supposed to get here.
|
||||||
|
if ((afd->Variants[0].Flags & VARF_Action) || !cls->IsDescendantOf(RUNTIME_CLASS(AStateProvider)) || !selfclass->IsDescendantOf(RUNTIME_CLASS(AStateProvider)))
|
||||||
{
|
{
|
||||||
return new FxVMFunctionCall(new FxSelf(*af), afd, argumentlist, *af, false);
|
return new FxVMFunctionCall(new FxSelf(*af), afd, argumentlist, *af, false);
|
||||||
}
|
}
|
||||||
|
@ -2663,7 +2697,7 @@ void ZCCCompiler::CompileStates()
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
FxExpression *ZCCCompiler::ConvertAST(PClass *cls, ZCC_TreeNode *ast)
|
FxExpression *ZCCCompiler::ConvertAST(PStruct *cls, ZCC_TreeNode *ast)
|
||||||
{
|
{
|
||||||
ConvertClass = cls;
|
ConvertClass = cls;
|
||||||
// there are two possibilities here: either a single function call or a compound statement. For a compound statement we also need to check if the last thing added was a return.
|
// there are two possibilities here: either a single function call or a compound statement. For a compound statement we also need to check if the last thing added was a return.
|
||||||
|
|
|
@ -20,6 +20,11 @@ struct ZCC_StructWork
|
||||||
TArray<ZCC_Enum *> Enums;
|
TArray<ZCC_Enum *> Enums;
|
||||||
TArray<ZCC_ConstantDef *> Constants;
|
TArray<ZCC_ConstantDef *> Constants;
|
||||||
TArray<ZCC_VarDeclarator *> Fields;
|
TArray<ZCC_VarDeclarator *> Fields;
|
||||||
|
TArray<ZCC_FuncDeclarator *> Functions;
|
||||||
|
|
||||||
|
ZCC_StructWork()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
ZCC_StructWork(ZCC_Struct * s, PSymbolTreeNode *n, ZCC_Class *outer)
|
ZCC_StructWork(ZCC_Struct * s, PSymbolTreeNode *n, ZCC_Class *outer)
|
||||||
{
|
{
|
||||||
|
@ -41,34 +46,25 @@ struct ZCC_StructWork
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ZCC_ClassWork
|
struct ZCC_ClassWork : public ZCC_StructWork
|
||||||
{
|
{
|
||||||
ZCC_Class *cls;
|
ZCC_Class *cls;
|
||||||
PSymbolTable TreeNodes;
|
|
||||||
PSymbolTreeNode *node;
|
|
||||||
TArray<ZCC_Enum *> Enums;
|
|
||||||
TArray<ZCC_ConstantDef *> Constants;
|
|
||||||
TArray<ZCC_VarDeclarator *> Fields;
|
|
||||||
TArray<ZCC_Default *> Defaults;
|
TArray<ZCC_Default *> Defaults;
|
||||||
TArray<ZCC_FuncDeclarator *> Functions;
|
|
||||||
TArray<ZCC_States *> States;
|
TArray<ZCC_States *> States;
|
||||||
|
|
||||||
ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n)
|
ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n)
|
||||||
{
|
{
|
||||||
|
strct = s;
|
||||||
cls = s;
|
cls = s;
|
||||||
node = n;
|
node = n;
|
||||||
}
|
OuterDef = nullptr;
|
||||||
|
Outer = nullptr;
|
||||||
FName NodeName() const
|
|
||||||
{
|
|
||||||
return cls->NodeName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PClass *Type()
|
PClass *Type()
|
||||||
{
|
{
|
||||||
return cls->Type;
|
return static_cast<PClass *>(strct->Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ZCC_ConstantWork
|
struct ZCC_ConstantWork
|
||||||
|
@ -108,6 +104,7 @@ private:
|
||||||
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);
|
||||||
|
void CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool forclass);
|
||||||
|
|
||||||
void InitFunctions();
|
void InitFunctions();
|
||||||
void CompileStates();
|
void CompileStates();
|
||||||
|
@ -142,12 +139,12 @@ private:
|
||||||
void Error(ZCC_TreeNode *node, const char *msg, ...);
|
void Error(ZCC_TreeNode *node, const char *msg, ...);
|
||||||
void MessageV(ZCC_TreeNode *node, const char *txtcolor, const char *msg, va_list argptr);
|
void MessageV(ZCC_TreeNode *node, const char *txtcolor, const char *msg, va_list argptr);
|
||||||
|
|
||||||
FxExpression *ConvertAST(PClass *cclass, ZCC_TreeNode *ast);
|
FxExpression *ConvertAST(PStruct *cclass, ZCC_TreeNode *ast);
|
||||||
FxExpression *ConvertNode(ZCC_TreeNode *node);
|
FxExpression *ConvertNode(ZCC_TreeNode *node);
|
||||||
FArgumentList &ConvertNodeList(FArgumentList &, ZCC_TreeNode *head);
|
FArgumentList &ConvertNodeList(FArgumentList &, ZCC_TreeNode *head);
|
||||||
|
|
||||||
DObject *Outer;
|
DObject *Outer;
|
||||||
PClass *ConvertClass; // class type to be used when resoving symbold while converting an AST
|
PStruct *ConvertClass; // class type to be used when resoving symbols while converting an AST
|
||||||
PSymbolTable *GlobalTreeNodes;
|
PSymbolTable *GlobalTreeNodes;
|
||||||
PSymbolTable *OutputSymbols;
|
PSymbolTable *OutputSymbols;
|
||||||
ZCC_AST &AST;
|
ZCC_AST &AST;
|
||||||
|
|
|
@ -202,19 +202,19 @@ struct ZCC_NamedNode : ZCC_TreeNode
|
||||||
PSymbolType *Symbol;
|
PSymbolType *Symbol;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ZCC_Class : ZCC_NamedNode
|
struct ZCC_Struct : ZCC_NamedNode
|
||||||
|
{
|
||||||
|
VM_UWORD Flags;
|
||||||
|
ZCC_TreeNode *Body;
|
||||||
|
PStruct *Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_Class : ZCC_Struct
|
||||||
{
|
{
|
||||||
ZCC_Identifier *ParentName;
|
ZCC_Identifier *ParentName;
|
||||||
ZCC_Identifier *Replaces;
|
ZCC_Identifier *Replaces;
|
||||||
VM_UWORD Flags;
|
|
||||||
ZCC_TreeNode *Body;
|
|
||||||
PClass *Type;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ZCC_Struct : ZCC_NamedNode
|
PClass *CType() { return static_cast<PClass *>(Type); }
|
||||||
{
|
|
||||||
ZCC_TreeNode *Body;
|
|
||||||
PStruct *Type;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ZCC_Enum : ZCC_NamedNode
|
struct ZCC_Enum : ZCC_NamedNode
|
||||||
|
|
Loading…
Reference in a new issue