Restructured the command def parser, added parameter lists to definition file.

Sat, 14 Jul 2012 15:44:38 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sat, 14 Jul 2012 15:44:38 +0300
changeset 10
2c0f76090372
parent 9
d279af9afd6d
child 11
f08abacb46c9

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

mercurial