Expression parser mostly up and running!! Still work to do on it though.

Sat, 28 Jul 2012 17:41:24 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sat, 28 Jul 2012 17:41:24 +0300
changeset 34
0a9a5902beaa
parent 33
fd35f6cb5f28
child 35
3d3f6ed40171

Expression parser mostly up and running!! Still work to do on it though.

commands.cxx file | annotate | diff | comparison | revisions
commands.def file | annotate | diff | comparison | revisions
commands.h file | annotate | diff | comparison | revisions
common.h file | annotate | diff | comparison | revisions
databuffer.h file | annotate | diff | comparison | revisions
main.cxx file | annotate | diff | comparison | revisions
objwriter.cxx file | annotate | diff | comparison | revisions
objwriter.h file | annotate | diff | comparison | revisions
parser.cxx file | annotate | diff | comparison | revisions
scriptreader.cxx file | annotate | diff | comparison | revisions
scriptreader.h file | annotate | diff | comparison | revisions
str.cxx file | annotate | diff | comparison | revisions
--- a/commands.cxx	Tue Jul 17 20:35:43 2012 +0300
+++ b/commands.cxx	Sat Jul 28 17:41:24 2012 +0300
@@ -51,7 +51,7 @@
 	ScriptReader* r = new ScriptReader ("commands.def");
 	g_CommDef = NULL;
 	CommandDef* curdef = g_CommDef;
-	unsigned int numCommDefs = 0; 
+	unsigned int numCommDefs = 0;
 	
 	while (r->PeekNext().len()) {
 		CommandDef* comm = new CommandDef;
@@ -71,7 +71,7 @@
 		
 		// Return value
 		r->MustNext ();
-		comm->returnvalue = GetReturnTypeByString (r->token);
+		comm->returnvalue = GetCommandType (r->token);
 		if (comm->returnvalue == -1)
 			r->ParserError ("bad return value type `%s`", r->token.chars());
 		
@@ -96,7 +96,7 @@
 			r->MustNext (":");
 			r->MustNext ();
 			
-			int type = GetReturnTypeByString (r->token);
+			int type = GetCommandType (r->token);
 			if (type == -1)
 				r->ParserError ("bad argument %d type `%s`", curarg, r->token.chars());
 			if (type == RETURNVAL_VOID)
@@ -109,7 +109,7 @@
 			// - 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);
+					r->token.len(), MAX_ARGNAMELEN - 1);
 			
 			strncpy (comm->argnames[curarg], r->token.chars(), MAX_ARGNAMELEN);
 			comm->argnames[curarg][MAX_ARGNAMELEN-1] = 0;
@@ -146,23 +146,22 @@
 	delete r;
 	
 	if (!numCommDefs)
-		error ("error: no commands defined!\n");
+		error ("no commands defined!\n");
 	printf ("%d command definitions read.\n", numCommDefs);
 }
 
-// urgh long name
-int GetReturnTypeByString (str t) {
+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..
 	t = 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;
+	return	!t.compare ("int") ? TYPE_INT :
+		!t.compare ("float") ? TYPE_INT :
+		!t.compare ("str") ? TYPE_STRING :
+		!t.compare ("void") ? TYPE_VOID :
+		!t.compare ("bool") ? TYPE_INT : -1;
 }
 
 // Inverse operation
@@ -177,7 +176,7 @@
 	return "";
 }
 
