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:
Randy Heit 2014-12-30 23:31:07 -06:00
parent 9e3ed70021
commit c6c2b21901
6 changed files with 243 additions and 97 deletions

View file

@ -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);

View file

@ -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(')');

View file

@ -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

View file

@ -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;
}
//==========================================================================
//
//

View file

@ -326,99 +326,7 @@ do_stop:
if (afd != NULL)
{
tcall->Function = afd->Variants[0].Implementation;
const TArray<PType *> &params = afd->Variants[0].Proto->ArgumentTypes;
const TArray<DWORD> &paramflags = 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 *> &params = afd->Variants[0].Proto->ArgumentTypes;
const TArray<DWORD> &paramflags = 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();
}
}

View file

@ -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);