mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-11-11 07:12:16 +00:00
Add FxVMFunctionCall class
- This replaces the general extensibility that had existed formerly in thingdef_function.cpp. Parameter parsing for function calls is shared with state parameter parsing. Functions are defined exactly in the same way as action functions, but without the 'action' keyword.
This commit is contained in:
parent
9e3ed70021
commit
c6c2b21901
6 changed files with 243 additions and 97 deletions
|
@ -188,6 +188,8 @@ AFuncDesc *FindFunction(const char * string);
|
|||
|
||||
|
||||
void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag);
|
||||
void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params,
|
||||
PFunction *afd, FString statestring, FStateDefinitions *statedef);
|
||||
|
||||
PFunction *FindGlobalActionFunction(const char *name);
|
||||
|
||||
|
|
|
@ -459,16 +459,21 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls)
|
|||
FName identifier = FName(sc.String);
|
||||
if (sc.CheckToken('('))
|
||||
{
|
||||
FArgumentList *args = NULL;
|
||||
FArgumentList *args = new FArgumentList;
|
||||
PFunction *func = dyn_cast<PFunction>(cls->Symbols.FindSymbol(identifier, true));
|
||||
try
|
||||
{
|
||||
if (!sc.CheckToken(')'))
|
||||
if (func != NULL)
|
||||
{
|
||||
sc.UnGet();
|
||||
ParseFunctionParameters(sc, cls, *args, func, "", NULL);
|
||||
return new FxVMFunctionCall(func, args, sc);
|
||||
}
|
||||
else if (!sc.CheckToken(')'))
|
||||
{
|
||||
args = new FArgumentList;
|
||||
do
|
||||
{
|
||||
args->Push(ParseExpressionM (sc, cls));
|
||||
|
||||
}
|
||||
while (sc.CheckToken(','));
|
||||
sc.MustGetToken(')');
|
||||
|
|
|
@ -782,6 +782,25 @@ public:
|
|||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxVMFunctionCall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxVMFunctionCall : public FxExpression
|
||||
{
|
||||
PFunction *Function;
|
||||
FArgumentList *ArgList;
|
||||
PType *ReturnType;
|
||||
|
||||
public:
|
||||
FxVMFunctionCall(PFunction *func, FArgumentList *args, const FScriptPosition &pos);
|
||||
~FxVMFunctionCall();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxGlobalFunctionCall
|
||||
|
|
|
@ -3004,6 +3004,109 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build)
|
|||
return dest;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxVMFunctionCall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxVMFunctionCall::FxVMFunctionCall(PFunction *func, FArgumentList *args, const FScriptPosition &pos)
|
||||
: FxExpression(pos)
|
||||
{
|
||||
Function = func;
|
||||
ArgList = args;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxVMFunctionCall::~FxVMFunctionCall()
|
||||
{
|
||||
SAFE_DELETE(ArgList);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxVMFunctionCall :: Resolve
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
bool failed = false;
|
||||
|
||||
if (ArgList != NULL)
|
||||
{
|
||||
for (unsigned i = 0; i < ArgList->Size(); i++)
|
||||
{
|
||||
(*ArgList)[i] = (*ArgList)[i]->Resolve(ctx);
|
||||
if ((*ArgList)[i] == NULL) failed = true;
|
||||
}
|
||||
}
|
||||
if (failed)
|
||||
{
|
||||
delete this;
|
||||
return NULL;
|
||||
}
|
||||
TArray<PType *> &rets = Function->Variants[0].Proto->ReturnTypes;
|
||||
assert(rets.Size() == 1);
|
||||
ReturnType = rets[0];
|
||||
// If more types are added to ParseNativeVariable(), add them here too.
|
||||
if (rets[0] == TypeSInt32) ValueType = VAL_Int;
|
||||
else if (rets[0] == TypeFloat64) ValueType = VAL_Float;
|
||||
else if (rets[0] == TypeAngle) ValueType = VAL_Angle;
|
||||
else if (rets[0] == TypeFixed) ValueType = VAL_Fixed;
|
||||
else
|
||||
{
|
||||
ValueType = VAL_Int;
|
||||
assert(0 && "Unhandled return type in FxVMFunctionCall::Resolve");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Assumption: This call is being made to generate code inside an action
|
||||
// method, so the first three address registers are all set up for such a
|
||||
// function. (self, stateowner, callingstate)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
assert(build->Registers[REGT_POINTER].GetMostUsed() >= 3);
|
||||
int count = ArgList->Size();
|
||||
|
||||
// Emit code to pass implied parameters
|
||||
if (Function->Flags & VARF_Method)
|
||||
{
|
||||
build->Emit(OP_PARAM, 0, REGT_POINTER, 0);
|
||||
count += 1;
|
||||
}
|
||||
if (Function->Flags & VARF_Action)
|
||||
{
|
||||
build->Emit(OP_PARAM, 0, REGT_POINTER, 1);
|
||||
build->Emit(OP_PARAM, 0, REGT_POINTER, 2);
|
||||
count += 2;
|
||||
}
|
||||
// Emit code to pass explicit parameters
|
||||
for (unsigned i = 0; i < ArgList->Size(); ++i)
|
||||
{
|
||||
(*ArgList)[i]->Emit(build);
|
||||
}
|
||||
// Get a register to store the return value in
|
||||
assert(ReturnType != NULL);
|
||||
ExpEmit reg(build, ReturnType->GetRegType());
|
||||
// Emit the call itself
|
||||
build->Emit(OP_CALL_K, build->GetConstantAddress(Function->Variants[0].Implementation, ATAG_OBJECT), count, 1);
|
||||
build->Emit(OP_RESULT, 0, reg.RegType, reg.RegNum);
|
||||
return reg;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
|
|
|
@ -326,99 +326,7 @@ do_stop:
|
|||
if (afd != NULL)
|
||||
{
|
||||
tcall->Function = afd->Variants[0].Implementation;
|
||||
const TArray<PType *> ¶ms = afd->Variants[0].Proto->ArgumentTypes;
|
||||
const TArray<DWORD> ¶mflags = afd->Variants[0].ArgFlags;
|
||||
int numparams = (int)params.Size();
|
||||
int pnum = 0;
|
||||
if (afd->Flags & VARF_Method)
|
||||
{
|
||||
numparams--;
|
||||
pnum++;
|
||||
}
|
||||
if (afd->Flags & VARF_Action)
|
||||
{
|
||||
numparams -= 2;
|
||||
pnum += 2;
|
||||
}
|
||||
if (numparams > 0)
|
||||
{
|
||||
int v;
|
||||
|
||||
if (!(paramflags[pnum] & VARF_Optional))
|
||||
{
|
||||
sc.MustGetStringName("(");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sc.CheckString("("))
|
||||
{
|
||||
goto endofstate;
|
||||
}
|
||||
}
|
||||
|
||||
while (numparams > 0)
|
||||
{
|
||||
FxExpression *x;
|
||||
if (params[pnum] == TypeState && sc.CheckNumber())
|
||||
{
|
||||
// Special case: State label as an offset
|
||||
if (sc.Number > 0 && statestring.Len() > 1)
|
||||
{
|
||||
sc.ScriptError("You cannot use state jumps commands with a jump offset on multistate definitions\n");
|
||||
}
|
||||
|
||||
v = sc.Number;
|
||||
if (v<0)
|
||||
{
|
||||
sc.ScriptError("Negative jump offsets are not allowed");
|
||||
}
|
||||
|
||||
if (v > 0)
|
||||
{
|
||||
x = new FxStateByIndex(bag.statedef.GetStateCount() + v, sc);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = new FxConstant((FState*)NULL, sc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the generic parameter parser for everything else
|
||||
x = ParseParameter(sc, bag.Info, params[pnum], false);
|
||||
}
|
||||
tcall->Parameters.Push(new FxParameter(x));
|
||||
pnum++;
|
||||
numparams--;
|
||||
if (numparams > 0)
|
||||
{
|
||||
if (params[pnum] == NULL)
|
||||
{ // varargs function
|
||||
if (sc.CheckString(")"))
|
||||
{
|
||||
goto endofstate;
|
||||
}
|
||||
pnum--;
|
||||
numparams++;
|
||||
}
|
||||
else if ((paramflags[pnum] & VARF_Optional) && sc.CheckString(")"))
|
||||
{
|
||||
goto endofstate;
|
||||
}
|
||||
sc.MustGetStringName (",");
|
||||
}
|
||||
}
|
||||
sc.MustGetStringName(")");
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.MustGetString();
|
||||
if (sc.Compare("("))
|
||||
{
|
||||
sc.ScriptError("You cannot pass parameters to '%s'\n",sc.String);
|
||||
}
|
||||
sc.UnGet();
|
||||
}
|
||||
ParseFunctionParameters(sc, bag.Info, tcall->Parameters, afd, statestring, &bag.statedef);
|
||||
goto endofstate;
|
||||
}
|
||||
sc.ScriptError("Invalid state parameter %s\n", sc.String);
|
||||
|
@ -448,3 +356,111 @@ endofstate:
|
|||
sc.SetEscape(true); // re-enable escape sequences
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ParseFunctionParameters
|
||||
//
|
||||
// Parses the parameters for a VM function. Called by both ParseStates
|
||||
// (which will set statestring and statedef) and by ParseExpression0 (which
|
||||
// will not set them). The first token returned by the scanner when entering
|
||||
// this function should be '('.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params,
|
||||
PFunction *afd, FString statestring, FStateDefinitions *statedef)
|
||||
{
|
||||
const TArray<PType *> ¶ms = afd->Variants[0].Proto->ArgumentTypes;
|
||||
const TArray<DWORD> ¶mflags = afd->Variants[0].ArgFlags;
|
||||
int numparams = (int)params.Size();
|
||||
int pnum = 0;
|
||||
if (afd->Flags & VARF_Method)
|
||||
{
|
||||
numparams--;
|
||||
pnum++;
|
||||
}
|
||||
if (afd->Flags & VARF_Action)
|
||||
{
|
||||
numparams -= 2;
|
||||
pnum += 2;
|
||||
}
|
||||
if (numparams > 0)
|
||||
{
|
||||
int v;
|
||||
|
||||
if (!(paramflags[pnum] & VARF_Optional))
|
||||
{
|
||||
sc.MustGetStringName("(");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sc.CheckString("("))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (numparams > 0)
|
||||
{
|
||||
FxExpression *x;
|
||||
if (statedef != NULL && params[pnum] == TypeState && sc.CheckNumber())
|
||||
{
|
||||
// Special case: State label as an offset
|
||||
if (sc.Number > 0 && statestring.Len() > 1)
|
||||
{
|
||||
sc.ScriptError("You cannot use state jumps commands with a jump offset on multistate definitions\n");
|
||||
}
|
||||
|
||||
v = sc.Number;
|
||||
if (v<0)
|
||||
{
|
||||
sc.ScriptError("Negative jump offsets are not allowed");
|
||||
}
|
||||
|
||||
if (v > 0)
|
||||
{
|
||||
x = new FxStateByIndex(statedef->GetStateCount() + v, sc);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = new FxConstant((FState*)NULL, sc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the generic parameter parser for everything else
|
||||
x = ParseParameter(sc, cls, params[pnum], false);
|
||||
}
|
||||
out_params.Push(new FxParameter(x));
|
||||
pnum++;
|
||||
numparams--;
|
||||
if (numparams > 0)
|
||||
{
|
||||
if (params[pnum] == NULL)
|
||||
{ // varargs function
|
||||
if (sc.CheckString(")"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
pnum--;
|
||||
numparams++;
|
||||
}
|
||||
else if ((paramflags[pnum] & VARF_Optional) && sc.CheckString(")"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
sc.MustGetStringName (",");
|
||||
}
|
||||
}
|
||||
sc.MustGetStringName(")");
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.MustGetString();
|
||||
if (sc.Compare("("))
|
||||
{
|
||||
sc.ScriptError("You cannot pass parameters to '%s'\n", afd->SymbolName.GetChars());
|
||||
}
|
||||
sc.UnGet();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ public:
|
|||
{
|
||||
public:
|
||||
RegAvailability();
|
||||
int GetMostUsed() { return MostUsed; }
|
||||
int Get(int count); // Returns the first register in the range
|
||||
void Return(int reg, int count);
|
||||
bool Reuse(int regnum);
|
||||
|
|
Loading…
Reference in a new issue