-CommandDef* GetCommandByName (str fname) {
+CommandDef* FindCommand (str fname) {
 	CommandDef* comm;
 	ITERATE_COMMANDS (comm) {
 		if (!fname.icompare (comm->name))
--- a/commands.def	Tue Jul 17 20:35:43 2012 +0300
+++ b/commands.def	Sat Jul 28 17:41:24 2012 +0300
@@ -1,8 +1,3 @@
-/* This file defines the commands botc will treat as valid.
- * Do not edit unless you know what you are doing!
- *
- * Syntax: number:name:returntype:numargs:maxargs[:argumentlist]
- */
 0:changestate:void:1:1:str(statename)
 1:delay:void:1:1:int(tics)
 2:rand:int:2:2:int(a):int(b)
--- a/commands.h	Tue Jul 17 20:35:43 2012 +0300
+++ b/commands.h	Sat Jul 28 17:41:24 2012 +0300
@@ -63,8 +63,8 @@
 };
 
 void ReadCommands ();
-CommandDef* GetCommandByName (str a);
-int GetReturnTypeByString (str token);
+CommandDef* FindCommand (str a);
+int GetCommandType (str token);
 str GetReturnTypeName (int r);
 str GetCommandPrototype (CommandDef* comm);
 
--- a/common.h	Tue Jul 17 20:35:43 2012 +0300
+++ b/common.h	Sat Jul 28 17:41:24 2012 +0300
@@ -47,6 +47,7 @@
 #include "bots.h"
 #include "str.h"
 
+// Application name and version
 #define APPNAME "botc"
 #define VERSION_MAJOR 0
 #define VERSION_MINOR 0
@@ -57,10 +58,10 @@
 	#define FILE_CASEINSENSITIVE
 #endif
 
-// Where is the parser at?
+// Parser mode: where is the parser at?
 enum parsermode {
 	MODE_TOPLEVEL,	// at top level
-	MODE_EVENT,	// inside event definition
+	MODE_EVENT,		// inside event definition
 	MODE_MAINLOOP,	// inside mainloop
 	MODE_ONENTER,	// inside onenter
 	MODE_ONEXIT,	// inside onexit
@@ -72,18 +73,21 @@
 		exit (1); \
 	}
 
+// Shortcut for formatting
 #define PERFORM_FORMAT(in, out) \
 	va_list v; \
 	va_start (v, in); \
 	char* out = vdynformat (in, v, 256); \
 	va_end (v);
 
+// Plural expression
 #define PLURAL(n) (n != 1) ? "s" : ""
 
 void error (const char* text, ...);
 char* ObjectFileName (str s);
 bool fexists (char* path);
 
+// Make the parser's variables globally available
 #ifndef __PARSER_CXX__
 extern int g_NumStates;
 extern int g_NumEvents;
@@ -92,7 +96,7 @@
 #endif
 
 // Power function
-template<class T> T pow (T a, T b) {
+template<class T> T pow (T a, int b) {
 	if (!b)
 		return 1;
 	
@@ -112,4 +116,7 @@
 	return (c <= 32 || c == 127 || c == 255);
 }
 
+// Byte datatype
+typedef unsigned long int byte;
+
 #endif // __COMMON_H__
\ No newline at end of file
--- a/databuffer.h	Tue Jul 17 20:35:43 2012 +0300
+++ b/databuffer.h	Sat Jul 28 17:41:24 2012 +0300
@@ -104,18 +104,28 @@
 		}
 	}
 	
+	// Merge another data buffer into this one.
+	void Merge (DataBuffer* other) {
+		for (unsigned int x = 0; x < other->writesize; x++) {
+			unsigned char c = *(other->buffer+x);
+			Write<unsigned char> (c);
+		}
+		
+		delete other;
+	}
+	
 private:
 	template <class T> unsigned char CharByte (T a, unsigned int b) {
 		if (b >= sizeof (T))
 			error ("CharByte: tried to get byte %u out of a %u-byte %s\n",
-				b, sizeof (T), typeid(T).name());
+				b, sizeof (T), typeid (T).name());
 		
 		unsigned long p1 = pow<unsigned long> (256, b);
 		unsigned long p2 = pow<unsigned long> (256, b+1);
 		unsigned long r = (a % p2) / p1;
 		
 		if (r > 256)
-			error ("result %lu too big!", r);
+			error ("DataBuffer::CharByte: result %lu too big!", r);
 		
 		unsigned char ur = static_cast<unsigned char> (r);
 		return ur;
--- a/main.cxx	Tue Jul 17 20:35:43 2012 +0300
+++ b/main.cxx	Sat Jul 28 17:41:24 2012 +0300
@@ -129,7 +129,7 @@
 	ReadCommands ();
 	
 	// Init stuff
-	InitStringTable();
+	InitStringTable ();
 	InitVariables ();
 	
 	// Prepare reader and writer
@@ -148,8 +148,11 @@
 	w->WriteToFile ();
 	
 	// Clear out the junk
+	printf ("clear r\n");
 	delete r;
+	printf ("clear w\n");
 	delete w;
+	printf ("done!\n");
 }
 
 void error (const char* text, ...) {
@@ -164,8 +167,6 @@
 	if (extdot >= s.len()-4)
 		s.trim (s.len() - extdot);
 	
-	// Add new ".o" extension
 	s += ".o";
-	
 	return s.chars();
 }
