Use action function return value to make state jumps happen

- The A_Jump family of action functions now return the state to jump
  to (NULL if no jump is to be taken) instead of jumping directly.
  It is the caller's responsibility to handle the jump. This will
  make it possible to use their results in if statements and
  do something other than jump.
- DECORATE return statements can now return the result of a function
  (but not any random expression--it must be a function call). To
  make a jump happen from inside a multi-action block, you must
  return the value of an A_Jump function. e.g.:
    { return A_Jump(128, "SomeState"); }
- The VMFunction class now contains its prototype instead of storing
  it at a higher level in PFunction. This is so that
  FState::CallAction can easily tell if a function returns a state.
- Removed the FxTailable class because with explicit return
  statements, it's not useful anymore.
This commit is contained in:
Randy Heit 2016-02-18 20:39:40 -06:00
commit b2ccd0bd28
18 changed files with 662 additions and 534 deletions

View file

@ -375,6 +375,42 @@ static void ParseArgListDef(FScanner &sc, PClassActor *cls,
sc.MustGetToken(';');
}
//==========================================================================
//
// SetImplicitArgs
//
// Adds the parameters implied by the function flags.
//
//==========================================================================
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, PClassActor *cls, DWORD funcflags)
{
// Must be called before adding any other arguments.
assert(args == NULL || args->Size() == 0);
assert(argflags == NULL || argflags->Size() == 0);
if (funcflags & VARF_Method)
{
// implied self pointer
if (args != NULL) args->Push(NewClassPointer(cls));
if (argflags != NULL) argflags->Push(0);
}
if (funcflags & VARF_Action)
{
// implied stateowner and callingstate pointers
if (args != NULL)
{
args->Push(NewClassPointer(RUNTIME_CLASS(AActor)));
args->Push(TypeState);
}
if (argflags != NULL)
{
argflags->Push(0);
argflags->Push(0);
}
}
}
//==========================================================================
//
// ParseFunctionDef
@ -401,15 +437,7 @@ void ParseFunctionDef(FScanner &sc, PClassActor *cls, FName funcname,
}
sc.MustGetToken('(');
if (funcflags & VARF_Method)
{
args.Push(NewClassPointer(cls)), argflags.Push(0); // implied self pointer
}
if (funcflags & VARF_Action)
{
args.Push(NewClassPointer(RUNTIME_CLASS(AActor))), argflags.Push(0); // implied stateowner pointer
args.Push(TypeState), argflags.Push(0); // implied callingstate pointer
}
SetImplicitArgs(&args, &argflags, cls, funcflags);
ParseArgListDef(sc, cls, args, argflags);
if (afd != NULL)
@ -1036,18 +1064,22 @@ static void ParseActionDef (FScanner &sc, PClassActor *cls)
sc.MustGetToken(TK_Native);
// check for a return value
if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Bool))
do
{
rets.Push(TypeSInt32);
}
else if (sc.CheckToken(TK_State))
{
rets.Push(TypeState);
}
else if (sc.CheckToken(TK_Float))
{
rets.Push(TypeFloat64);
if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Bool))
{
rets.Push(TypeSInt32);
}
else if (sc.CheckToken(TK_State))
{
rets.Push(TypeState);
}
else if (sc.CheckToken(TK_Float))
{
rets.Push(TypeFloat64);
}
}
while (sc.CheckToken(','));
sc.MustGetToken(TK_Identifier);
funcname = sc.String;
ParseFunctionDef(sc, cls, funcname, rets, VARF_Method | VARF_Action);