Parser can now read expressions 100% properly and can perform variable assignment. I'd call this a milestone!

Sun, 29 Jul 2012 04:02:07 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sun, 29 Jul 2012 04:02:07 +0300
changeset 36
a8838b5f1213
parent 35
3d3f6ed40171
child 37
c349dca807f9

Parser can now read expressions 100% properly and can perform variable assignment. I'd call this a milestone!

commands.cxx file | annotate | diff | comparison | revisions
common.h file | annotate | diff | comparison | revisions
databuffer.h file | annotate | diff | comparison | revisions
events.cxx file | annotate | diff | comparison | revisions
main.cxx file | annotate | diff | comparison | revisions
objwriter.h file | annotate | diff | comparison | revisions
parser.cxx file | annotate | diff | comparison | revisions
preprocessor.cxx file | annotate | diff | comparison | revisions
scriptreader.cxx file | annotate | diff | comparison | revisions
scriptreader.h file | annotate | diff | comparison | revisions
stringtable.cxx file | annotate | diff | comparison | revisions
variables.cxx file | annotate | diff | comparison | revisions
--- a/commands.cxx	Sat Jul 28 17:57:37 2012 +0300
+++ b/commands.cxx	Sun Jul 29 04:02:07 2012 +0300
@@ -47,6 +47,8 @@
 #include "str.h"
 #include "commands.h"
 
+// ============================================================================
+// Reads command definitions from commands.def and stores them to memory.
 void ReadCommands () {
 	ScriptReader* r = new ScriptReader ("commands.def");
 	g_CommDef = NULL;
@@ -66,6 +68,8 @@
 		// Name
 		r->MustNext ();
 		comm->name = r->token;
+		if (IsKeyword (comm->name))
+			r->ParserError ("command name `%s` conflicts with keyword", comm->name.chars());
 		
 		r->MustNext (":");
 		
@@ -142,20 +146,21 @@
 		numCommDefs++;
 	}
 	
+	if (!numCommDefs)
+		r->ParserError ("no commands defined!\n");
+	
 	r->CloseFile ();
 	delete r;
-	
-	if (!numCommDefs)
-		error ("no commands defined!\n");
 	printf ("%d command definitions read.\n", numCommDefs);
 }
 
+// ============================================================================
+// Get command type by name
 int GetCommandType (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..
+	// stored?) and add proper floating point number support.
 	t = t.tolower();
 	return	!t.compare ("int") ? TYPE_INT :
 		!t.compare ("float") ? TYPE_INT :
@@ -164,7 +169,8 @@
 		!t.compare ("bool") ? TYPE_INT : -1;
 }
 