\ No newline at end of file
--- a/objwriter.cxx	Tue Jul 17 20:35:43 2012 +0300
+++ b/objwriter.cxx	Sat Jul 28 17:41:24 2012 +0300
@@ -45,6 +45,7 @@
 #include "common.h"
 #include "str.h"
 #include "objwriter.h"
+#include "stringtable.h"
 
 #include "bots.h"
 
@@ -60,8 +61,12 @@
 
 ObjWriter::~ObjWriter () {
 	delete MainBuffer;
-	delete MainLoopBuffer;
 	delete OnEnterBuffer;
+	
+	// This crashes for some reason o_O
+	// Should make no difference anyway, since ObjWriter
+	// is only deleted at the end of the program anyway
+	// delete MainLoopBuffer;
 }
 
 void ObjWriter::WriteString (char* s) {
@@ -78,6 +83,13 @@
 	WriteString (s.chars());
 }
 
+void ObjWriter::WriteBuffer (DataBuffer* buf) {
+	for (unsigned int x = 0; x < buf->writesize; x++) {
+		unsigned char c = *(buf->buffer+x);
+		Write<unsigned char> (c);
+	}
+}
+
 void ObjWriter::WriteBuffers () {
 	// If there was no mainloop defined, write a dummy one now.
 	if (!g_GotMainLoop) {
@@ -88,10 +100,7 @@
 	// Write the onenter and mainloop buffers, IN THAT ORDER
 	for (int i = 0; i < 2; i++) {
 		DataBuffer* buf = (!i) ? OnEnterBuffer : MainLoopBuffer;
-		for (unsigned int x = 0; x < buf->writesize; x++) {
-			unsigned char c = *(buf->buffer+x);
-			Write<unsigned char> (c);
-		}
+		WriteBuffer (buf);
 		
 		// Clear the buffer afterwards for potential next state
 		delete buf;
@@ -102,6 +111,21 @@
 	g_GotMainLoop = false;
 }
 
+// Write string table
+void ObjWriter::WriteStringTable () {
+	// If we added strings here, we need to write a list of them.
+	unsigned int stringcount = CountStringTable ();
+	if (stringcount) {
+		Write<long> (DH_STRINGLIST);
+		Write<long> (stringcount);
+		
+		for (unsigned int a = 0; a < stringcount; a++)
+			WriteString (g_StringTable[a]);
+	}
+	
+	printf ("%u string%s written\n", stringcount, PLURAL (stringcount));
+}
+
 // Write main buffer to file
 void ObjWriter::WriteToFile () {
 	fp = fopen (filepath, "w");
--- a/objwriter.h	Tue Jul 17 20:35:43 2012 +0300
+++ b/objwriter.h	Sat Jul 28 17:41:24 2012 +0300
@@ -68,7 +68,9 @@
 	void WriteString (char* s);
 	void WriteString (const char* s);
 	void WriteString (str s);
+	void WriteBuffer (DataBuffer* buf);
 	void WriteBuffers ();
+	void WriteStringTable ();
 	void WriteToFile ();
 	
 	template <class T> void Write (T stuff) {
@@ -80,7 +82,7 @@
 	}
 	
 	// Cannot use default arguments in function templates..
-	void Write (long stuff) {Write<long> (stuff);}
+	void Write (byte stuff) {Write<byte> (stuff);}
 };
 
 #endif // __OBJWRITER_H__
\ No newline at end of file
--- a/parser.cxx	Tue Jul 17 20:35:43 2012 +0300
+++ b/parser.cxx	Sat Jul 28 17:41:24 2012 +0300
@@ -61,6 +61,8 @@
 bool g_stateSpawnDefined = false;
 bool g_GotMainLoop = false;
 
+// ============================================================================
+// Main parser
 void ScriptReader::BeginParse (ObjWriter* w) {
 	while (Next()) {
 		if (!token.icompare ("state")) {
@@ -112,7 +114,7 @@
 			g_CurMode = MODE_EVENT;
 			
 			w->Write (DH_EVENT);
-			w->Write<long> (e->number);
+			w->Write<byte> (e->number);
 			g_NumEvents++;
 			continue;
 		}
@@ -181,65 +183,13 @@
 				MustNext (";");
 			continue;
 		}
-		// Check global variables
-		ScriptVar* g = FindGlobalVariable (token);
-		if (g) {
-			// Not in top level, unfortunately..
-			if (g_CurMode == MODE_TOPLEVEL)
-				ParserError ("can't alter variables at top level");
-			
-			// Build operator string. Only '=' is one
-			// character, others are two.
-			MustNext ();
-			str oper = token;
-			if (token.compare ("=") != 0) {
-				MustNext ();
-				oper += token;
-			}
-			
-			// Unary operators
-			if (!oper.compare ("++")) {
-				w->Write<long> (DH_INCGLOBALVAR);
-				w->Write<long> (g->index);
-			} else if (!oper.compare ("--")) {
-				w->Write<long> (DH_DECGLOBALVAR);
-				w->Write<long> (g->index);
-			} else {
-				// Binary operators
-				// And only with numbers for now too.
-				// TODO: make a proper expression parser!
-				MustNumber();
-				
-				int val = atoi (token.chars());
-				w->Write<long> (DH_PUSHNUMBER);
-				w->Write<long> (val);
-				
-				int h =	!oper.compare("=") ? DH_ASSIGNGLOBALVAR :
-					!oper.compare("+=") ? DH_ADDGLOBALVAR :
-					!oper.compare("-=") ? DH_SUBGLOBALVAR :
-					!oper.compare("*=") ? DH_MULGLOBALVAR :
-					!oper.compare("/=") ? DH_DIVGLOBALVAR :
-					!oper.compare("%=") ? DH_MODGLOBALVAR : -1;
-				
-				if (h == -1)
-					ParserError ("bad operator `%s`!", oper.chars());
-				
-				w->Write<long> (h);
-				w->Write<long> (g->index);
-			}
-				
-			MustNext (";");
-			continue;
-		}
 		
-		// Check if it's a command.
-		CommandDef* comm = GetCommandByName (token);
-		if (comm) { 
-			ParseCommand (comm, w);
-			continue;
-		}
-		
-		ParserError ("unknown keyword `%s`", token.chars());
+		// If it's not a keyword, parse it as an expression.
+		DataBuffer* b = ParseExpression (TYPE_VOID);
+		w->WriteBuffer (b);
+		delete b;
+		printf ("expression done!\n");
+		MustThis (";");
 	}
 	
 	if (g_CurMode != MODE_TOPLEVEL)
@@ -250,97 +200,266 @@
 		ParserError ("script must have a state named `stateSpawn`!");
 	
 	// Dump the last state's onenter and mainloop
-	w->WriteBuffers();
+	w->WriteBuffers ();
 	
-	// If we added strings here, we need to write a list of them.
-	unsigned int stringcount = CountStringTable ();
-	if (stringcount) {
-		w->Write<long> (DH_STRINGLIST);
-		w->Write<long> (stringcount);
-		for (unsigned int a = 0; a < stringcount; a++)
-			w->WriteString (g_StringTable[a]);
-	}
-	
-	printf ("%u string%s written\n", stringcount, PLURAL (stringcount));
+	// String table
+	w->WriteStringTable ();
 }
 
-void ScriptReader::ParseCommand (CommandDef* comm, ObjWriter* w) {
+// ============================================================================
+// Parses a given command
+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!");
 	
+	printf ("token: %s\n", token.chars());
 	MustNext ("(");
+	
 	int curarg = 0;
 	while (1) {
-		if (curarg >= comm->maxargs) {
-			if (!PeekNext().compare (","))
-				ParserError ("got `,` while expecting command-terminating `)`, are you passing too many parameters? (max %d)",
-					comm->maxargs);
-			MustNext (")");
+		if (!token.compare (")")) {
+			printf ("closing command with token `%s`\n", token.chars());
+			if (curarg < comm->numargs - 1)
+				ParserError ("too few arguments passed to %s\n", comm->name.chars());
+			break;
 			curarg++;
-			break;
 		}
-		
-		if (!PeekNext().len())
-			ParserError ("unexpected end-of-file, unterminated command");
-		
-		// If we get a ")" now, the user probably gave too few parameters
-		if (!PeekNext().compare (")"))
-			ParserError ("unexpected `)`, did you pass too few parameters? (need %d)", comm->numargs);
+		printf ("prev token: %s\n", token.chars());
+		MustNext ();
+		printf ("next token: %s\n", token.chars());
 		
-		// Argument may be using a variable
-		ScriptVar* g = FindGlobalVariable (PeekNext ());
-		if (g && comm->argtypes[curarg] != RETURNVAL_STRING) {
-			// Advance cursor past the var name
-			Next();
-			
-			w->Write<long> (DH_PUSHGLOBALVAR);
-			w->Write<long> (g->index);
-		} else {
-			// Check for raw value
-			switch (comm->argtypes[curarg]) {
-			case RETURNVAL_INT:
-				MustNumber();
-				w->Write<long> (DH_PUSHNUMBER);
-				w->Write<long> (atoi (token.chars ()));
-				break;
-			case RETURNVAL_BOOLEAN:
-				MustBool();
-				w->Write<long> (DH_PUSHNUMBER);
-				w->Write<long> (BoolValue ());
-				break;
-			case RETURNVAL_STRING:
-				MustString();
-				w->Write<long> (DH_PUSHSTRINGINDEX);
-				w->Write<long> (PushToStringTable (token.chars()));
-				break;
-			}
-		}
+		// 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]));
 		
 		if (curarg < comm->numargs - 1) {
-			MustNext (",");
+			MustThis (",");
 		} else if (curarg < comm->maxargs - 1) {
 			// Can continue, but can terminate as well.
-			if (!PeekNext ().compare (")")) {
-				MustNext (")");
+			if (!token.compare (")")) {
 				curarg++;
 				break;
 			} else
-				MustNext (",");
+				MustThis (",");
 		}
 		
 		curarg++;
 	}
-	MustNext (";");
 	
 	// If the script skipped any optional arguments, fill in defaults.
 	while (curarg < comm->maxargs) {
-		w->Write<long> (DH_PUSHNUMBER);
-		w->Write<long> (comm->defvals[curarg]);
+		r->Write<byte> (DH_PUSHNUMBER);
+		r->Write<byte> (comm->defvals[curarg]);
 		curarg++;
 	}
 	
-	w->Write<long> (DH_COMMAND);
-	w->Write<long> (comm->number);
-	w->Write<long> (comm->maxargs);
+	r->Write<byte> (DH_COMMAND);
+	r->Write<byte> (comm->number);
+	r->Write<byte> (comm->maxargs);
+	
+	printf ("command complete\n");
+	return r;
+}
+
+static bool IsAssignmentOperator (int oper) {
+	switch (oper) {
+	case OPER_ASSIGNADD:
+	case OPER_ASSIGNSUB:
+	case OPER_ASSIGNMUL:
+	case OPER_ASSIGNDIV:
+	case OPER_ASSIGNMOD:
+	case OPER_ASSIGN:
+		return true;
+	}
+	return false;
+}
+
+// ============================================================================
+static long DataHeaderByOperator (ScriptVar* var, int oper) {
+	if (IsAssignmentOperator (oper)) {
+		if (!var)
+			error ("operator %d requires left operand to be a variable\n", oper);
+		
+		// TODO: At the moment, vars only are global
+		switch (oper) {
+		case OPER_ASSIGNADD: return DH_ADDGLOBALVAR;
+		case OPER_ASSIGNSUB: return DH_SUBGLOBALVAR;
+		case OPER_ASSIGNMUL: return DH_MULGLOBALVAR;
+		case OPER_ASSIGNDIV: return DH_DIVGLOBALVAR;
+		case OPER_ASSIGNMOD: return DH_MODGLOBALVAR;
+		case OPER_ASSIGN: return DH_ASSIGNGLOBALVAR;
+		default: error ("bad assignment operator!!\n");
+		}
+	}
+	
+	switch (oper) {
+	case OPER_ADD: return DH_ADD;
+	case OPER_SUBTRACT: return DH_SUBTRACT;
+	case OPER_MULTIPLY: return DH_MULTIPLY;
+	case OPER_DIVIDE: return DH_DIVIDE;
+	case OPER_MODULUS: return DH_MODULUS;
+	}
+	
+	error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper);
+	return 0;
+}
+
+// ============================================================================
+// Parses an expression
+DataBuffer* ScriptReader::ParseExpression (int reqtype) {
+	printf ("begin parsing expression. this token is `%s`, next token is `%s`\n",
+		token.chars(), PeekNext().chars());
+	DataBuffer* retbuf = new DataBuffer (64);
+	
+	DataBuffer* lb = NULL;
+	
+	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);
+	printf ("done\n");
+	
+	// Get an operator
+	printf ("parse operator at token %s\n", token.chars());
+	int oper = ParseOperator ();
+	printf ("got %d\n", oper);
+	// No operator found - stop here.
+	if (oper == -1) {
+		retbuf->Merge (lb);
+		printf ("expression complete without operator, stopping at `%s`\n", token.chars());
+		return retbuf;
+	}
+	
+	// Parse the right operand,
+	printf ("parse right operand\n");
+	MustNext ();
+	DataBuffer* rb = ParseExprValue (reqtype);
+	printf ("done\n");
+	
+	retbuf->Merge (lb);
+	retbuf->Merge (rb);
+	
+	long dh = DataHeaderByOperator (var, oper);
+	retbuf->Write<byte> (dh);
+	
+	if (IsAssignmentOperator (oper))
+		retbuf->Write<byte> (var->index);
+	
+	printf ("expression complete\n");
+	return retbuf;
+}
+
+// ============================================================================
+// Parsess an operator from tokens and returns an identifier.
+int ScriptReader::ParseOperator () {
+	str oper;
+	oper += PeekNext ();
+	
+	// Check one-char operators
+	int o =	!oper.compare ("=") ? OPER_ASSIGN :
+		!oper.compare ("+") ? OPER_ADD :
+		!oper.compare ("-") ? OPER_SUBTRACT :
+		!oper.compare ("*") ? OPER_MULTIPLY :
+		!oper.compare ("/") ? OPER_DIVIDE :
+		!oper.compare ("%") ? OPER_MODULUS :
+		-1;
+	
+	if (o != -1) {
+		MustNext ();
+		return o;
+	}
+	
+	// Two-char operators
+	MustNext ();
+	oper += PeekNext (1);
+	
+	o =	!oper.compare ("+=") ? OPER_ASSIGNADD :
+		!oper.compare ("-=") ? OPER_ASSIGNSUB :
+		!oper.compare ("*=") ? OPER_ASSIGNMUL :
+		!oper.compare ("/=") ? OPER_ASSIGNDIV :
+		!oper.compare ("%=") ? OPER_ASSIGNMOD :
+		-1;
+	
+	if (o != -1) {
+		MustNext ();
+		MustNext ();
+	}
+	
+	return o;
+}
+
+// ============================================================================
+// Parses a value in the expression and returns the data needed to push
+// it, contained in a data buffer. A value can be either a variable, a command,
+// a literal or an expression.
+DataBuffer* ScriptReader::ParseExprValue (int reqtype) {
+	printf ("parse expr value `%s` with requirement type %d\n", token.chars(), reqtype);
+	DataBuffer* b = new DataBuffer(16);
+	
+	ScriptVar* g;
+	
+	if (!token.compare ("(")) {
+		printf ("value is an expression\n");
+		// Expression
+		MustNext ();
+		DataBuffer* c = ParseExpression (reqtype);
+		b->Merge (c);
+		MustNext (")");
+	} else if (CommandDef* comm = FindCommand (token)) {
+		printf ("value is a command\n");
+		delete b;
+		
+		// Command
+		if (reqtype && comm->returnvalue != reqtype)
+			ParserError ("%s returns an incompatible data type", comm->name.chars());
+		return 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);
+	} else {
+		printf ("value is a literal\n");
+		// If nothing else, check for literal
+		switch (reqtype) {
+		case TYPE_VOID:
+			ParserError ("bad syntax");
+			break;
+		case TYPE_INT: {
+			if (!token.isnumber ())
+				ParserError ("expected an integer, got `%s`", 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);
+			long v = atoi (token.chars ());
+			b->Write<byte> (static_cast<byte> (abs (v)));
+			if (v < 0)
+				b->Write<byte> (DH_UNARYMINUS);
+			break;
+		}
+		case TYPE_STRING:
+			// PushToStringTable either returns the string index of the
+			// string if it finds it in the table, or writes it to the
+			// 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()));
+			break;
+		}
+	}
+	
+	return b;
 }
