From cf87d0f1e9a1e879698d7975409125253a44b51a Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Wed, 14 Aug 2013 22:00:16 -0500 Subject: [PATCH] Use PFunction instead of PSymbolActionFunction in DECORATE parser --- src/d_dehacked.cpp | 24 ++-- src/dobjtype.cpp | 22 +++- src/dobjtype.h | 31 +----- src/thingdef/olddecorations.cpp | 18 +-- src/thingdef/thingdef.h | 4 +- src/thingdef/thingdef_data.cpp | 4 +- src/thingdef/thingdef_parse.cpp | 185 +++++++++++++++---------------- src/thingdef/thingdef_states.cpp | 53 +++++---- 8 files changed, 169 insertions(+), 172 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index d0ba97237..a375b3435 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -81,7 +81,7 @@ static void UnloadDehSupp (); // This is a list of all the action functions used by each of Doom's states. -static TArray Actions; +static TArray Actions; // These are the original heights of every Doom 2 thing. They are used if a patch // specifies that a thing should be hanging from the ceiling but doesn't specify @@ -788,8 +788,8 @@ void SetDehParams(FState *state, int codepointer) FScriptPosition *pos = new FScriptPosition(FString("DEHACKED"), 0); // Let's identify the codepointer we're dealing with. - PSymbolActionFunction *sym; - sym = dyn_cast(RUNTIME_CLASS(AInventory)->Symbols.FindSymbol(FName(MBFCodePointers[codepointer].name), true)); + PFunction *sym; + sym = dyn_cast(RUNTIME_CLASS(AInventory)->Symbols.FindSymbol(FName(MBFCodePointers[codepointer].name), true)); if (sym == NULL) return; if (codepointer < 0 || codepointer >= countof(MBFCodePointerFactories)) @@ -809,7 +809,7 @@ void SetDehParams(FState *state, int codepointer) buildit.Emit(OP_PARAM, 0, REGT_POINTER, 2); // Emit code for action parameters. int argcount = MBFCodePointerFactories[codepointer](buildit, value1, value2); - buildit.Emit(OP_TAIL_K, buildit.GetConstantAddress(sym->Function, ATAG_OBJECT), NAP + argcount, 0); + buildit.Emit(OP_TAIL_K, buildit.GetConstantAddress(sym->Variants[0].Implementation, ATAG_OBJECT), NAP + argcount, 0); // Attach it to the state. VMScriptFunction *sfunc = buildit.MakeFunction(); sfunc->NumArgs = NAP; @@ -1711,7 +1711,7 @@ static int PatchWeapon (int weapNum) return result; } -static void SetPointer(FState *state, PSymbolActionFunction *sym, int frame = 0) +static void SetPointer(FState *state, PFunction *sym, int frame = 0) { if (sym == NULL) { @@ -1720,7 +1720,7 @@ static void SetPointer(FState *state, PSymbolActionFunction *sym, int frame = 0) } else { - state->SetAction(sym->Function); + state->SetAction(sym->Variants[0].Implementation); for (unsigned int i = 0; i < MBFCodePointers.Size(); i++) { @@ -2103,15 +2103,15 @@ static int PatchCodePtrs (int dummy) // This skips the action table and goes directly to the internal symbol table // DEH compatible functions are easy to recognize. - PSymbolActionFunction *sym = dyn_cast(RUNTIME_CLASS(AInventory)->Symbols.FindSymbol(symname, true)); + PFunction *sym = dyn_cast(RUNTIME_CLASS(AInventory)->Symbols.FindSymbol(symname, true)); if (sym == NULL) { Printf("Frame %d: Unknown code pointer '%s'\n", frame, Line2); } else { - FString &args = sym->Arguments; - if (args.Len() != 0 && (args[0] < 'a' || args[0] > 'z')) + TArray &args = sym->Variants[0].ArgFlags; + if (args.Size() != 0 && !(args[0] & VARF_Optional)) { Printf("Frame %d: Incompatible code pointer '%s'\n", frame, Line2); sym = NULL; @@ -2680,15 +2680,15 @@ static bool LoadDehSupp () // or AActor so this will find all of them. FString name = "A_"; name << sc.String; - PSymbolActionFunction *sym = dyn_cast(RUNTIME_CLASS(AInventory)->Symbols.FindSymbol(name, true)); + PFunction *sym = dyn_cast(RUNTIME_CLASS(AInventory)->Symbols.FindSymbol(name, true)); if (sym == NULL) { sc.ScriptError("Unknown code pointer '%s'", sc.String); } else { - FString &args = sym->Arguments; - if (args.Len() != 0 && (args[0] < 'a' || args[0] > 'z')) + TArray &args = sym->Variants[0].ArgFlags; + if (args.Size() != 0 && !(args[0] & VARF_Optional)) { sc.ScriptError("Incompatible code pointer '%s'", sc.String); } diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 443fd35b7..68e5e8b31 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -1145,6 +1145,25 @@ size_t PFunction::PropagateMark() return Variants.Size() * sizeof(Variants[0]) + Super::PropagateMark(); } +//========================================================================== +// +// PFunction :: AddVariant +// +// Adds a new variant for this function. Does not check if a matching +// variant already exists. +// +//========================================================================== + +unsigned PFunction::AddVariant(PPrototype *proto, TArray &argflags, VMFunction *impl) +{ + Variant variant; + + variant.Proto = proto; + variant.ArgFlags = argflags; + variant.Implementation = impl; + return Variants.Push(variant); +} + /* PClass *****************************************************************/ IMPLEMENT_POINTY_CLASS(PClass) @@ -1814,9 +1833,6 @@ CCMD(typetable) IMPLEMENT_ABSTRACT_CLASS(PSymbol); IMPLEMENT_CLASS(PSymbolConst); IMPLEMENT_CLASS(PSymbolVariable); -IMPLEMENT_POINTY_CLASS(PSymbolActionFunction) - DECLARE_POINTER(Function) -END_POINTERS IMPLEMENT_POINTY_CLASS(PSymbolVMFunction) DECLARE_POINTER(Function) END_POINTERS diff --git a/src/dobjtype.h b/src/dobjtype.h index 6223d2cdf..17748b13b 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -64,23 +64,7 @@ public: }; // An action function ------------------------------------------------------- -// -// The Arguments string is a string of characters as understood by -// the DECORATE parser: -// -// If the letter is uppercase, it is required. Lowercase letters are optional. -// i = integer -// f = fixed point -// s = sound name -// m = actor name -// t = string -// l = jump label -// c = color -// x = expression -// y = expression -// If the final character is a +, the previous parameter is repeated indefinitely, -// and an "imaginary" first parameter is inserted containing the total number of -// parameters passed. + struct FState; struct StateCallData; class VMFrameStack; @@ -89,18 +73,6 @@ struct VMReturn; typedef int (*actionf_p)(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret);/*(VM_ARGS)*/ class VMFunction; -class PSymbolActionFunction : public PSymbol -{ - DECLARE_CLASS(PSymbolActionFunction, PSymbol); - HAS_OBJECT_POINTERS; -public: - FString Arguments; - VMFunction *Function; - - PSymbolActionFunction(FName name) : PSymbol(name) {} - PSymbolActionFunction() : PSymbol(NAME_None) {} -}; - // A VM function ------------------------------------------------------------ class PSymbolVMFunction : public PSymbol @@ -469,6 +441,7 @@ public: TArray Variants; DWORD Flags; + unsigned AddVariant(PPrototype *proto, TArray &argflags, VMFunction *impl); size_t PropagateMark(); diff --git a/src/thingdef/olddecorations.cpp b/src/thingdef/olddecorations.cpp index 8c1da024c..541f3602c 100644 --- a/src/thingdef/olddecorations.cpp +++ b/src/thingdef/olddecorations.cpp @@ -233,24 +233,24 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) { if (extra.bExplosive) { - type->OwnedStates[extra.DeathStart].SetAction(FindGlobalActionFunction("A_Explode")->Function); + type->OwnedStates[extra.DeathStart].SetAction(FindGlobalActionFunction("A_Explode")->Variants[0].Implementation); } } else { // The first frame plays the death sound and // the second frame makes it nonsolid. - type->OwnedStates[extra.DeathStart].SetAction(FindGlobalActionFunction("A_Scream")->Function); + type->OwnedStates[extra.DeathStart].SetAction(FindGlobalActionFunction("A_Scream")->Variants[0].Implementation); if (extra.bSolidOnDeath) { } else if (extra.DeathStart + 1 < extra.DeathEnd) { - type->OwnedStates[extra.DeathStart+1].SetAction(FindGlobalActionFunction("A_NoBlocking")->Function); + type->OwnedStates[extra.DeathStart+1].SetAction(FindGlobalActionFunction("A_NoBlocking")->Variants[0].Implementation); } else { - type->OwnedStates[extra.DeathStart].SetAction(FindGlobalActionFunction("A_ScreamAndUnblock")->Function); + type->OwnedStates[extra.DeathStart].SetAction(FindGlobalActionFunction("A_ScreamAndUnblock")->Variants[0].Implementation); } if (extra.DeathHeight == 0) @@ -282,17 +282,17 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) // The first frame plays the burn sound and // the second frame makes it nonsolid. - type->OwnedStates[extra.FireDeathStart].SetAction(FindGlobalActionFunction("A_ActiveSound")->Function); + type->OwnedStates[extra.FireDeathStart].SetAction(FindGlobalActionFunction("A_ActiveSound")->Variants[0].Implementation); if (extra.bSolidOnBurn) { } else if (extra.FireDeathStart + 1 < extra.FireDeathEnd) { - type->OwnedStates[extra.FireDeathStart+1].SetAction(FindGlobalActionFunction("A_NoBlocking")->Function); + type->OwnedStates[extra.FireDeathStart+1].SetAction(FindGlobalActionFunction("A_NoBlocking")->Variants[0].Implementation); } else { - type->OwnedStates[extra.FireDeathStart].SetAction(FindGlobalActionFunction("A_ActiveAndUnblock")->Function); + type->OwnedStates[extra.FireDeathStart].SetAction(FindGlobalActionFunction("A_ActiveAndUnblock")->Variants[0].Implementation); } if (extra.BurnHeight == 0) extra.BurnHeight = ((AActor*)(type->Defaults))->height; @@ -313,14 +313,14 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) type->OwnedStates[i].Tics = 5; type->OwnedStates[i].TicRange = 0; type->OwnedStates[i].Misc1 = 0; - type->OwnedStates[i].SetAction(FindGlobalActionFunction("A_FreezeDeath")->Function); + type->OwnedStates[i].SetAction(FindGlobalActionFunction("A_FreezeDeath")->Variants[0].Implementation); i = type->NumOwnedStates - 1; type->OwnedStates[i].NextState = &type->OwnedStates[i]; type->OwnedStates[i].Tics = 1; type->OwnedStates[i].TicRange = 0; type->OwnedStates[i].Misc1 = 0; - type->OwnedStates[i].SetAction(FindGlobalActionFunction("A_FreezeDeathChunks")->Function); + type->OwnedStates[i].SetAction(FindGlobalActionFunction("A_FreezeDeathChunks")->Variants[0].Implementation); bag.statedef.SetStateLabel("Ice", &type->OwnedStates[extra.IceDeathStart]); } else if (extra.bGenericIceDeath) diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 1b253ec89..6a1f4fb00 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -188,7 +188,7 @@ AFuncDesc *FindFunction(const char * string); void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag); -PSymbolActionFunction *FindGlobalActionFunction(const char *name); +PFunction *FindGlobalActionFunction(const char *name); //========================================================================== // @@ -201,7 +201,7 @@ void SetReplacement(FScanner &sc, PClassActor *info, FName replaceName); void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *part2, int mod); void FinishActor(const FScriptPosition &sc, PClassActor *info, Baggage &bag); -FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, char type, bool constant); +FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool constant); enum diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index c45774d66..bf99ee363 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -553,9 +553,9 @@ FVariableInfo *FindVariable(const char * string, const PClass *cls) // //========================================================================== -PSymbolActionFunction *FindGlobalActionFunction(const char *name) +PFunction *FindGlobalActionFunction(const char *name) { - return dyn_cast(RUNTIME_CLASS(AActor)->Symbols.FindSymbol(name, false)); + return dyn_cast(RUNTIME_CLASS(AActor)->Symbols.FindSymbol(name, false)); } diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 59c1bcf08..17cb90135 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -67,35 +67,46 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def); // //========================================================================== -FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, char type, bool constant) +FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool constant) { FxExpression *x = NULL; int v; - switch(type) + if (type == TypeSound) { - case 'S': - case 's': // Sound name sc.MustGetString(); x = new FxConstant(FSoundID(sc.String), sc); - break; - - case 'M': - case 'm': // Actor name + } + else if (type == TypeSInt32 || type == TypeFloat64) + { + x = ParseExpression (sc, cls); + if (constant && !x->isConstant()) + { + sc.ScriptMessage("Default parameter must be constant."); + FScriptPosition::ErrorCounter++; + } + // Do automatic coercion between ints and floats. + if (type == TypeSInt32) + { + if (x->ValueType != VAL_Int) + { + x = new FxIntCast(x); + } + } + else + { + if (x->ValueType != VAL_Float) + { + x = new FxFloatCast(x); + } + } + } + else if (type == TypeName || type == TypeString) + { sc.SetEscape(true); sc.MustGetString(); sc.SetEscape(false); - x = new FxClassTypeCast(RUNTIME_CLASS(AActor), new FxConstant(FName(sc.String), sc)); - break; - - case 'N': - case 'n': // name - case 'T': - case 't': // String - sc.SetEscape(true); - sc.MustGetString(); - sc.SetEscape(false); - if (type == 'n' || type == 'N') + if (type == TypeName) { x = new FxConstant(sc.String[0] ? FName(sc.String) : NAME_None, sc); } @@ -103,10 +114,9 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, char type, bool con { x = new FxConstant(strbin1(sc.String), sc); } - break; - - case 'C': - case 'c': // Color + } + else if (type == TypeColor) + { sc.MustGetString (); if (sc.Compare("none")) { @@ -122,16 +132,12 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, char type, bool con // 0 needs to be the default so we have to mark the color. v = MAKEARGB(1, RPART(c), GPART(c), BPART(c)); } - { - ExpVal val; - val.Type = VAL_Color; - val.Int = v; - x = new FxConstant(val, sc); - break; - } - - case 'L': - case 'l': + ExpVal val; + val.Type = VAL_Color; + val.Int = v; + x = new FxConstant(val, sc); + } + else if (type == TypeState) { // This forces quotation marks around the state name. sc.MustGetToken(TK_StringConst); @@ -151,39 +157,18 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, char type, bool con { x = new FxMultiNameState(sc.String, sc); } - break; } - - case 'X': - case 'x': - case 'Y': - case 'y': - x = ParseExpression (sc, cls); - if (constant && !x->isConstant()) - { - sc.ScriptMessage("Default parameter must be constant."); - FScriptPosition::ErrorCounter++; - } - // Do automatic coercion between ints and floats. - if (type == 'x' || type == 'X') - { - if (x->ValueType != VAL_Int) - { - x = new FxIntCast(x); - } - } - else - { - if (x->ValueType != VAL_Float) - { - x = new FxFloatCast(x); - } - } - break; - - default: - assert(false); - return NULL; + else if (type->GetClass() == RUNTIME_CLASS(PClassPointer)) + { // Actor name + sc.SetEscape(true); + sc.MustGetString(); + sc.SetEscape(false); + x = new FxClassTypeCast(static_cast(type)->ClassRestriction, new FxConstant(FName(sc.String), sc)); + } + else + { + assert(false && "Unknown parameter type"); + x = NULL; } return x; } @@ -917,15 +902,12 @@ static void ParseActorProperty(FScanner &sc, Baggage &bag) static void ParseActionDef (FScanner &sc, PClassActor *cls) { - enum - { - OPTIONAL = 1 - }; - bool error = false; const AFuncDesc *afd; FName funcname; - FString args; + TArray rets; + TArray args; + TArray argflags; if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0) { @@ -934,6 +916,11 @@ 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)) + { + rets.Push(TypeSInt32); + } sc.MustGetToken(TK_Identifier); funcname = sc.String; afd = FindFunction(sc.String); @@ -942,13 +929,18 @@ static void ParseActionDef (FScanner &sc, PClassActor *cls) sc.ScriptMessage ("The function '%s' has not been exported from the executable.", sc.String); error++; } + args.Push(NewClassPointer(cls)), argflags.Push(0); // implied self pointer + args.Push(NewClassPointer(RUNTIME_CLASS(AActor))), argflags.Push(0); // implied stateowner pointer + args.Push(TypeState), argflags.Push(0); // implied callingstate pointer + sc.MustGetToken('('); if (!sc.CheckToken(')')) { while (sc.TokenType != ')') { int flags = 0; - char type = '@'; + PType *type = NULL; + PClass *restrict = NULL; // Retrieve flags before type name for (;;) @@ -967,32 +959,42 @@ static void ParseActionDef (FScanner &sc, PClassActor *cls) { case TK_Bool: case TK_Int: - type = 'x'; + type = TypeSInt32; break; case TK_Float: - type = 'y'; + type = TypeFloat64; break; - case TK_Sound: type = 's'; break; - case TK_String: type = 't'; break; - case TK_Name: type = 'n'; break; - case TK_State: type = 'l'; break; - case TK_Color: type = 'c'; break; + case TK_Sound: type = TypeSound; break; + case TK_String: type = TypeString; break; + case TK_Name: type = TypeName; break; + case TK_State: type = TypeState; break; + case TK_Color: type = TypeColor; break; case TK_Class: sc.MustGetToken('<'); - sc.MustGetToken(TK_Identifier); // Skip class name, since the parser doesn't care + sc.MustGetToken(TK_Identifier); // Get class name + restrict = PClass::FindClass(sc.String); + if (restrict == NULL) + { + sc.ScriptMessage("Unknown class type %s", sc.String); + FScriptPosition::ErrorCounter++; + } + else + { + type = NewClassPointer(restrict); + } sc.MustGetToken('>'); - type = 'm'; break; case TK_Ellipsis: - type = '+'; + // Making the final type NULL signals a varargs function. + type = NULL; sc.MustGetToken(')'); sc.UnGet(); break; default: sc.ScriptMessage ("Unknown variable type %s", sc.TokenName(sc.TokenType, sc.String).GetChars()); - type = 'x'; + type = TypeSInt32; FScriptPosition::ErrorCounter++; break; } @@ -1008,16 +1010,13 @@ static void ParseActionDef (FScanner &sc, PClassActor *cls) if (sc.CheckToken('=')) { - flags |= OPTIONAL; + flags |= VARF_Optional; FxExpression *def = ParseParameter(sc, cls, type, true); delete def; } - if (!(flags & OPTIONAL) && type != '+') - { - type -= 'a' - 'A'; - } - args += type; + args.Push(type); + argflags.Push(flags); sc.MustGetAnyToken(); if (sc.TokenType != ',' && sc.TokenType != ')') { @@ -1028,14 +1027,14 @@ static void ParseActionDef (FScanner &sc, PClassActor *cls) sc.MustGetToken(';'); if (afd != NULL) { - PSymbolActionFunction *sym = new PSymbolActionFunction(funcname); - sym->Arguments = args; - sym->Function = *(afd->VMPointer); + PFunction *sym = new PFunction(funcname); + sym->AddVariant(NewPrototype(rets, args), argflags, *(afd->VMPointer)); + sym->Flags = VARF_Method | VARF_Action; if (error) { FScriptPosition::ErrorCounter++; } - else if (cls->Symbols.AddSymbol (sym) == NULL) + else if (cls->Symbols.AddSymbol(sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in class '%s'.", diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index fb3ecf673..dafe4d76c 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -100,7 +100,7 @@ bool DoActionSpecials(FScanner &sc, FState & state, Baggage &bag, FStateTempCall sc.ScriptError ("Too many arguments to %s", specname.GetChars()); } - tcall->Function = FindGlobalActionFunction("A_CallSpecial")->Function; + tcall->Function = FindGlobalActionFunction("A_CallSpecial")->Variants[0].Implementation; return true; } return false; @@ -312,18 +312,29 @@ do_stop: goto endofstate; } - PSymbolActionFunction *afd = dyn_cast(bag.Info->Symbols.FindSymbol (FName(sc.String, true), true)); + PFunction *afd = dyn_cast(bag.Info->Symbols.FindSymbol(FName(sc.String, true), true)); if (afd != NULL) { - tcall->Function = afd->Function; - if (!afd->Arguments.IsEmpty()) + 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) { - const char *params = afd->Arguments.GetChars(); - int numparams = (int)afd->Arguments.Len(); - int v; - if (!islower(*params)) + if (!(paramflags[pnum] & VARF_Optional)) { sc.MustGetStringName("("); } @@ -335,13 +346,10 @@ do_stop: } } - bool varargs = params[numparams - 1] == '+'; - int varargcount = 0; - - while (*params) + while (numparams > 0) { FxExpression *x; - if ((*params == 'l' || *params == 'L') && sc.CheckNumber()) + if (params[pnum] == TypeState && sc.CheckNumber()) { // Special case: State label as an offset if (sc.Number > 0 && statestring.Len() > 1) @@ -349,7 +357,7 @@ do_stop: sc.ScriptError("You cannot use state jumps commands with a jump offset on multistate definitions\n"); } - v=sc.Number; + v = sc.Number; if (v<0) { sc.ScriptError("Negative jump offsets are not allowed"); @@ -367,22 +375,23 @@ do_stop: else { // Use the generic parameter parser for everything else - x = ParseParameter(sc, bag.Info, *params, false); + x = ParseParameter(sc, bag.Info, params[pnum], false); } tcall->Parameters.Push(new FxParameter(x)); - params++; - varargcount++; - if (*params) + pnum++; + numparams--; + if (numparams > 0) { - if (*params == '+') - { + if (params[pnum] == NULL) + { // varargs function if (sc.CheckString(")")) { goto endofstate; } - params--; + pnum--; + numparams++; } - else if ((islower(*params) || *params=='!') && sc.CheckString(")")) + else if ((paramflags[pnum] & VARF_Optional) && sc.CheckString(")")) { goto endofstate; }