//========================================================================== // // Processes a flag. Also used by olddecorations.cpp // //========================================================================== void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *part2, int mod) { FFlagDef *fd; if ( (fd = FindFlag (bag.Info, part1, part2)) ) { AActor *defaults = (AActor*)bag.Info->Defaults; if (fd->structoffset == -1) // this is a deprecated flag that has been changed into a real property { HandleDeprecatedFlags(defaults, bag.Info, mod=='+', fd->flagbit); } else { ModActorFlag(defaults, fd, mod == '+'); } } else { if (part2 == NULL) { sc.ScriptMessage("\"%s\" is an unknown flag\n", part1); } else { sc.ScriptMessage("\"%s.%s\" is an unknown flag\n", part1, part2); } FScriptPosition::ErrorCounter++; } }
void ParseFunctionDef(FScanner &sc, PClassActor *cls, FName funcname, TArray<PType *> &rets, DWORD funcflags) { assert(cls != NULL); const AFuncDesc *afd; TArray<PType *> args; TArray<DWORD> argflags; afd = FindFunction(funcname); if (afd == NULL) { sc.ScriptMessage ("The function '%s' has not been exported from the executable.", funcname.GetChars()); FScriptPosition::ErrorCounter++; } sc.MustGetToken('('); SetImplicitArgs(&args, &argflags, cls, funcflags); ParseArgListDef(sc, cls, args, argflags); if (afd != NULL) { PFunction *sym = new PFunction(funcname); sym->AddVariant(NewPrototype(rets, args), argflags, *(afd->VMPointer)); sym->Flags = funcflags; if (cls->Symbols.AddSymbol(sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in class '%s'.", funcname.GetChars(), cls->TypeName.GetChars()); FScriptPosition::ErrorCounter++; } } }
void SetReplacement(FScanner &sc, FActorInfo *info, FName replaceName) { // Check for "replaces" if (replaceName != NAME_None) { // Get actor name const PClass *replacee = PClass::FindClass (replaceName); if (replacee == NULL) { sc.ScriptMessage("Replaced type '%s' not found for %s", replaceName.GetChars(), info->Class->TypeName.GetChars()); return; } else if (replacee->ActorInfo == NULL) { sc.ScriptMessage("Replaced type '%s' for %s is not an actor", replaceName.GetChars(), info->Class->TypeName.GetChars()); return; } if (replacee != NULL) { replacee->ActorInfo->Replacement = info; info->Replacee = replacee->ActorInfo; } } }
static void ParseActorProperty(FScanner &sc, Baggage &bag) { static const char *statenames[] = { "Spawn", "See", "Melee", "Missile", "Pain", "Death", "XDeath", "Burn", "Ice", "Raise", "Crash", "Crush", "Wound", "Disintegrate", "Heal", NULL }; strlwr (sc.String); FString propname = sc.String; if (sc.CheckString (".")) { sc.MustGetString (); propname += '.'; strlwr (sc.String); propname += sc.String; } else { sc.UnGet (); } FPropertyInfo *prop = FindProperty(propname); if (prop != NULL) { if (bag.Info->Class->IsDescendantOf(prop->cls)) { ParsePropertyParams(sc, prop, (AActor *)bag.Info->Class->Defaults, bag); } else { sc.ScriptMessage("\"%s\" requires an actor of type \"%s\"\n", propname.GetChars(), prop->cls->TypeName.GetChars()); FScriptPosition::ErrorCounter++; } } else if (!propname.CompareNoCase("States")) { if (bag.StateSet) { sc.ScriptMessage("'%s' contains multiple state declarations", bag.Info->Class->TypeName.GetChars()); FScriptPosition::ErrorCounter++; } ParseStates(sc, bag.Info, (AActor *)bag.Info->Class->Defaults, bag); bag.StateSet=true; } else if (MatchString(propname, statenames) != -1) { bag.statedef.SetStateLabel(propname, CheckState (sc, bag.Info->Class)); } else { sc.ScriptError("\"%s\" is an unknown actor property\n", propname.GetChars()); } }
static void ParseActionDef (FScanner &sc, PClassActor *cls) { unsigned int error = 0; FName funcname; TArray<PType *> rets; if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0) { sc.ScriptMessage ("Action functions can only be imported by internal class and actor definitions!"); FScriptPosition::ErrorCounter++; } sc.MustGetToken(TK_Native); // check for a return value do { 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); }
static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls) { int currvalue = 0; sc.MustGetToken('{'); while (!sc.CheckToken('}')) { sc.MustGetToken(TK_Identifier); FName symname = sc.String; if (sc.CheckToken('=')) { FxExpression *expr = ParseExpression (sc, cls); currvalue = expr->EvalExpression(NULL).GetInt(); delete expr; } PSymbolConst *sym = new PSymbolConst(symname); sym->ValueType = VAL_Int; sym->Value = currvalue; if (symt->AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } // This allows a comma after the last value but doesn't enforce it. if (sc.CheckToken('}')) break; sc.MustGetToken(','); currvalue++; } sc.MustGetToken(';'); }
static void ParseConstant (FScanner &sc, PSymbolTable *symt, PClassActor *cls) { // Read the type and make sure it's int or float. if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Float)) { int type = sc.TokenType; sc.MustGetToken(TK_Identifier); FName symname = sc.String; sc.MustGetToken('='); FxExpression *expr = ParseExpression (sc, cls, true); sc.MustGetToken(';'); if (!expr->isConstant()) { sc.ScriptMessage("Constant definition is not a constant"); FScriptPosition::ErrorCounter++; } else { ExpVal val = static_cast<FxConstant *>(expr)->GetValue(); delete expr; PSymbolConstNumeric *sym; if (type == TK_Int) { sym = new PSymbolConstNumeric(symname, TypeSInt32); sym->Value = val.GetInt(); } else { sym = new PSymbolConstNumeric(symname, TypeFloat64); sym->Float = val.GetFloat(); } if (symt->AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } } } else { sc.ScriptMessage("Numeric type required for constant"); FScriptPosition::ErrorCounter++; } }
//========================================================================== // // Reads an actor definition // //========================================================================== static void ParseActor(FScanner &sc) { PClassActor *info = NULL; Baggage bag; info = ParseActorHeader(sc, &bag); sc.MustGetToken('{'); while (sc.MustGetAnyToken(), sc.TokenType != '}') { switch (sc.TokenType) { case TK_Action: ParseActionDef (sc, info); break; case TK_Const: ParseConstant (sc, &info->Symbols, info); break; case TK_Enum: ParseEnum (sc, &info->Symbols, info); break; case TK_Native: ParseNativeFunction (sc, info); break; case TK_Var: ParseUserVariable (sc, &info->Symbols, info); break; case TK_Identifier: ParseActorProperty(sc, bag); break; case TK_States: if (bag.StateSet) { sc.ScriptMessage("'%s' contains multiple state declarations", bag.Info->TypeName.GetChars()); FScriptPosition::ErrorCounter++; } ParseStates(sc, bag.Info, (AActor *)bag.Info->Defaults, bag); bag.StateSet = true; break; case '+': case '-': ParseActorFlag(sc, bag, sc.TokenType); break; default: sc.ScriptError("Unexpected '%s' in definition of '%s'", sc.String, bag.Info->TypeName.GetChars()); break; } } FinishActor(sc, info, bag); sc.SetCMode (false); }
static void ParseSpawnMap(FScanner &sc, SpawnMap & themap, const char *descript) { TMap<int, bool> defined; int error = 0; MapinfoSpawnItem editem; editem.filename = sc.ScriptName; while (true) { if (sc.CheckString("}")) return; else if (sc.CheckNumber()) { int ednum = sc.Number; sc.MustGetStringName("="); sc.MustGetString(); bool *def = defined.CheckKey(ednum); if (def != NULL) { sc.ScriptMessage("%s %d defined more than once", descript, ednum); error++; } else if (ednum < 0) { sc.ScriptMessage("%s must be positive, got %d", descript, ednum); error++; } defined[ednum] = true; editem.classname = sc.String; editem.linenum = sc.Line; themap.Insert(ednum, editem); } else { sc.ScriptError("Number expected"); } } if (error > 0) { sc.ScriptError("%d errors encountered in %s definition", error, descript); } }
static void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls) { // Read the type and make sure it's int or float. if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Float)) { int type = sc.TokenType; sc.MustGetToken(TK_Identifier); FName symname = sc.String; sc.MustGetToken('='); FxExpression *expr = ParseExpression (sc, cls); sc.MustGetToken(';'); ExpVal val = expr->EvalExpression(NULL); delete expr; PSymbolConst *sym = new PSymbolConst(symname); if (type == TK_Int) { sym->ValueType = VAL_Int; sym->Value = val.GetInt(); } else { sym->ValueType = VAL_Float; sym->Float = val.GetFloat(); } if (symt->AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } } else { sc.ScriptMessage("Numeric type required for constant"); FScriptPosition::ErrorCounter++; } }
static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClassActor *cls) { int currvalue = 0; sc.MustGetToken('{'); while (!sc.CheckToken('}')) { sc.MustGetToken(TK_Identifier); FName symname = sc.String; if (sc.CheckToken('=')) { FxExpression *expr = ParseExpression (sc, cls, true); if (!expr->isConstant()) { sc.ScriptMessage("'%s' must be constant", symname.GetChars()); FScriptPosition::ErrorCounter++; } else { currvalue = static_cast<FxConstant *>(expr)->GetValue().GetInt(); } delete expr; } PSymbolConstNumeric *sym = new PSymbolConstNumeric(symname, TypeSInt32); sym->Value = currvalue; if (symt->AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } // This allows a comma after the last value but doesn't enforce it. if (sc.CheckToken('}')) break; sc.MustGetToken(','); currvalue++; } sc.MustGetToken(';'); }
static void ParseNativeFunction(FScanner &sc, PClassActor *cls) { TArray<PType *> rets(1); if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0) { sc.ScriptMessage ("functions can only be declared by native actors!"); FScriptPosition::ErrorCounter++; } // Read the type and make sure it's int or float. sc.MustGetAnyToken(); switch (sc.TokenType) { case TK_Int: case TK_Bool: rets.Push(TypeSInt32); break; case TK_Float: rets.Push(TypeFloat64); break; case TK_Angle_t: rets.Push(TypeAngle); break; case TK_Fixed_t: rets.Push(TypeFixed); break; case TK_State: rets.Push(TypeState); break; case TK_Identifier: rets.Push(NewPointer(RUNTIME_CLASS(DObject))); // Todo: Object type sc.ScriptError("Object type variables not implemented yet!"); break; default: sc.ScriptError("Invalid return type %s", sc.String); return; } sc.MustGetToken(TK_Identifier); ParseFunctionDef(sc, cls, sc.String, rets, VARF_Method); }
static bool ReplaceMenu(FScanner &sc, FMenuDescriptor *desc) { FMenuDescriptor **pOld = MenuDescriptors.CheckKey(desc->mMenuName); if (pOld != NULL && *pOld != NULL) { if (CheckCompatible(desc, *pOld)) { delete *pOld; } else { sc.ScriptMessage("Tried to replace menu '%s' with a menu of different type", desc->mMenuName.GetChars()); return true; } } MenuDescriptors[desc->mMenuName] = desc; return false; }
void gl_ParseObject(FScanner &sc) { int type; FString name; // get name sc.GetString(); name = sc.String; if (!PClass::FindClass(name)) sc.ScriptMessage("Warning: dynamic lights attached to non-existent actor %s\n", name.GetChars()); // check for opening brace sc.GetString(); if (sc.Compare("{")) { ScriptDepth++; while (ScriptDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_FRAME: gl_ParseFrame(sc, name); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } } else { sc.ScriptError("Expected '{'.\n"); } }
void SetReplacement(FScanner &sc, PClassActor *info, FName replaceName) { // Check for "replaces" if (replaceName != NAME_None) { // Get actor name PClassActor *replacee = PClass::FindActor(replaceName); if (replacee == NULL) { sc.ScriptMessage("Replaced type '%s' not found for %s", replaceName.GetChars(), info->TypeName.GetChars()); return; } if (replacee != NULL) { replacee->Replacement = info; info->Replacee = replacee; } } }
bool FIntermissionActionTextscreen::ParseKey(FScanner &sc) { if (sc.Compare("Position")) { sc.MustGetToken('='); sc.MustGetToken(TK_IntConst); mTextX = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); mTextY = sc.Number; return true; } else if (sc.Compare("TextLump")) { sc.MustGetToken('='); sc.MustGetToken(TK_StringConst); int lump = Wads.CheckNumForFullName(sc.String, true); if (lump > 0) { mText = Wads.ReadLump(lump).GetString(); } else { // only print an error if coming from a PWAD if (Wads.GetLumpFile(sc.LumpNum) > 1) sc.ScriptMessage("Unknown text lump '%s'", sc.String); mText.Format("Unknown text lump '%s'", sc.String); } return true; } else if (sc.Compare("Text")) { sc.MustGetToken('='); do { sc.MustGetToken(TK_StringConst); mText << sc.String << '\n'; } while (sc.CheckToken(',')); return true; } else if (sc.Compare("TextColor")) { sc.MustGetToken('='); sc.MustGetToken(TK_StringConst); mTextColor = V_FindFontColor(sc.String); return true; } else if (sc.Compare("TextDelay")) { sc.MustGetToken('='); if (!sc.CheckToken('-')) { sc.MustGetFloat(); mTextDelay = int(sc.Float*TICRATE); } else { sc.MustGetToken(TK_IntConst); mTextDelay = sc.Number; } return true; } else if (sc.Compare("textspeed")) { sc.MustGetToken('='); sc.MustGetToken(TK_IntConst); mTextSpeed = sc.Number; return true; } else return Super::ParseKey(sc); }
static void DoParse(const char *filename) { if (TokenMap.CountUsed() == 0) { InitTokenMap(); } FScanner sc; void *parser; int tokentype; int lump; bool failed; ZCCToken value; lump = Wads.CheckNumForFullName(filename, true); if (lump >= 0) { sc.OpenLumpNum(lump); } else if (FileExists(filename)) { sc.OpenFile(filename); } else { Printf("Could not find script lump '%s'\n", filename); return; } parser = ZCCParseAlloc(malloc); failed = false; #ifdef _DEBUG FILE *f = fopen("trace.txt", "w"); char prompt = '\0'; ZCCParseTrace(f, &prompt); #endif ZCCParseState state(sc); while (sc.GetToken()) { value.SourceLoc = sc.GetMessageLine(); switch (sc.TokenType) { case TK_StringConst: value.String = state.Strings.Alloc(sc.String, sc.StringLen); tokentype = ZCC_STRCONST; break; case TK_NameConst: value.Int = sc.Name; tokentype = ZCC_NAMECONST; break; case TK_IntConst: value.Int = sc.Number; tokentype = ZCC_INTCONST; break; case TK_UIntConst: value.Int = sc.Number; tokentype = ZCC_UINTCONST; break; case TK_FloatConst: value.Float = sc.Float; tokentype = ZCC_FLOATCONST; break; case TK_Identifier: value.Int = FName(sc.String); tokentype = ZCC_IDENTIFIER; break; case TK_NonWhitespace: value.Int = FName(sc.String); tokentype = ZCC_NWS; break; default: TokenMapEntry *zcctoken = TokenMap.CheckKey(sc.TokenType); if (zcctoken != NULL) { tokentype = zcctoken->TokenType; value.Int = zcctoken->TokenName; } else { sc.ScriptMessage("Unexpected token %s.\n", sc.TokenName(sc.TokenType).GetChars()); goto parse_end; } break; } ZCCParse(parser, tokentype, value, &state); if (failed) { sc.ScriptMessage("Parse failed\n"); goto parse_end; } } parse_end: value.Int = -1; ZCCParse(parser, ZCC_EOF, value, &state); ZCCParse(parser, 0, value, &state); ZCCParseFree(parser, free); PSymbolTable symbols(&GlobalSymbols); ZCCCompiler cc(state, NULL, symbols); cc.Compile(); #ifdef _DEBUG if (f != NULL) { fclose(f); } FString ast = ZCC_PrintAST(state.TopNode); FString astfile = ExtractFileBase(filename, false); astfile << ".ast"; f = fopen(astfile, "w"); if (f != NULL) { fputs(ast.GetChars(), f); fclose(f); } #endif }
static void ParseNativeVariable (FScanner &sc, PSymbolTable * symt, PClass *cls) { FExpressionType valuetype; if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0) { sc.ScriptMessage ("variables can only be imported by internal class and actor definitions!"); FScriptPosition::ErrorCounter++; } // Read the type and make sure it's int or float. sc.MustGetAnyToken(); switch (sc.TokenType) { case TK_Int: valuetype = VAL_Int; break; case TK_Float: valuetype = VAL_Float; break; case TK_Angle_t: valuetype = VAL_Angle; break; case TK_Fixed_t: valuetype = VAL_Fixed; break; case TK_Bool: valuetype = VAL_Bool; break; case TK_Identifier: valuetype = VAL_Object; // Todo: Object type sc.ScriptError("Object type variables not implemented yet!"); break; default: sc.ScriptError("Invalid variable type %s", sc.String); return; } sc.MustGetToken(TK_Identifier); FName symname = sc.String; if (sc.CheckToken('[')) { FxExpression *expr = ParseExpression (sc, cls); int maxelems = expr->EvalExpression(NULL).GetInt(); delete expr; sc.MustGetToken(']'); valuetype.MakeArray(maxelems); } sc.MustGetToken(';'); const FVariableInfo *vi = FindVariable(symname, cls); if (vi == NULL) { sc.ScriptError("Unknown native variable '%s'", symname.GetChars()); } PSymbolVariable *sym = new PSymbolVariable(symname); sym->offset = vi->address; // todo sym->ValueType = valuetype; sym->bUserVar = false; if (symt->AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } }
static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClass *cls) { FExpressionType valuetype; // Only non-native classes may have user variables. if (!cls->bRuntimeClass) { sc.ScriptError("Native classes may not have user variables"); } // Read the type and make sure it's int. sc.MustGetAnyToken(); if (sc.TokenType != TK_Int) { sc.ScriptMessage("User variables must be of type int"); FScriptPosition::ErrorCounter++; } valuetype = VAL_Int; sc.MustGetToken(TK_Identifier); // For now, restrict user variables to those that begin with "user_" to guarantee // no clashes with internal member variable names. if (sc.StringLen < 6 || strnicmp("user_", sc.String, 5) != 0) { sc.ScriptMessage("User variable names must begin with \"user_\""); FScriptPosition::ErrorCounter++; } FName symname = sc.String; if (sc.CheckToken('[')) { FxExpression *expr = ParseExpression(sc, cls); int maxelems = expr->EvalExpression(NULL).GetInt(); delete expr; sc.MustGetToken(']'); if (maxelems <= 0) { sc.ScriptMessage("Array size must be positive"); FScriptPosition::ErrorCounter++; maxelems = 1; } valuetype.MakeArray(maxelems); } sc.MustGetToken(';'); // We must ensure that we do not define duplicates, even when they come from a parent table. if (symt->FindSymbol(symname, true) != NULL) { sc.ScriptMessage ("'%s' is already defined in '%s' or one of its ancestors.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; return; } PSymbolVariable *sym = new PSymbolVariable(symname); sym->offset = cls->Extend(sizeof(int) * (valuetype.Type == VAL_Array ? valuetype.size : 1)); sym->ValueType = valuetype; sym->bUserVar = true; if (symt->AddSymbol(sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } }
static void ParseActionDef (FScanner &sc, PClass *cls) { enum { OPTIONAL = 1 }; unsigned int error = 0; const AFuncDesc *afd; FName funcname; FString args; TArray<FxExpression *> DefaultParams; bool hasdefaults = false; if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0) { sc.ScriptMessage ("Action functions can only be imported by internal class and actor definitions!"); ++error; } sc.MustGetToken(TK_Native); sc.MustGetToken(TK_Identifier); funcname = sc.String; afd = FindFunction(sc.String); if (afd == NULL) { sc.ScriptMessage ("The function '%s' has not been exported from the executable.", sc.String); ++error; } sc.MustGetToken('('); if (!sc.CheckToken(')')) { while (sc.TokenType != ')') { int flags = 0; char type = '@'; // Retrieve flags before type name for (;;) { if (sc.CheckToken(TK_Coerce) || sc.CheckToken(TK_Native)) { } else { break; } } // Read the variable type sc.MustGetAnyToken(); switch (sc.TokenType) { case TK_Bool: case TK_Int: type = 'x'; break; case TK_Float: type = 'y'; break; case TK_Sound: type = 's'; break; case TK_String: type = 't'; break; case TK_Name: type = 't'; break; case TK_State: type = 'l'; break; case TK_Color: type = 'c'; break; case TK_Class: sc.MustGetToken('<'); sc.MustGetToken(TK_Identifier); // Skip class name, since the parser doesn't care sc.MustGetToken('>'); type = 'm'; break; case TK_Ellipsis: type = '+'; sc.MustGetToken(')'); sc.UnGet(); break; default: sc.ScriptMessage ("Unknown variable type %s", sc.TokenName(sc.TokenType, sc.String).GetChars()); type = 'x'; FScriptPosition::ErrorCounter++; break; } // Read the optional variable name if (!sc.CheckToken(',') && !sc.CheckToken(')')) { sc.MustGetToken(TK_Identifier); } else { sc.UnGet(); } FxExpression *def; if (sc.CheckToken('=')) { hasdefaults = true; flags |= OPTIONAL; def = ParseParameter(sc, cls, type, true); } else { def = NULL; } DefaultParams.Push(def); if (!(flags & OPTIONAL) && type != '+') { type -= 'a' - 'A'; } args += type; sc.MustGetAnyToken(); if (sc.TokenType != ',' && sc.TokenType != ')') { sc.ScriptError ("Expected ',' or ')' but got %s instead", sc.TokenName(sc.TokenType, sc.String).GetChars()); } } } sc.MustGetToken(';'); if (afd != NULL) { PSymbolActionFunction *sym = new PSymbolActionFunction(funcname); sym->Arguments = args; sym->Function = afd->Function; if (hasdefaults) { sym->defaultparameterindex = StateParams.Size(); for(unsigned int i = 0; i < DefaultParams.Size(); i++) { StateParams.Add(DefaultParams[i], cls, true); } } else { sym->defaultparameterindex = -1; } if (error) { FScriptPosition::ErrorCounter += error; } else if (cls->Symbols.AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in class '%s'.", funcname.GetChars(), cls->TypeName.GetChars()); FScriptPosition::ErrorCounter++; } } }
static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cls) { PType *type; int maxelems = 1; // Only non-native classes may have user variables. if (!cls->bRuntimeClass) { sc.ScriptError("Native classes may not have user variables"); } // Read the type and make sure it's acceptable. sc.MustGetAnyToken(); if (sc.TokenType != TK_Int && sc.TokenType != TK_Float) { sc.ScriptMessage("User variables must be of type 'int' or 'float'"); FScriptPosition::ErrorCounter++; } type = sc.TokenType == TK_Int ? (PType *)TypeSInt32 : (PType *)TypeFloat64; sc.MustGetToken(TK_Identifier); // For now, restrict user variables to those that begin with "user_" to guarantee // no clashes with internal member variable names. if (sc.StringLen < 6 || strnicmp("user_", sc.String, 5) != 0) { sc.ScriptMessage("User variable names must begin with \"user_\""); FScriptPosition::ErrorCounter++; } FName symname = sc.String; // We must ensure that we do not define duplicates, even when they come from a parent table. if (symt->FindSymbol(symname, true) != NULL) { sc.ScriptMessage ("'%s' is already defined in '%s' or one of its ancestors.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; return; } if (sc.CheckToken('[')) { FxExpression *expr = ParseExpression(sc, cls, true); if (!expr->isConstant()) { sc.ScriptMessage("Array size must be a constant"); FScriptPosition::ErrorCounter++; maxelems = 1; } else { maxelems = static_cast<FxConstant *>(expr)->GetValue().GetInt(); } sc.MustGetToken(']'); if (maxelems <= 0) { sc.ScriptMessage("Array size must be positive"); FScriptPosition::ErrorCounter++; maxelems = 1; } type = NewArray(type, maxelems); } sc.MustGetToken(';'); PField *sym = cls->AddField(symname, type, 0); if (sym == NULL) { sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } }
//========================================================================== //*** // ParseStates // parses a state block // //========================================================================== void ParseStates(FScanner &sc, FActorInfo * actor, AActor * defaults, Baggage &bag) { FString statestring; FState state; char lastsprite[5]=""; sc.MustGetStringName ("{"); sc.SetEscape(false); // disable escape sequences in the state parser while (!sc.CheckString ("}") && !sc.End) { memset(&state,0,sizeof(state)); statestring = ParseStateString(sc); if (!statestring.CompareNoCase("GOTO")) { do_goto: statestring = ParseStateString(sc); if (sc.CheckString ("+")) { sc.MustGetNumber (); statestring += '+'; statestring += sc.String; } if (!bag.statedef.SetGotoLabel(statestring)) { sc.ScriptError("GOTO before first state"); } } else if (!statestring.CompareNoCase("STOP")) { do_stop: if (!bag.statedef.SetStop()) { sc.ScriptError("STOP before first state"); continue; } } else if (!statestring.CompareNoCase("WAIT") || !statestring.CompareNoCase("FAIL")) { if (!bag.statedef.SetWait()) { sc.ScriptError("%s before first state", sc.String); continue; } } else if (!statestring.CompareNoCase("LOOP")) { if (!bag.statedef.SetLoop()) { sc.ScriptError("LOOP before first state"); continue; } } else { sc.MustGetString(); if (sc.Compare (":")) { do { bag.statedef.AddStateLabel(statestring); statestring = ParseStateString(sc); if (!statestring.CompareNoCase("GOTO")) { goto do_goto; } else if (!statestring.CompareNoCase("STOP")) { goto do_stop; } sc.MustGetString (); } while (sc.Compare (":")); // continue; } sc.UnGet (); if (statestring.Len() != 4) { sc.ScriptError ("Sprite names must be exactly 4 characters\n"); } state.sprite = GetSpriteIndex(statestring); state.Misc1 = state.Misc2 = 0; state.ParameterIndex = 0; sc.MustGetString(); statestring = sc.String; if (sc.CheckString("RANDOM")) { int min, max; sc.MustGetStringName("("); sc.MustGetNumber(); min = clamp<int>(sc.Number, -1, SHRT_MAX); sc.MustGetStringName(","); sc.MustGetNumber(); max = clamp<int>(sc.Number, -1, SHRT_MAX); sc.MustGetStringName(")"); if (min > max) { swapvalues(min, max); } state.Tics = min; state.TicRange = max - min; } else { sc.MustGetNumber(); state.Tics = clamp<int>(sc.Number, -1, SHRT_MAX); state.TicRange = 0; } while (sc.GetString() && !sc.Crossed) { if (sc.Compare("BRIGHT")) { state.Fullbright = true; continue; } if (sc.Compare("FAST")) { state.Fast = true; continue; } if (sc.Compare("SLOW")) { state.Slow = true; continue; } if (sc.Compare("NODELAY")) { if (bag.statedef.GetStateLabelIndex(NAME_Spawn) == bag.statedef.GetStateCount()) { state.NoDelay = true; } else { sc.ScriptMessage("NODELAY may only be used immediately after Spawn:"); } continue; } if (sc.Compare("OFFSET")) { // specify a weapon offset sc.MustGetStringName("("); sc.MustGetNumber(); state.Misc1 = sc.Number; sc.MustGetStringName (","); sc.MustGetNumber(); state.Misc2 = sc.Number; sc.MustGetStringName(")"); continue; } if (sc.Compare("LIGHT")) { sc.MustGetStringName("("); do { sc.MustGetString(); #ifdef DYNLIGHT AddStateLight(&state, sc.String); #endif } while (sc.CheckString(",")); sc.MustGetStringName(")"); continue; } if (sc.Compare("CANRAISE")) { state.CanRaise = true; continue; } // Make the action name lowercase strlwr (sc.String); if (DoActionSpecials(sc, state, bag)) { goto endofstate; } FName funcname = FName(sc.String, true); PSymbol *sym = bag.Info->Class->Symbols.FindSymbol (funcname, true); if (sym != NULL && sym->SymbolType == SYM_ActionFunction) { PSymbolActionFunction *afd = static_cast<PSymbolActionFunction *>(sym); state.SetAction(afd, false); if (!afd->Arguments.IsEmpty()) { const char *params = afd->Arguments.GetChars(); int numparams = (int)afd->Arguments.Len(); int v; if (!islower(*params)) { sc.MustGetStringName("("); } else { if (!sc.CheckString("(")) { state.ParameterIndex = afd->defaultparameterindex+1; goto endofstate; } } int paramindex = PrepareStateParameters(&state, numparams, bag.Info->Class); int paramstart = paramindex; bool varargs = params[numparams - 1] == '+'; int varargcount = 0; if (varargs) { paramindex++; } else if (afd->defaultparameterindex > -1) { StateParams.Copy(paramindex, afd->defaultparameterindex, int(afd->Arguments.Len())); } while (*params) { FxExpression *x; if ((*params == 'l' || *params == 'L') && 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->Class, *params, false); } StateParams.Set(paramindex++, x); params++; if (varargs) { varargcount++; } if (*params) { if (*params == '+') { if (sc.CheckString(")")) { StateParams.Set(paramstart, new FxConstant(varargcount, sc)); goto endofstate; } params--; StateParams.Reserve(1, bag.Info->Class); } else if ((islower(*params) || *params=='!') && sc.CheckString(")")) { goto endofstate; } sc.MustGetStringName (","); } } sc.MustGetStringName(")"); } else { sc.MustGetString(); if (sc.Compare("(")) { sc.ScriptError("You cannot pass parameters to '%s'\n", funcname.GetChars()); } sc.UnGet(); } goto endofstate; } sc.ScriptError("Invalid state parameter %s\n", sc.String); } sc.UnGet(); endofstate: if (!bag.statedef.AddStates(&state, statestring)) { sc.ScriptError ("Invalid frame character string '%s'", statestring.GetChars()); } } } sc.SetEscape(true); // re-enable escape sequences }
static void ParseArgListDef(FScanner &sc, PClassActor *cls, TArray<PType *> &args, TArray<DWORD> &argflags) { if (!sc.CheckToken(')')) { while (sc.TokenType != ')') { int flags = 0; PType *type = NULL; PClass *restrict = NULL; // Retrieve flags before type name for (;;) { if (sc.CheckToken(TK_Coerce) || sc.CheckToken(TK_Native)) { } else { break; } } // Read the variable type sc.MustGetAnyToken(); switch (sc.TokenType) { case TK_Bool: type = TypeBool; break; case TK_Int: type = TypeSInt32; break; case TK_Float: type = TypeFloat64; 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); // 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('>'); break; case TK_Ellipsis: // 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 = TypeSInt32; FScriptPosition::ErrorCounter++; break; } // Read the optional variable name if (!sc.CheckToken(',') && !sc.CheckToken(')')) { sc.MustGetToken(TK_Identifier); } else { sc.UnGet(); } if (sc.CheckToken('=')) { flags |= VARF_Optional; FxExpression *def = ParseParameter(sc, cls, type, true); delete def; } args.Push(type); argflags.Push(flags); sc.MustGetAnyToken(); if (sc.TokenType != ',' && sc.TokenType != ')') { sc.ScriptError ("Expected ',' or ')' but got %s instead", sc.TokenName(sc.TokenType, sc.String).GetChars()); } } } sc.MustGetToken(';'); }