Sat, 14 Jul 2012 15:44:38 +0300
Restructured the command def parser, added parameter lists to definition file.
commands.cxx | file | annotate | diff | comparison | revisions | |
commands.def | file | annotate | diff | comparison | revisions | |
commands.h | file | annotate | diff | comparison | revisions | |
scriptreader.cxx | file | annotate | diff | comparison | revisions | |
scriptreader.h | file | annotate | diff | comparison | revisions |
--- a/commands.cxx Sat Jul 14 04:17:06 2012 +0300 +++ b/commands.cxx Sat Jul 14 15:44:38 2012 +0300 @@ -41,6 +41,7 @@ #define __COMMANDS_CXX__ #include <stdlib.h> #include <stdio.h> +#include <string.h> #include "common.h" #include "scriptreader.h" #include "str.h" @@ -50,74 +51,88 @@ void ReadCommands () { ScriptReader* r = new ScriptReader ((char*)"commands.def"); + r->extdelimeters = true; g_CommDef = NULL; CommandDef* curdef = g_CommDef; - unsigned int numCommDefs = 0; + unsigned int numCommDefs = 0; - while (r->Next()) { + while (r->PeekNext().compare ("") != 0) { CommandDef* comm = new CommandDef; + comm->next = NULL; + + // Number + r->MustNumber (); + comm->number = r->token; + + r->MustNext (":"); + + // Name + r->MustNext (); + comm->name = r->token; + + r->MustNext (":"); - // Any more than 4 is a warning, any less is an error. - unsigned int c = r->token.count (const_cast<char*> (":")); - if (c < 4) - r->ParserError ("not enough parameters: got %d, expected 4", c); + // Return value + r->MustNext (); + comm->returnvalue = GetReturnTypeByString (r->token); + if (comm->returnvalue == -1) + r->ParserError ("bad return value type `%s`", r->token.chars()); + + r->MustNext (":"); + + // Num args + r->MustNumber (); + comm->numargs = r->token; - int n = 0; - str t = ""; - for (unsigned int u = 0; u < r->token.len(); u++) { - // If we're at the last character of the string, we need - // to both add the character to t and check it. Thus, - // we do the addition, exceptionally, here. - if (u == r->token.len() - 1 && r->token[u] != ':') - t += r->token[u]; + r->MustNext (":"); + + // Max args + r->MustNumber (); + comm->maxargs = r->token; + + if (comm->maxargs > MAX_MAXARGS) + r->ParserError ("maxargs (%d) greater than %d!", comm->maxargs, MAX_MAXARGS); + + // Argument types + int curarg = 0; + while (curarg < comm->maxargs) { + r->MustNext (":"); + r->MustNext (); - if (r->token[u] == ':' || u == r->token.len() - 1) { - int i = atoi (t.chars()); - switch (n) { - case 0: - // Number - comm->number = i; - break; - case 1: - // Name - comm->name = t; - break; - case 2: - // Return value - t.tolower(); - if (!t.compare ("int")) - comm->returnvalue = RETURNVAL_INT; - else if (!t.compare ("str")) - comm->returnvalue = RETURNVAL_STRING; - else if (!t.compare ("void")) - comm->returnvalue = RETURNVAL_VOID; - else if (!t.compare ("bool")) - comm->returnvalue = RETURNVAL_BOOLEAN; - else - r->ParserError ("bad return value type `%s`", t.chars()); - break; - case 3: - // Num args - comm->numargs = i; - break; - case 4: - // Max args - comm->maxargs = i; - break; - default: - r->ParserWarning ("too many parameters"); - break; + int type = GetReturnTypeByString (r->token); + if (type == -1) + r->ParserError ("bad argument %d type `%s`", curarg, r->token.chars()); + if (type == RETURNVAL_VOID) + r->ParserError ("void is not a valid argument type!"); + comm->argtypes[curarg] = type; + + r->MustNext ("("); + r->MustNext (); + + // - 1 because of terminating null character + if (r->token.len() > MAX_ARGNAMELEN - 1) + r->ParserWarning ("argument name is too long (%d, max is %d)", + r->token.len(), MAX_ARGNAMELEN-1); + + strncpy (comm->argnames[curarg], r->token.chars(), MAX_ARGNAMELEN); + comm->argnames[curarg][MAX_ARGNAMELEN-1] = 0; + + // If this is an optional parameter, we need the default value. + if (curarg >= comm->numargs) { + r->MustNext ("="); + switch (type) { + case RETURNVAL_INT: r->MustNumber(); break; + case RETURNVAL_STRING: r->token = r->MustGetString(); break; + case RETURNVAL_BOOLEAN: r->MustBool(); break; } - n++; - t = ""; - } else { - t += r->token[u]; + comm->defvals[curarg] = r->token; } + + r->MustNext (")"); + curarg++; } - comm->next = NULL; - if (!g_CommDef) g_CommDef = comm; @@ -142,6 +157,33 @@ printf ("%d command definitions read.\n", numCommDefs); } +// urgh long name +int GetReturnTypeByString (str t) { + // "float" is for now just int. + // TODO: find out how BotScript floats work + // (are they real floats or fixed? how are they + // stored?) and add proper floating point support. + // NOTE: Also, shouldn't use RETURNVAL for data types.. + t.tolower(); + return !t.compare ("int") ? RETURNVAL_INT : + !t.compare ("float") ? RETURNVAL_INT : + !t.compare ("str") ? RETURNVAL_STRING : + !t.compare ("void") ? RETURNVAL_VOID : + !t.compare ("bool") ? RETURNVAL_BOOLEAN : -1; +} + +// Inverse operation +str GetReturnTypeName (int r) { + switch (r) { + case RETURNVAL_INT: return "int"; break; + case RETURNVAL_STRING: return "str"; break; + case RETURNVAL_VOID: return "void"; break; + case RETURNVAL_BOOLEAN: return "bool"; break; + } + + return ""; +} + CommandDef* GetCommandByName (str a) { a.tolower (); CommandDef* c;
--- a/commands.def Sat Jul 14 04:17:06 2012 +0300 +++ b/commands.def Sat Jul 14 15:44:38 2012 +0300 @@ -1,38 +1,38 @@ -0:changestate:void:1:1 -1:delay:void:1:1 -2:rand:int:2:2 +0:changestate:void:1:1:str(statename) +1:delay:void:1:1:int(tics) +2:rand:int:2:2:int(a):int(b) 3:StringsAreEqual:bool:0:0 -4:LookForPowerups:int:2:2 -5:LookForWeapons:int:2:2 -6:LookForAmmo:int:2:2 -7:LookForBaseHealth:int:2:2 -8:LookForBaseArmor:int:2:2 -9:LookForSuperHealth:int:2:2 -10:LookForSuperArmor:int:2:2 -11:LookForPlayerEnemies:int:1:1 +4:LookForPowerups:int:2:2:int(start):bool(visibilitycheck) +5:LookForWeapons:int:2:2:int(start):bool(visibilitycheck) +6:LookForAmmo:int:2:2:int(start):bool(visibilitycheck) +7:LookForBaseHealth:int:2:2:int(start):bool(visibilitycheck) +8:LookForBaseArmor:int:2:2:int(start):bool(visibilitycheck) +9:LookForSuperHealth:int:2:2:int(start):bool(visibilitycheck) +10:LookForSuperArmor:int:2:2:int(start):bool(visibilitycheck) +11:LookForPlayerEnemies:int:1:1:int(start) 12:GetClosestPlayerEnemy:int:0:0 -13:MoveLeft:void:1:1 -14:MoveRight:void:1:1 -15:MoveForward:void:1:1 -16:MoveBackwards:void:1:1 +13:MoveLeft:void:1:1:float(speed) +14:MoveRight:void:1:1:float(speed) +15:MoveForward:void:1:1:float(speed) +16:MoveBackwards:void:1:1:float(speed) 17:StopMovement:void:0:0 18:StopForwardMovement:void:0:0 19:StopSidewaysMovement:void:0:0 -20:CheckTerrain:int:2:2 -21:PathToGoal:int:1:1 -22:PathToLastKnownEnemyPosition:int:1:1 -23:PathToLastHeardSound:int:1:1 -24:Roam:int:1:1 -25:GetPathingCostToItem:int:1:1 -26:GetDistanceToItem:int:1:1 -27:GetItemName:str:1:1 -28:IsItemVisible:bool:1:1 -29:SetGoal:void:1:1 +20:CheckTerrain:int:2:2:int(distance):int(angle) +21:PathToGoal:int:1:1:float(speed) +22:PathToLastKnownEnemyPosition:int:1:1:float(speed) +23:PathToLastHeardSound:int:1:1:float(speed) +24:Roam:int:1:1:float(speed) +25:GetPathingCostToItem:int:1:1:int(item) +26:GetDistanceToItem:int:1:1:int(item) +27:GetItemName:str:1:1:int(item) +28:IsItemVisible:bool:1:1:int(item) +29:SetGoal:void:1:1:int(item) 30:BeginAimingAtEnemy:void:0:0 31:StopAimingAtEnemy:void:0:0 -32:Turn:void:1:1 +32:Turn:void:1:1:int(turnangle) 33:GetCurrentAngle:int:0:0 -34:SetEnemy:void:1:1 +34:SetEnemy:void:1:1:int(player) 35:ClearEnemy:void:0:0 36:IsEnemyAlive:bool:0:0 37:IsEnemyVisible:bool:0:0 @@ -43,17 +43,17 @@ 42:BeginFiringWeapon:void:0:0 43:StopFiringWeapon:void:0:0 44:GetCurrentWeapon:str:0:0 -45:ChangeWeapon:void:0:0 -46:GetWeaponFromItem:str:1:1 -47:IsWeaponOwned:bool:1:1 -48:IsFavoriteWeapon:bool:0:0 -49:Say:void:0:0 -50:SayFromFile:void:0:0 -51:SayFromChatFile:void:0:0 +45:ChangeWeapon:void:1:1:str(weapon) +46:GetWeaponFromItem:str:1:1:int(item) +47:IsWeaponOwned:bool:1:1:int(item) +48:IsFavoriteWeapon:bool:1:1:str(weapon) +49:Say:void:1:1:str(message) +50:SayFromFile:void:2:2:str(filename):str(section) +51:SayFromChatFile:void:2:2:str(filename):str(section) 52:BeginChatting:void:0:0 53:StopChatting:void:0:0 -54:ChatSectionExists:bool:0:0 -55:ChatSectionExistsInFile:bool:0:0 +54:ChatSectionExists:bool:1:1:str(section) +55:ChatSectionExistsInFile:bool:2:2:str(filename):str(section) 56:GetLastChatString:str:0:0 57:GetLastChatPlayer:str:0:0 58:GetChatFrequency:int:0:0 @@ -76,18 +76,18 @@ 75:GetEvade:int:0:0 76:GetReactionTime:int:0:0 77:GetPerception:int:0:0 -78:SetSkillIncrease:void:1:1 +78:SetSkillIncrease:void:1:1:bool(increase) 79:IsSkillIncreased:bool:0:0 -80:SetSkillDecrease:void:1:1 +80:SetSkillDecrease:void:1:1:bool(decrease) 81:IsSkillDecreased:bool:0:0 82:GetGameMode:int:0:0 83:GetSpread:int:0:0 84:GetLastJoinedPlayer:str:0:0 -85:GetPlayerName:str:1:1 +85:GetPlayerName:str:1:1:int(player) 86:GetReceivedMedal:int:0:0 -87:ACS_Execute:void:5:5 +87:ACS_Execute:void:1:5:int(script):int(map=0):int(arg0=0):int(arg1=0):int(arg2=0) 88:GetFavoriteWeapon:str:0:0 -89:SayFromLump:void:0:0 -90:SayFromChatLump:void:0:0 -91:ChatSectionExistsInLump:bool:0:0 -92:ChatSectionExistsInChatLump:bool:0:0 \ No newline at end of file +89:SayFromLump:void:2:2:str(lump):str(section) +90:SayFromChatLump:void:1:1:str(section) +91:ChatSectionExistsInLump:bool:2:2:str(lump):str(section) +92:ChatSectionExistsInChatLump:bool:1:1:str(section) \ No newline at end of file
--- a/commands.h Sat Jul 14 04:17:06 2012 +0300 +++ b/commands.h Sat Jul 14 15:44:38 2012 +0300 @@ -41,6 +41,9 @@ #ifndef __COMMANDS_H__ #define __COMMANDS_H__ +#define MAX_MAXARGS 8 +#define MAX_ARGNAMELEN 16 + #include "str.h" #include "botcommands.h" @@ -52,11 +55,16 @@ int number; int numargs; int maxargs; - RETURNVAL_e returnvalue; + int returnvalue; + int argtypes[MAX_MAXARGS]; + char argnames[MAX_MAXARGS][MAX_ARGNAMELEN]; + int defvals[MAX_MAXARGS]; CommandDef* next; }; void ReadCommands (); CommandDef* GetCommandByName (str a); +int GetReturnTypeByString (str token); +str GetReturnTypeName (int r); #endif // __COMMANDS_H__ \ No newline at end of file
--- a/scriptreader.cxx Sat Jul 14 04:17:06 2012 +0300 +++ b/scriptreader.cxx Sat Jul 14 15:44:38 2012 +0300 @@ -63,6 +63,7 @@ } curline = 1; + curchar = 1; pos = 0; token = ""; } @@ -80,37 +81,37 @@ if (atnewline) { atnewline = false; curline++; + curchar = 0; // gets incremented to 1 } if (c[0] == '\n') atnewline = true; + curchar++; return c[0]; } // true if was found, false if not. bool ScriptReader::Next () { str tmp = ""; - bool quote = false; - tokenquoted = false; + bool dontreadnext = false; while (!feof (fp)) { - char c = ReadChar (); + c = ReadChar (); - // Extended delimeters: parenthesis, quote marks, braces and brackets. + // Extended delimeters: basically any non-alnum characters. // These delimeters break the word too. If there was prior data, // the delimeter pushes the cursor back so that the next character // will be the same delimeter. If there isn't, the delimeter itself // is included (and thus becomes a token itself.) bool shouldBreak = false; if (extdelimeters) { - switch (c) { - case '(': case ')': - case '{': case '}': - case '[': case ']': - case '"': - // Push the cursor back + if ((c >= 33 && c <= 47) || + (c >= 58 && c <= 64) || + // underscore isn't a delimeter + (c >= 91 && c <= 96 && c != 95) || + (c >= 123 && c <= 126)) { if (tmp.len()) - fseek (fp, ftell (fp)-1, SEEK_SET); + fseek (fp, ftell (fp) - 1, SEEK_SET); else tmp += c; shouldBreak = true; @@ -120,7 +121,7 @@ if (shouldBreak) break; - if (IsDelimeter (c) && !quote) { + if (IsDelimeter (c)) { // Don't break if we haven't gathered anything yet. if (tmp.len()) break; @@ -200,10 +201,11 @@ } void ScriptReader::ParserMessage (const char* header, char* message) { - fprintf (stderr, "%sIn file %s, on line %d: %s\n", - header, filepath.chars(), curline, message); + fprintf (stderr, "%sIn file %s, at line %u, col %u: %s\n", + header, filepath.chars(), curline, curchar, message); } +// I guess this should be a void function putting the return value into token? str ScriptReader::MustGetString () { if (!extdelimeters) ParserError ("MustGetString doesn't work with parsers not using extended delimeters!"); @@ -225,4 +227,21 @@ } return string; +} + +void ScriptReader::MustNumber () { + MustNext (); + if (!token.isnumber()) + ParserError ("expected a number, got `%s`", token.chars()); +} + +void ScriptReader::MustBool () { + MustNext(); + if (!token.compare ("0") || !token.compare ("1") || + !token.compare ("true") || !token.compare ("false") || + !token.compare ("yes") || !token.compare ("no")) { + return; + } + + ParserError ("expected a boolean value, got `%s`", token.chars()); } \ No newline at end of file
--- a/scriptreader.h Sat Jul 14 04:17:06 2012 +0300 +++ b/scriptreader.h Sat Jul 14 15:44:38 2012 +0300 @@ -59,8 +59,8 @@ str filepath; unsigned int pos; unsigned int curline; + unsigned int curchar; str token; - bool tokenquoted; bool atnewline; bool extdelimeters; @@ -74,13 +74,20 @@ str PeekNext (); void Seek (unsigned int n, int origin); void MustNext (const char* c = ""); + str MustGetString (); + void MustNumber (); + void MustBool (); + void ParserError (const char* message, ...); void ParserWarning (const char* message, ...); - void ParserMessage (const char* header, char* message); - str MustGetString (); // parser.cxx: void BeginParse (ObjWriter* w); + +private: + bool nextreintepret; + char c; + void ParserMessage (const char* header, char* message); }; #endif // __SCRIPTREADER_H__ \ No newline at end of file