\ No newline at end of file
--- a/scriptreader.cxx	Tue Jul 17 20:35:43 2012 +0300
+++ b/scriptreader.cxx	Sat Jul 28 17:41:24 2012 +0300
@@ -47,6 +47,8 @@
 
 ScriptReader::ScriptReader (str path) {
 	token = "";
+	prevtoken = "";
+	prevpos = 0;
 	fc = -1;
 	
 	for (unsigned int u = 0; u < MAX_FILESTACK; u++)
@@ -70,8 +72,7 @@
 // Opens a file and pushes its pointer to stack
 void ScriptReader::OpenFile (str path) {
 	if (fc+1 >= MAX_FILESTACK) 
-		ParserError ("supposed to open file `%s` but file stack is full! \
-			do you have recursive `#include` directives?",
+		ParserError ("supposed to open file `%s` but file stack is full! do you have recursive `#include` directives?",
 			path.chars());
 	
 	// Save the position first.
@@ -157,10 +158,10 @@
 	return c[0];
 }
 
-// true if was found, false if not.
+// Read a token from the file buffer. Returns true if token was found, false if not.
 bool ScriptReader::Next (bool peek) {
+	prevpos = ftell (fp[fc]);
 	str tmp = "";
-	// printf ("begin token\n");
 	
 	while (1) {
 		// Check end-of-file
@@ -190,7 +191,6 @@
 		}
 		
 		c = ReadChar ();
-		// printf ("add char [%d] `%c`\n", c, c);
 		
 		// If this is a comment we're reading, check if this character
 		// gets the comment terminated, otherwise ignore it.
@@ -236,32 +236,46 @@
 	}
 	
 	// If we got nothing here, read failed. This should
-	// only hapen in the case of EOF.
+	// only happen in the case of EOF.
 	if (!tmp.len()) {
 		token = "";
 		return false;
 	}
 	
 	pos[fc]++;
+	prevtoken = token;
 	token = tmp;
 	return true;
 }
 
+void ScriptReader::Prev () {
+	if (!prevpos)
+		error ("ScriptReader::Prev: cannot go back twice!\n");
+	
+	fseek (fp[fc], prevpos, SEEK_SET);
+	prevpos = 0;
+	token = prevtoken;
+}
+
 // Returns the next token without advancing the cursor.
-str ScriptReader::PeekNext () {
-	// Store current position
+str ScriptReader::PeekNext (int offset) {
+	// Store current information
+	str storedtoken = token;
 	int cpos = ftell (fp[fc]);
 	
 	// Advance on the token.
-	if (!Next (true))
-		return "";
+	while (offset >= 0) {
+		if (!Next (true))
+			return "";
+		offset--;
+	}
 	
 	str tmp = token;
 	
 	// Restore position
 	fseek (fp[fc], cpos, SEEK_SET);
 	pos[fc]--;
-	
+	token = storedtoken;
 	return tmp;
 }
 
@@ -290,9 +304,13 @@
 			ParserError ("expected a token, reached end of file instead\n");
 	}
 	
-	if (strlen (c) && token.compare (c) != 0) {
+	if (strlen (c))
+		MustThis (c);
+}
+
+void ScriptReader::MustThis (const char* c) {
+	if (token.compare (c) != 0)
 		ParserError ("expected `%s`, got `%s` instead", c, token.chars());
-	}
 }
 
 void ScriptReader::ParserError (const char* message, ...) {
@@ -314,8 +332,12 @@
 		fprintf (stderr, "%s%s\n", header, message);
 }
 
