diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 21074b9f2..90dd83750 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -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 &out_params, + PFunction *afd, FString statestring, FStateDefinitions *statedef); PFunction *FindGlobalActionFunction(const char *name); diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 92e5ab686..5b7c1f0f4 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -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(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(')'); diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 1c840c19b..8d3c162df 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -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 diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 60f24ad95..5fec0cf45 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -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 &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; +} + //========================================================================== // // diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index fec49cc7c..041d3790e 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -326,99 +326,7 @@ do_stop: if (afd != NULL) { tcall->Function = afd->Variants[0].Implementation; - const TArray ¶ms = afd->Variants[0].Proto->ArgumentTypes; - const TArray ¶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 &out_params, + PFunction *afd, FString statestring, FStateDefinitions *statedef) +{ + const TArray ¶ms = afd->Variants[0].Proto->ArgumentTypes; + const TArray ¶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(); + } +} diff --git a/src/zscript/vmbuilder.h b/src/zscript/vmbuilder.h index 74537c857..b8b1df848 100644 --- a/src/zscript/vmbuilder.h +++ b/src/zscript/vmbuilder.h @@ -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);