-// Inverse operation
+// ============================================================================
+// Inverse operation - type name by value
 str GetReturnTypeName (int r) {
 	switch (r) {
 	case RETURNVAL_INT: return "int"; break;
@@ -176,6 +182,8 @@
 	return "";
 }
 
+// ============================================================================
+// Finds a command by name
 CommandDef* FindCommand (str fname) {
 	CommandDef* comm;
 	ITERATE_COMMANDS (comm) {
@@ -186,6 +194,8 @@
 	return NULL;
 }
 
+// ============================================================================
+// Returns the prototype of the command
 str GetCommandPrototype (CommandDef* comm) {
 	str text;
 	text += GetReturnTypeName (comm->returnvalue);
--- a/common.h	Sat Jul 28 17:57:37 2012 +0300
+++ b/common.h	Sun Jul 29 04:02:07 2012 +0300
@@ -117,6 +117,13 @@
 }
 
 // Byte datatype
-typedef unsigned long int byte;
+typedef unsigned long int word;
+
+// Keywords
+#define NUM_KEYWORDS 20
+#ifndef __MAIN_CXX__
+extern const char** g_Keywords;
+#endif
+bool IsKeyword (str s);
 
 #endif // __COMMON_H__
\ No newline at end of file
--- a/databuffer.h	Sat Jul 28 17:57:37 2012 +0300
+++ b/databuffer.h	Sun Jul 29 04:02:07 2012 +0300
@@ -106,6 +106,9 @@
 	
 	// Merge another data buffer into this one.
 	void Merge (DataBuffer* other) {
+		if (!other)
+				return;
+		
 		for (unsigned int x = 0; x < other->writesize; x++) {
 			unsigned char c = *(other->buffer+x);
 			Write<unsigned char> (c);
--- a/events.cxx	Sat Jul 28 17:57:37 2012 +0300
+++ b/events.cxx	Sun Jul 29 04:02:07 2012 +0300
@@ -47,6 +47,9 @@
 #include "events.h"
 
 EventDef* g_EventDef;
+
+// ============================================================================
+// Read event definitions from file
 void ReadEvents () {
 	ScriptReader* r = new ScriptReader ("events.def");
 	g_EventDef = NULL;
@@ -75,12 +78,16 @@
 	printf ("%d event definitions read.\n", numEventDefs);
 }
 
+// ============================================================================
+// Delete event definitions recursively
 void UnlinkEvents (EventDef* e) {
 	if (e->next)
 		UnlinkEvents (e->next);
 	delete e;
 }
 
+// ============================================================================
+// Finds an event definition by index
 EventDef* FindEventByIdx (unsigned int idx) {
 	EventDef* e = g_EventDef;
 	while (idx > 0) {
@@ -92,6 +99,8 @@
 	return e;
 }
 
+// ============================================================================
+// Finds an event definition by name
 EventDef* FindEventByName (str a) {
 	EventDef* e;
 	for (e = g_EventDef; e->next != NULL; e = e->next) {
--- a/main.cxx	Sat Jul 28 17:57:37 2012 +0300
+++ b/main.cxx	Sun Jul 29 04:02:07 2012 +0300
@@ -38,6 +38,8 @@
  *	POSSIBILITY OF SUCH DAMAGE.
  */
 
+#define __MAIN_CXX__
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -54,13 +56,31 @@
 #include "bots.h"
 #include "botcommands.h"
 
-bool fexists (char* path) {
-	if (FILE* test = fopen (path, "r")) {
-		fclose (test);
-		return true;
-	}
-	return false;
-}
+const char* g_Keywords[NUM_KEYWORDS] = {
+	"state",
+	"event",
+	"mainloop",
+	"onenter",
+	"onexit",
+	"var",
+	
+	// These ones aren't implemented yet but I plan to do so, thus they are
+	// reserved. Also serves as a to-do list of sorts for me. >:F
+	"break",
+	"case",
+	"continue",
+	"default",
+	"do",
+	"else",
+	"enum", // Would enum actually be useful? I think so.
+	"for",
+	"func", // Would function support need external support from zandronum?
+	"goto", // Labels and goto must be done before if or any loops...
+	"if",
+	"return",
+	"switch",
+	"while"
+};
 
 int main (int argc, char** argv) {
 	// Intepret command-line parameters:
@@ -88,10 +108,17 @@
 	printf ("%s\n%s\n", header.chars(), headerline.chars());
 	
 	if (argc < 2) {
-		fprintf (stderr, "usage: %s: <infile> [outfile]\n", argv[0]);
+		fprintf (stderr, "usage: %s <infile> [outfile]\n", argv[0]);
+		fprintf (stderr, "       %s -l                 # lists commands\n", argv[0]);
 		exit (1);
 	}
 	
+	// A word should always be exactly 4 bytes. The above list command
+	// doesn't need it, but the rest of the program does.
+	if (sizeof (word) != 4)
+		error ("%s expects a word (unsigned long int) to be 4 bytes in size, is %d\n",
+			APPNAME, sizeof (word));
+	
 	str outfile;
 	if (argc < 3)
 		outfile = ObjectFileName (argv[1]);
@@ -142,7 +169,7 @@
 	
 	// Parse done, print statistics and write to file
 	unsigned int globalcount = CountGlobalVars ();
-	printf ("%u global variable%s\n", globalcount, PLURAL (globalcount));
+	printf ("%u/%u global variable%s\n", globalcount, MAX_SCRIPT_VARIABLES, PLURAL (globalcount));
 	printf ("%d state%s written\n", g_NumStates, PLURAL (g_NumStates));
 	printf ("%d event%s written\n", g_NumEvents, PLURAL (g_NumEvents));
 	w->WriteToFile ();
@@ -155,12 +182,29 @@
 	printf ("done!\n");
 }
 
+// ============================================================================
+// Utility functions
+
+// ============================================================================
+// Does the given file exist?
+bool fexists (char* path) {
+	if (FILE* test = fopen (path, "r")) {
+		fclose (test);
+		return true;
+	}
+	return false;
+}
+
+// ============================================================================
+// Generic error
 void error (const char* text, ...) {
 	PERFORM_FORMAT (text, c);
 	fprintf (stderr, "error: %s", c);
 	exit (1);
 }
 
+// ============================================================================
+// Mutates given filename to an object filename
 char* ObjectFileName (str s) {
 	// Locate the extension and chop it out
 	unsigned int extdot = s.last (".");
@@ -169,4 +213,13 @@
 	
 	s += ".o";
 	return s.chars();
+}
+
+// ============================================================================
+// Is the given argument a reserved keyword?
+bool IsKeyword (str s) {
+	for (unsigned int u = 0; u < NUM_KEYWORDS; u++)
+		if (!s.icompare (g_Keywords[u]))
+			return true;
+	return false;
 }
\ No newline at end of file
--- a/objwriter.h	Sat Jul 28 17:57:37 2012 +0300
+++ b/objwriter.h	Sun Jul 29 04:02:07 2012 +0300
@@ -82,7 +82,7 @@
 	}
 	
 	// Cannot use default arguments in function templates..
-	void Write (byte stuff) {Write<byte> (stuff);}
+	void Write (word stuff) {Write<word> (stuff);}
 };
 
 #endif // __OBJWRITER_H__
\ No newline at end of file
--- a/parser.cxx	Sat Jul 28 17:57:37 2012 +0300
+++ b/parser.cxx	Sun Jul 29 04:02:07 2012 +0300
@@ -62,9 +62,12 @@
 bool g_GotMainLoop = false;
 
 // ============================================================================
-// Main parser
+// Main parser code. Begins read of the script file, checks the syntax of it
+// and writes the data to the object file via ObjWriter - which also takes care
+// of necessary buffering so stuff is written in the correct order.
 void ScriptReader::BeginParse (ObjWriter* w) {
 	while (Next()) {
+		printf ("BeginParse: token: `%s`\n", token.chars());
 		if (!token.icompare ("state")) {
 			MUST_TOPLEVEL
 			
@@ -114,7 +117,7 @@
 			g_CurMode = MODE_EVENT;
 			
 			w->Write (DH_EVENT);
-			w->Write<byte> (e->number);
+			w->Write<word> (e->number);
 			g_NumEvents++;
 			continue;
 		}
@@ -164,6 +167,7 @@
 		}
 		
 		if (!token.compare ("}")) {
+			printf ("parse closing brace\n");
 			// Closing brace
 			int dataheader =	(g_CurMode == MODE_EVENT) ? DH_ENDEVENT :
 						(g_CurMode == MODE_MAINLOOP) ? DH_ENDMAINLOOP :
@@ -174,8 +178,8 @@
 				ParserError ("unexpected `}`");
 			
 			// Data header must be written before mode is changed because
-			// onenter and mainloop go into buffers, and we want the closing
-			// data headers into said buffers too.
+			// onenter and mainloop go into special buffers, and we want
+			// the closing data headers into said buffers too.
 			w->Write (dataheader);
 			g_CurMode = MODE_TOPLEVEL;
 			
@@ -184,12 +188,23 @@
 			continue;
 		}
 		
+		// If it's a variable, expect assignment.
+		if (ScriptVar* var = FindGlobalVariable (token)) {
+			DataBuffer* b = ParseAssignment (var);
+			printf ("current token after assignment: `%s`\n", token.chars());
+			MustNext (";");
+			w->WriteBuffer (b);
+			delete b;
+			continue;
+		}
+		
 		// If it's not a keyword, parse it as an expression.
+		printf ("token length: %d, first char: %c [%d]\n", token.len(), token.chars()[0], token.chars()[0]);
 		DataBuffer* b = ParseExpression (TYPE_VOID);
 		w->WriteBuffer (b);
 		delete b;
-		printf ("expression done!\n");
-		MustThis (";");
+		printf ("expression done! current token is %s\n", token.chars());
+		MustNext (";");
 	}
 	
 	if (g_CurMode != MODE_TOPLEVEL)
@@ -207,18 +222,22 @@
 }
 
 // ============================================================================
-// Parses a given command
+// Parses a command call
 DataBuffer* ScriptReader::ParseCommand (CommandDef* comm) {
 	DataBuffer* r = new DataBuffer(64);
-	// If this was defined at top-level, we stop right at square one!
 	if (g_CurMode == MODE_TOPLEVEL)
-		ParserError ("no commands allowed at top level!");
+		ParserError ("command call at top level");
 	
+	printf ("\n\n\n=====================================\nBEGIN PARSING COMMAND\n");
 	printf ("token: %s\n", token.chars());
 	MustNext ("(");
+	MustNext ();
 	
 	int curarg = 0;
 	while (1) {
+		printf ("at argument %d\n", curarg);
+		printf ("next token: %s\n", token.chars());
+		
 		if (!token.compare (")")) {
 			printf ("closing command with token `%s`\n", token.chars());
 			if (curarg < comm->numargs - 1)
@@ -226,28 +245,26 @@
 			break;
 			curarg++;
 		}
-		printf ("prev token: %s\n", token.chars());
-		MustNext ();
-		printf ("next token: %s\n", token.chars());
-		
-		// jump back to start
-		if (!token.compare (")"))
-			continue;
 		
 		if (curarg >= comm->maxargs)
 			ParserError ("too many arguments passed to %s\n", comm->name.chars());
 		
 		r->Merge (ParseExpression (comm->argtypes[curarg]));
+		MustNext ();
+		printf ("after expression, token is `%s`\n", token.chars());
 		
 		if (curarg < comm->numargs - 1) {
 			MustThis (",");
+			MustNext ();
 		} else if (curarg < comm->maxargs - 1) {
 			// Can continue, but can terminate as well.
 			if (!token.compare (")")) {
 				curarg++;
 				break;
-			} else
+			} else {
 				MustThis (",");
+				MustNext ();
+			}
 		}
 		
 		curarg++;
@@ -255,19 +272,21 @@
 	
 	// If the script skipped any optional arguments, fill in defaults.
 	while (curarg < comm->maxargs) {
-		r->Write<byte> (DH_PUSHNUMBER);
-		r->Write<byte> (comm->defvals[curarg]);
+		r->Write<word> (DH_PUSHNUMBER);
+		r->Write<word> (comm->defvals[curarg]);
 		curarg++;
 	}
 	
-	r->Write<byte> (DH_COMMAND);
-	r->Write<byte> (comm->number);
-	r->Write<byte> (comm->maxargs);
+	r->Write<word> (DH_COMMAND);
+	r->Write<word> (comm->number);
+	r->Write<word> (comm->maxargs);
 	
 	printf ("command complete\n");
 	return r;
 }
 
+// ============================================================================
+// Is the given operator an assignment operator?
 static bool IsAssignmentOperator (int oper) {
 	switch (oper) {
 	case OPER_ASSIGNADD:
@@ -282,6 +301,7 @@
 }
 
 // ============================================================================
+// Finds an operator's corresponding dataheader
 static long DataHeaderByOperator (ScriptVar* var, int oper) {
 	if (IsAssignmentOperator (oper)) {
 		if (!var)
@@ -312,7 +332,7 @@
 }
 
 // ============================================================================
-// Parses an expression
+// Parses an expression, potentially recursively
 DataBuffer* ScriptReader::ParseExpression (int reqtype) {
 	printf ("begin parsing expression. this token is `%s`, next token is `%s`\n",
 		token.chars(), PeekNext().chars());
@@ -320,20 +340,18 @@
 	
 	DataBuffer* lb = NULL;
 	
+	// If it's a variable, note it down - we need to do special checks with it later.
 	ScriptVar* var = FindGlobalVariable (token);
-	if (var) {
-		MustNext ();
-		if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction
-			ParserError ("can't alter variables at top level");
-		reqtype = TYPE_INT;
-	} else
-		lb = ParseExprValue (reqtype);
+	
+	lb = ParseExprValue (reqtype);
 	printf ("done\n");
 	
 	// Get an operator
 	printf ("parse operator at token %s\n", token.chars());
-	int oper = ParseOperator ();
+	int oper = ParseOperator (true);
+	printf ("operator parsed: token is now %s\n", token.chars());
 	printf ("got %d\n", oper);
+	
 	// No operator found - stop here.
 	if (oper == -1) {
 		retbuf->Merge (lb);
@@ -341,30 +359,37 @@
 		return retbuf;
 	}
 	
+	// We peeked the operator, move forward now
+	MustNext();
+	
+	// Can't be an assignement operator, those belong in assignments.
+	if (IsAssignmentOperator (oper))
+		ParserError ("assignment operator inside expressions");
+	
 	// Parse the right operand,
 	printf ("parse right operand\n");
 	MustNext ();
 	DataBuffer* rb = ParseExprValue (reqtype);
 	printf ("done\n");
 	
+	retbuf->Merge (rb);
 	retbuf->Merge (lb);
-	retbuf->Merge (rb);
 	
 	long dh = DataHeaderByOperator (var, oper);
-	retbuf->Write<byte> (dh);
-	
-	if (IsAssignmentOperator (oper))
-		retbuf->Write<byte> (var->index);
+	retbuf->Write<word> (dh);
 	
 	printf ("expression complete\n");
 	return retbuf;
 }
 
 // ============================================================================
-// Parsess an operator from tokens and returns an identifier.
-int ScriptReader::ParseOperator () {
+// Parses an operator string. Returns the operator number code.
+int ScriptReader::ParseOperator (bool peek) {
 	str oper;
-	oper += PeekNext ();
+	if (peek)
+		oper += PeekNext ();
+	else
+		oper += token;
 	
 	// Check one-char operators
 	int o =	!oper.compare ("=") ? OPER_ASSIGN :
@@ -376,13 +401,11 @@
 		-1;
 	
 	if (o != -1) {
-		MustNext ();
 		return o;
 	}
 	
 	// Two-char operators
-	MustNext ();
-	oper += PeekNext (1);
+	oper += PeekNext (peek ? 1 : 0);
 	
 	o =	!oper.compare ("+=") ? OPER_ASSIGNADD :
 		!oper.compare ("-=") ? OPER_ASSIGNSUB :
@@ -391,10 +414,8 @@
 		!oper.compare ("%=") ? OPER_ASSIGNMOD :
 		-1;
 	
-	if (o != -1) {
+	if (o != -1)
 		MustNext ();
-		MustNext ();
-	}
 	
 	return o;
 }
@@ -423,12 +444,12 @@
 		// Command
 		if (reqtype && comm->returnvalue != reqtype)
 			ParserError ("%s returns an incompatible data type", comm->name.chars());
-		return ParseCommand (comm);
+		b = ParseCommand (comm);
 	} else if ((g = FindGlobalVariable (token)) && reqtype != TYPE_STRING) {
 		printf ("value is a global var\n");
 		// Global variable
-		b->Write<byte> (DH_PUSHGLOBALVAR);
-		b->Write<byte> (g->index);
+		b->Write<word> (DH_PUSHGLOBALVAR);
+		b->Write<word> (g->index);
 	} else {
 		printf ("value is a literal\n");
 		// If nothing else, check for literal
@@ -437,22 +458,15 @@
 			ParserError ("bad syntax");
 			break;
 		case TYPE_INT: {
-			printf ("value is an integer literal\n");
-			/* if (!token.isnumber ())
-				ParserError ("expected an integer, got `%s`", token.chars());
-			*/
 			MustNumber (true);
-			printf ("literal is `%s` = %d\n", token.chars(), atoi (token.chars()));
 			
 			// All values are written unsigned - thus we need to write the value's
 			// absolute value, followed by an unary minus if it was negative.
-			b->Write<byte> (DH_PUSHNUMBER);
+			b->Write<word> (DH_PUSHNUMBER);
 			long v = atoi (token.chars ());
-			b->Write<byte> (static_cast<byte> (abs (v)));
-			if (v < 0) {
-				printf ("%ld is negative, write abs value %d and unary minus\n", v, abs(v));
-				b->Write<byte> (DH_UNARYMINUS);
-			}
+			b->Write<word> (static_cast<word> (abs (v)));
+			if (v < 0)
+				b->Write<word> (DH_UNARYMINUS);
 			break;
 		}
 		case TYPE_STRING:
@@ -461,11 +475,45 @@
 			// table and returns it index if it doesn't find it there.
 			printf ("value is a string literal\n");
 			MustString (true);
-			b->Write<byte> (DH_PUSHSTRINGINDEX);
-			b->Write<byte> (PushToStringTable (token.chars()));
+			b->Write<word> (DH_PUSHSTRINGINDEX);
+			b->Write<word> (PushToStringTable (token.chars()));
 			break;
 		}
 	}
+	printf ("value parsed: current token is `%s`\n", token.chars());
 	
 	return b;
+}
+
+// ============================================================================
+// Parses an assignment. An assignment starts with a variable name, followed
+// by an assignment operator, followed by an expression value. Expects current
+// token to be the name of the variable, and expects the variable to be given.
+DataBuffer* ScriptReader::ParseAssignment (ScriptVar* var) {
+	printf ("ASSIGNMENT: this token is `%s`, next token is `%s`\n",
+		token.chars(), PeekNext().chars());
+	
+	// Get an operator
+	printf ("parse assignment operator at token %s\n", token.chars());
+	
+	MustNext ();
+	int oper = ParseOperator ();
+	if (!IsAssignmentOperator (oper))
+		ParserError ("expected assignment operator");
+	printf ("got %d\n", oper);
+	
+	if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction
+		ParserError ("can't alter variables at top level");
+	
+	// Parse the right operand,
+	printf ("parse right operand\n");
+	MustNext ();
+	DataBuffer* retbuf = ParseExprValue (TYPE_INT);
+	
+	long dh = DataHeaderByOperator (var, oper);
+	retbuf->Write<word> (dh);
+	retbuf->Write<word> (var->index);
+	
+	printf ("assignment complete\n");
+	return retbuf;
 }
\ No newline at end of file
--- a/preprocessor.cxx	Sat Jul 28 17:57:37 2012 +0300
+++ b/preprocessor.cxx	Sun Jul 29 04:02:07 2012 +0300
@@ -65,6 +65,7 @@
 	}
 }
 
+// ============================================================================
 // Reads a word until whitespace
 str ScriptReader::PPReadWord (char &term) {
 	str word;
@@ -79,13 +80,16 @@
 	return word;
 }
 
+// ============================================================================
+// Preprocess any directives found in the script file
 void ScriptReader::PreprocessDirectives () {
 	size_t spos = ftell (fp[fc]);
 	if (!DoDirectivePreprocessing ())
 		fseek (fp[fc], spos, SEEK_SET);
 }
 
-/* Returns true if the pre-processing was successful, false if not.
+/* ============================================================================
+ * Returns true if the pre-processing was successful, false if not.
  * If pre-processing was successful, the file pointer remains where
  * it was, if not, it's pushed back to where it was before preprocessing
  * took place and is parsed normally.
--- a/scriptreader.cxx	Sat Jul 28 17:57:37 2012 +0300
+++ b/scriptreader.cxx	Sun Jul 29 04:02:07 2012 +0300
@@ -59,7 +59,11 @@
 }
 
 ScriptReader::~ScriptReader () {
-	FinalChecks ();
+	// If comment mode is 2 by the time the file ended, the
+	// comment was left unterminated. 1 is no problem, since
+	// it's terminated by newlines anyway.
+	if (commentmode == 2)
+		ParserError ("unterminated `/*`-style comment");
 	
 	for (unsigned int u = 0; u < MAX_FILESTACK; u++) {
 		if (fp[u]) {
@@ -362,16 +366,19 @@
 		MustNext ();
 	num += token;
 	
-	// Cater for a possible minus sign, since it
-	// breaks the token, read a new one with the number.
+	// The number can possibly start off with a minus sign, which
+	// also breaks the token. If we encounter it, read another token
+	// and merge it to this one.
 	if (!token.compare ("-")) {
 		MustNext ();
 		num += token;
 	}
 	
+	// Result must be a number.
 	if (!num.isnumber())
 		ParserError ("expected a number, got `%s`", num.chars());
 	
+	// Save the number into the token.
 	token = num;
 }
 
@@ -396,13 +403,4 @@
 	case RETURNVAL_STRING: MustString (); break;
 	case RETURNVAL_BOOLEAN: MustBool (); break;
 	}
-}
-
-// Checks to be performed at the end of file
-void ScriptReader::FinalChecks () {
-	// If comment mode is 2 by the time the file ended, the
-	// comment was left unterminated. 1 is no problem, since
-	// it's terminated by newlines anyway.
-	if (commentmode == 2)
-		ParserError ("unterminated `/*`-style comment");
 }
\ No newline at end of file
--- a/scriptreader.h	Sat Jul 28 17:57:37 2012 +0300
+++ b/scriptreader.h	Sun Jul 29 04:02:07 2012 +0300
@@ -48,6 +48,8 @@
 
 #define MAX_FILESTACK 8
 
+class ScriptVar;
+
 class ScriptReader {
 public:
 	// ====================================================================
@@ -89,13 +91,12 @@
 	void ParserError (const char* message, ...);
 	void ParserWarning (const char* message, ...);
 	
-	void FinalChecks ();
-	
 	// parser.cxx:
 	void BeginParse (ObjWriter* w);
 	DataBuffer* ParseCommand (CommandDef* comm);
 	DataBuffer* ParseExpression (int reqtype);
-	int ParseOperator ();
+	DataBuffer* ParseAssignment (ScriptVar* var);
+	int ParseOperator (bool peek = false);
 	DataBuffer* ParseExprValue (int reqtype);
 	
 	// preprocessor.cxx:
--- a/stringtable.cxx	Sat Jul 28 17:57:37 2012 +0300
+++ b/stringtable.cxx	Sun Jul 29 04:02:07 2012 +0300
@@ -46,6 +46,8 @@
 #include "bots.h"
 #include "stringtable.h"
 
+// ============================================================================
+// Initializes the string table
 void InitStringTable() {
 	// Zero out everything first.
 	for (unsigned int a = 0; a < MAX_LIST_STRINGS; a++)
@@ -53,10 +55,13 @@
 			g_StringTable[a][b] = 0;
 }
 
+// ============================================================================
+// Potentially adds a string to the table and returns the index of it.
 unsigned int PushToStringTable (char* s) {
 	// Find a free slot in the table. 
 	unsigned int a;
 	for (a = 0; a < MAX_LIST_STRINGS; a++) {
+		// String is already in the table, thus return it.
 		if (!strcmp (s, g_StringTable[a]))
 			return a;
 		
@@ -81,6 +86,8 @@
 	return a;
 }
 
+// ============================================================================
+// Counts the amount of strings in the table.
 unsigned int CountStringTable () {
 	unsigned int count = 0;
 	for (unsigned int a = 0; a < MAX_LIST_STRINGS; a++) {
--- a/variables.cxx	Sat Jul 28 17:57:37 2012 +0300
+++ b/variables.cxx	Sun Jul 29 04:02:07 2012 +0300
@@ -59,9 +59,17 @@
 		g_GlobalVariables[u] = NULL;
 }
 
+// ============================================================================
 // Tries to declare a new global-scope variable. Returns pointer
 // to new global variable, NULL if declaration failed.
 ScriptVar* DeclareGlobalVariable (ScriptReader* r, str name) {
+	// Check that the name is valid
+	if (FindCommand (name))
+		r->ParserError ("name of variable-to-be `%s` conflicts with that of a command", name.chars());
+	
+	if (IsKeyword (name))
+		r->ParserError ("name of variable-to-be `%s` is a keyword", name.chars());
+	
 	// Find a NULL pointer to a global variable
 	ScriptVar* g;
 	unsigned int u = 0;
@@ -71,7 +79,7 @@
 			break;
 		
 		if (!g_GlobalVariables[u]->name.icompare (name))
-			r->ParserError ("tried to declare global scope variable %s twice", name.chars());
+			r->ParserError ("attempted redeclaration of variable `%s`", name.chars());
 	}
 	
 	if (u == MAX_SCRIPT_VARIABLES)
@@ -96,6 +104,7 @@
 }
 */
 
+// ============================================================================
 // Find a global variable by name
 ScriptVar* FindGlobalVariable (str name) {
 	unsigned int u = 0;
@@ -111,6 +120,8 @@
 	return NULL;
 }
 
+// ============================================================================
+// Count all declared global variables
 unsigned int CountGlobalVars () {
 	unsigned int count = 0;
 	unsigned int u = 0;

mercurial