-void ScriptReader::MustString () {
-	MustNext ("\"");
+// if gotquote == 1, the current token already holds the quotation mark.
+void ScriptReader::MustString (bool gotquote) {
+	if (gotquote)
+		MustThis ("\"");
+	else
+		MustNext ("\"");
 	
 	str string;
 	// Keep reading characters until we find a terminating quote.
--- a/scriptreader.h	Tue Jul 17 20:35:43 2012 +0300
+++ b/scriptreader.h	Sat Jul 28 17:41:24 2012 +0300
@@ -62,6 +62,8 @@
 	long savedpos[MAX_FILESTACK]; // filepointer cursor position
 	str token;
 	int commentmode;
+	long prevpos;
+	str prevtoken;
 	
 	// ====================================================================
 	// METHODS
@@ -73,10 +75,12 @@
 	char ReadChar ();
 	char PeekChar (int offset = 0);
 	bool Next (bool peek = false);
-	str PeekNext ();
+	void Prev ();
+	str PeekNext (int offset = 0);
 	void Seek (unsigned int n, int origin);
 	void MustNext (const char* c = "");
-	void MustString ();
+	void MustThis (const char* c);
+	void MustString (bool gotquote = false);
 	void MustNumber ();
 	void MustBool ();
 	void MustValue (int type);
@@ -89,7 +93,10 @@
 	
 	// parser.cxx:
 	void BeginParse (ObjWriter* w);
-	void ParseCommand (CommandDef* comm, ObjWriter* w);
+	DataBuffer* ParseCommand (CommandDef* comm);
+	DataBuffer* ParseExpression (int reqtype);
+	int ParseOperator ();
+	DataBuffer* ParseExprValue (int reqtype);
 	
 	// preprocessor.cxx:
 	void PreprocessDirectives ();
@@ -106,4 +113,26 @@
 	str PPReadWord (char &term);
 };
 
+enum {
+	TYPE_VOID = 0,
+	TYPE_INT,
+	TYPE_STRING,
+	TYPE_FLOAT
+};
+
+// Operators
+enum {
+	OPER_ADD,
+	OPER_SUBTRACT,
+	OPER_MULTIPLY,
+	OPER_DIVIDE,
+	OPER_MODULUS,
+	OPER_ASSIGN,
+	OPER_ASSIGNADD,
+	OPER_ASSIGNSUB,
+	OPER_ASSIGNMUL,
+	OPER_ASSIGNDIV,
+	OPER_ASSIGNMOD
+};
+
 #endif // __SCRIPTREADER_H__
\ No newline at end of file
--- a/str.cxx	Tue Jul 17 20:35:43 2012 +0300
+++ b/str.cxx	Sat Jul 28 17:41:24 2012 +0300
@@ -45,7 +45,8 @@
 #include "str.h"
 #include "common.h"
 
-#define ITERATE_STRING(u) for (unsigned int u = 0; u < strlen (text); u++)
+#define ITERATE_STRING(u) \
+	for (unsigned int u = 0; u < strlen (text); u++)
 
 // ============================================================================
 // vdynformat: Try to write to a formatted string with size bytes first, if

mercurial