src/parser.cxx

changeset 73
1ee9b312dc18
parent 72
03e4d9db3fd9
child 74
007fbadfa7f9
--- a/src/parser.cxx	Fri Jan 10 21:58:42 2014 +0200
+++ b/src/parser.cxx	Sat Jan 11 22:36:31 2014 +0200
@@ -1,792 +1,943 @@
-#include "objwriter.h"
-#include "scriptreader.h"
+/*
+	Copyright (c) 2013-2014, Santeri Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions are met:
+
+		* Redistributions of source code must retain the above copyright
+		  notice, this list of conditions and the following disclaimer.
+
+		* Redistributions in binary form must reproduce the above copyright
+		  notice, this list of conditions and the following disclaimer in the
+		  documentation and/or other materials provided with the distribution.
+
+		* Neither the name of the <organization> nor the
+		  names of its contributors may be used to endorse or promote products
+		  derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+	DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+	DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "object_writer.h"
+#include "parser.h"
 #include "events.h"
 #include "commands.h"
 #include "stringtable.h"
 #include "variables.h"
 #include "containers.h"
-
-#define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \
-	ParserError ("%s-statements may only be defined at top level!", token.chars());
+#include "lexer.h"
 
-#define MUST_NOT_TOPLEVEL if (g_CurMode == MODE_TOPLEVEL) \
-	ParserError ("%s-statements may not be defined at top level!", token.chars());
-
+#define TOKEN (string (m_lx->get_token()->string))
 #define SCOPE(n) scopestack[g_ScopeCursor - n]
 
+// TODO: make these static
 int g_NumStates = 0;
 int g_NumEvents = 0;
 parsermode_e g_CurMode = MODE_TOPLEVEL;
 string g_CurState = "";
 bool g_stateSpawnDefined = false;
 bool g_GotMainLoop = false;
-unsigned int g_ScopeCursor = 0;
-DataBuffer* g_IfExpression = null;
+int g_ScopeCursor = 0;
+data_buffer* g_IfExpression = null;
 bool g_CanElse = false;
 string* g_UndefinedLabels[MAX_MARKS];
-bool g_Neurosphere = false; // neurosphere-compat
-list<constinfo_t> g_ConstInfo;
+list<constant_info> g_ConstInfo;
+
+static botscript_parser* g_current_parser = null;
+
+// ============================================================================
+//
+botscript_parser::botscript_parser() :
+	m_lx (new lexer) {}
+
+// ============================================================================
+//
+botscript_parser::~botscript_parser()
+{
+	delete m_lx;
+}
+
+// ============================================================================
+//
+void botscript_parser::check_toplevel()
+{
+	if (g_CurMode != MODE_TOPLEVEL)
+		error ("%1-statements may only be defined at top level!", TOKEN.chars());
+}
+
+// ============================================================================
+//
+void botscript_parser::check_not_toplevel()
+{
+	if (g_CurMode == MODE_TOPLEVEL)
+		error ("%1-statements must not be defined at top level!", TOKEN.chars());
+}
 
 // ============================================================================
 // 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
+// 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::ParseBotScript (ObjWriter* w) {
+void botscript_parser::parse_botscript (string file_name, object_writer* w)
+{
+	// Lex and preprocess the file
+	m_lx->process_file (file_name);
+
 	// Zero the entire block stack first
 	for (int i = 0; i < MAX_SCOPE; i++)
-		ZERO(scopestack[i]);
-	
+		ZERO (scopestack[i]);
+
 	for (int i = 0; i < MAX_MARKS; i++)
 		g_UndefinedLabels[i] = null;
-	
-	while (Next()) {
+
+	while (m_lx->get_next())
+	{
 		// Check if else is potentically valid
-		if (token == "else" && !g_CanElse)
-			ParserError ("else without preceding if");
-		if (token != "else")
+		if (TOKEN == "else" && !g_CanElse)
+			error ("else without preceding if");
+
+		if (TOKEN != "else")
 			g_CanElse = false;
-		
-		// ============================================================
-		if (token == "state") {
-			MUST_TOPLEVEL
-			
-			MustString ();
-			
-			// State name must be a word.
-			if (token.first (" ") != token.len())
-				ParserError ("state name must be a single word, got `%s`", token.chars());
-			string statename = token;
-			
-			// stateSpawn is special - it *must* be defined. If we
-			// encountered it, then mark down that we have it.
-			if (-token == "statespawn")
-				g_stateSpawnDefined = true;
-			
-			// Must end in a colon
-			MustNext (":");
-			
-			// Write the previous state's onenter and
-			// mainloop buffers to file now
-			if (g_CurState.len())
-				w->WriteBuffers();
-			
-			w->Write (DH_STATENAME);
-			w->WriteString (statename);
-			w->Write (DH_STATEIDX);
-			w->Write (g_NumStates);
-			
-			g_NumStates++;
-			g_CurState = token;
-			g_GotMainLoop = false;
-			continue;
-		}
-		
-		// ============================================================
-		if (token == "event") {
-			MUST_TOPLEVEL
-			
-			// Event definition
-			MustString ();
-			
-			EventDef* e = FindEventByName (token);
-			if (!e)
-				ParserError ("bad event, got `%s`\n", token.chars());
-			
-			MustNext ("{");
-			
-			g_CurMode = MODE_EVENT;
-			
-			w->Write (DH_EVENT);
-			w->Write (e->number);
-			g_NumEvents++;
-			continue;
-		}
-		
-		// ============================================================
-		if (token == "mainloop") {
-			MUST_TOPLEVEL
-			MustNext ("{");
-			
-			// Mode must be set before dataheader is written here!
-			g_CurMode = MODE_MAINLOOP;
-			w->Write (DH_MAINLOOP);
-			continue;
-		}
-		
-		// ============================================================
-		if (token == "onenter" || token == "onexit") {
-			MUST_TOPLEVEL
-			bool onenter = token == "onenter";
-			MustNext ("{");
-			
-			// Mode must be set before dataheader is written here,
-			// because onenter goes to a separate buffer.
-			g_CurMode = onenter ? MODE_ONENTER : MODE_ONEXIT;
-			w->Write (onenter ? DH_ONENTER : DH_ONEXIT);
-			continue;
-		}
-		
-		// ============================================================
-		if (token == "int" || token == "str" || token == "bool") {
-			// For now, only globals are supported
-			if (g_CurMode != MODE_TOPLEVEL || g_CurState.len())
-				ParserError ("variables must only be global for now");
-			
-			type_e type =	(token == "int") ? TYPE_INT :
-							(token == "str") ? TYPE_STRING :
-							TYPE_BOOL;
-			
-			MustNext ();
-			
-			// Var name must not be a number
-			if (token.is_numeric())
-				ParserError ("variable name must not be a number");
-			
-			string varname = token;
-			ScriptVar* var = DeclareGlobalVariable (this, type, varname);
-			
-			if (!var)
-				ParserError ("declaring %s variable %s failed",
-					g_CurState.len() ? "state" : "global", varname.chars());
-			
-			MustNext (";");
-			continue;
-		}
-		
-		// ============================================================
-		// Goto
-		if (token == "goto") {
-			MUST_NOT_TOPLEVEL
-			
-			// Get the name of the label
-			MustNext ();
-			
-			// Find the mark this goto statement points to
-			unsigned int m = w->FindMark (token);
-			
-			// If not set, define it
-			if (m == MAX_MARKS) {
-				m = w->AddMark (token);
-				g_UndefinedLabels[m] = new string (token);
+
+		switch (m_lx->get_token())
+		{
+			case tk_state:
+			{
+				check_toplevel();
+				m_lx->must_get_next (tk_string);
+
+				// State name must be a word.
+				if (TOKEN.first (" ") != -1)
+					error ("state name must be a single word, got `%1`", TOKEN);
+
+				string statename = TOKEN;
+
+				// stateSpawn is special - it *must* be defined. If we
+				// encountered it, then mark down that we have it.
+				if (-TOKEN == "statespawn")
+					g_stateSpawnDefined = true;
+
+				// Must end in a colon
+				m_lx->must_get_next (tk_colon);
+
+				// write the previous state's onenter and
+				// mainloop buffers to file now
+				if (g_CurState.len())
+					w->write (Buffers();
+
+				w->write (DH_STATENAME);
+				w->write_string (statename);
+				w->write (DH_STATEIDX);
+				w->write (g_NumStates);
+
+				g_NumStates++;
+				g_CurState = TOKEN;
+				g_GotMainLoop = false;
 			}
-			
-			// Add a reference to the mark.
-			w->Write (DH_GOTO);
-			w->AddReference (m);
-			MustNext (";");
-			continue;
-		}
-		
-		// ============================================================
-		// If
-		if (token == "if") {
-			MUST_NOT_TOPLEVEL
-			PushScope ();
-			
-			// Condition
-			MustNext ("(");
-			
-			// Read the expression and write it.
-			MustNext ();
-			DataBuffer* c = ParseExpression (TYPE_INT);
-			w->WriteBuffer (c);
-			
-			MustNext (")");
-			MustNext ("{");
-			
-			// Add a mark - to here temporarily - and add a reference to it.
-			// Upon a closing brace, the mark will be adjusted.
-			unsigned int marknum = w->AddMark ("");
-			
-			// Use DH_IFNOTGOTO - if the expression is not true, we goto the mark
-			// we just defined - and this mark will be at the end of the scope block.
-			w->Write (DH_IFNOTGOTO);
-			w->AddReference (marknum);
-			
-			// Store it
-			SCOPE(0).mark1 = marknum;
-			SCOPE(0).type = SCOPETYPE_IF;
-			continue;
-		}
-		
-		if (token == "else") {
-			MUST_NOT_TOPLEVEL
-			MustNext ("{");
-			
-			// Don't use PushScope as it resets the scope
-			g_ScopeCursor++;
-			if (g_ScopeCursor >= MAX_SCOPE)
-				ParserError ("too deep scope");
-			
-			if (SCOPE(0).type != SCOPETYPE_IF)
-				ParserError ("else without preceding if");
-			
-			// Write down to jump to the end of the else statement
-			// Otherwise we have fall-throughs
-			SCOPE(0).mark2 = w->AddMark ("");
-			
-			// Instruction to jump to the end after if block is complete
-			w->Write (DH_GOTO);
-			w->AddReference (SCOPE(0).mark2);
-			
-			// Move the ifnot mark here and set type to else
-			w->MoveMark (SCOPE(0).mark1);
-			SCOPE(0).type = SCOPETYPE_ELSE;
-			continue;
-		}
-		
-		// ============================================================
-		// While
-		if (token == "while") {
-			MUST_NOT_TOPLEVEL
-			PushScope ();
-			
-			// While loops need two marks - one at the start of the loop and one at the
-			// end. The condition is checked at the very start of the loop, if it fails,
-			// we use goto to skip to the end of the loop. At the end, we loop back to
-			// the beginning with a go-to statement.
-			unsigned int mark1 = w->AddMark (""); // start
-			unsigned int mark2 = w->AddMark (""); // end
-			
-			// Condition
-			MustNext ("(");
-			MustNext ();
-			DataBuffer* expr = ParseExpression (TYPE_INT);
-			MustNext (")");
-			MustNext ("{");
-			
-			// Write condition
-			w->WriteBuffer (expr);
-			
-			// Instruction to go to the end if it fails
-			w->Write (DH_IFNOTGOTO);
-			w->AddReference (mark2);
-			
-			// Store the needed stuff
-			SCOPE(0).mark1 = mark1;
-			SCOPE(0).mark2 = mark2;
-			SCOPE(0).type = SCOPETYPE_WHILE;
-			continue;
-		}
-		
-		// ============================================================
-		// For loop
-		if (token == "for") {
-			MUST_NOT_TOPLEVEL
-			PushScope ();
-			
-			// Initializer
-			MustNext ("(");
-			MustNext ();
-			DataBuffer* init = ParseStatement (w);
-			if (!init)
-				ParserError ("bad statement for initializer of for");
-			
-			MustNext (";");
-			
-			// Condition
-			MustNext ();
-			DataBuffer* cond = ParseExpression (TYPE_INT);
-			if (!cond)
-				ParserError ("bad statement for condition of for");
-			
-			MustNext (";");
-			
-			// Incrementor
-			MustNext ();
-			DataBuffer* incr = ParseStatement (w);
-			if (!incr)
-				ParserError ("bad statement for incrementor of for");
-			
-			MustNext (")");
-			MustNext ("{");
-			
-			// First, write out the initializer
-			w->WriteBuffer (init);
-			
-			// Init two marks
-			int mark1 = w->AddMark ("");
-			int mark2 = w->AddMark ("");
-			
-			// Add the condition
-			w->WriteBuffer (cond);
-			w->Write (DH_IFNOTGOTO);
-			w->AddReference (mark2);
-			
-			// Store the marks and incrementor
-			SCOPE(0).mark1 = mark1;
-			SCOPE(0).mark2 = mark2;
-			SCOPE(0).buffer1 = incr;
-			SCOPE(0).type = SCOPETYPE_FOR;
-			continue;
-		}
-		
-		// ============================================================
-		// Do/while loop
-		if (token == "do") {
-			MUST_NOT_TOPLEVEL
-			PushScope ();
-			MustNext ("{");
-			SCOPE(0).mark1 = w->AddMark ("");
-			SCOPE(0).type = SCOPETYPE_DO;
-			continue;
-		}
-		
-		// ============================================================
-		// Switch
-		if (token == "switch") {
-			/* This goes a bit tricky. switch is structured in the
-			 * bytecode followingly:
-			 * (expression)
-			 * case a: goto casemark1
-			 * case b: goto casemark2
-			 * case c: goto casemark3
-			 * goto mark1 // jump to end if no matches
-			 * casemark1: ...
-			 * casemark2: ...
-			 * casemark3: ...
-			 * mark1: // end mark
-			 */
-			
-			MUST_NOT_TOPLEVEL
-			PushScope ();
-			MustNext ("(");
-			MustNext ();
-			w->WriteBuffer (ParseExpression (TYPE_INT));
-			MustNext (")");
-			MustNext ("{");
-			SCOPE(0).type = SCOPETYPE_SWITCH;
-			SCOPE(0).mark1 = w->AddMark (""); // end mark
-			SCOPE(0).buffer1 = null; // default header
-			continue;
-		}
-		
-		// ============================================================
-		if (token == "case") {
-			// case is only allowed inside switch
-			if (SCOPE(0).type != SCOPETYPE_SWITCH)
-				ParserError ("case label outside switch");
-			
-			// Get the literal (Zandronum does not support expressions here)
-			MustNumber ();
-			int num = atoi (token.chars ());
-			MustNext (":");
-			
-			for (int i = 0; i < MAX_CASE; i++)
-				if (SCOPE(0).casenumbers[i] == num)
-					ParserError ("multiple case %d labels in one switch", num);
-			
-			// Write down the expression and case-go-to. This builds
-			// the case tree. The closing event will write the actual
-			// blocks and move the marks appropriately.
-			//	AddSwitchCase will add the reference to the mark
-			// for the case block that this heralds, and takes care
-			// of buffering setup and stuff like that.
-			//	null the switch buffer for the case-go-to statement,
-			// we want it all under the switch, not into the case-buffers.
-			w->SwitchBuffer = null;
-			w->Write (DH_CASEGOTO);
-			w->Write (num);
-			AddSwitchCase (w, null);
-			SCOPE(0).casenumbers[SCOPE(0).casecursor] = num;
-			continue;
-		}
-		
-		if (token == "default") {
-			if (SCOPE(0).type != SCOPETYPE_SWITCH)
-				ParserError ("default label outside switch");
-			
-			if (SCOPE(0).buffer1)
-				ParserError ("multiple default labels in one switch");
-			
-			MustNext (":");
-			
-			// The default header is buffered into buffer1, since
-			// it has to be the last of the case headers
+			break;
+
+			// ============================================================
+			//
+			case tk_event:
+			{
+				check_toplevel();
+
+				// Event definition
+				m_lx->must_get_next (tk_string);
+
+				event_info* e = find_event_by_name (token_string());
+
+				if (!e)
+					error ("bad event, got `%1`\n", token_string());
+
+				m_lx->must_get_next (tk_brace_start);
+				g_CurMode = MODE_EVENT;
+				w->write (DH_EVENT);
+				w->write (e->number);
+				g_NumEvents++;
+				continue;
+			} break;
+
+			// ============================================================
+			//
+			case tk_mainloop:
+			{
+				check_toplevel();
+				m_lx->must_get_next (tk_brace_start);
+
+				// Mode must be set before dataheader is written here!
+				g_CurMode = MODE_MAINLOOP;
+				w->write (DH_MAINLOOP);
+			}
+			break;
+
+			// ============================================================
 			//
-			// Since the expression is pushed into the switch
-			// and is only popped when case succeeds, we have
-			// to pop it with DH_DROP manually if we end up in
-			// a default.
-			DataBuffer* b = new DataBuffer;
-			SCOPE(0).buffer1 = b;
-			b->Write (DH_DROP);
-			b->Write (DH_GOTO);
-			AddSwitchCase (w, b);
-			continue;
-		}
-		
-		// ============================================================
-		// Break statement.
-		if (token == "break") {
-			if (!g_ScopeCursor)
-				ParserError ("unexpected `break`");
-			
-			w->Write (DH_GOTO);
-			
-			// switch and if use mark1 for the closing point,
-			// for and while use mark2.
-			switch (SCOPE(0).type) {
-			case SCOPETYPE_IF:
-			case SCOPETYPE_SWITCH:
-				w->AddReference (SCOPE(0).mark1);
-				break;
-			case SCOPETYPE_FOR:
-			case SCOPETYPE_WHILE:
-				w->AddReference (SCOPE(0).mark2);
-				break;
-			default:
-				ParserError ("unexpected `break`");
-				break;
-			}
-			
-			MustNext (";");
-			continue;
-		}
-		
-		// ============================================================
-		// Continue
-		if (token == "continue") {
-			MustNext (";");
-			
-			int curs;
-			bool found = false;
-			
-			// Drop through the scope until we find a loop block
-			for (curs = g_ScopeCursor; curs > 0 && !found; curs--) {
-				switch (scopestack[curs].type) {
-				case SCOPETYPE_FOR:
-				case SCOPETYPE_WHILE:
-				case SCOPETYPE_DO:
-					w->Write (DH_GOTO);
-					w->AddReference (scopestack[curs].mark1);
-					found = true;
-					break;
-				default:
-					break;
-				}
-			}
-			
-			// No loop blocks
-			if (!found)
-				ParserError ("`continue`-statement not inside a loop");
-			
-			continue;
-		}
-		
-		// ============================================================
-		// Label
-		if (PeekNext() == ":") {
-			MUST_NOT_TOPLEVEL
-			
-			// want no conflicts..
-			if (IsKeyword (token))
-				ParserError ("label name `%s` conflicts with keyword\n", token.chars());
-			if (FindCommand (token))
-				ParserError ("label name `%s` conflicts with command name\n", token.chars());
-			if (FindGlobalVariable (token))
-				ParserError ("label name `%s` conflicts with variable\n", token.chars());
-			
-			// See if a mark already exists for this label
-			int mark = -1;
-			for (int i = 0; i < MAX_MARKS; i++) {
-				if (g_UndefinedLabels[i] && *g_UndefinedLabels[i] == token) {
-					mark = i;
-					w->MoveMark (i);
-					
-					// No longer undefinde
-					delete g_UndefinedLabels[i];
-					g_UndefinedLabels[i] = null;
-				}
+			case tk_onenter:
+			case tk_onexit:
+			{
+				check_toplevel();
+				bool onenter = (m_lx->get_token() == "onenter");
+				m_lx->must_get_next (tk_brace_start);
+
+				// Mode must be set before dataheader is written here,
+				// because onenter goes to a separate buffer.
+				g_CurMode = onenter ? MODE_ONENTER : MODE_ONEXIT;
+				w->write (onenter ? DH_ONENTER : DH_ONEXIT);
 			}
-			
-			// Not found in unmarked lists, define it now
-			if (mark == -1)
-				w->AddMark (token);
-			
-			MustNext (":");
-			continue;
-		}
-		
-		// ============================================================
-		if (token == "const") {
-			constinfo_t info;
-			
-			// Get the type
-			MustNext ();
-			info.type = GetTypeByName (token);
-			
-			if (info.type == TYPE_UNKNOWN || info.type == TYPE_VOID)
-				ParserError ("unknown type `%s` for constant", token.c_str());
-			
-			MustNext ();
-			info.name = token;
-			
-			MustNext ("=");
-			
-			switch (info.type) {
-			case TYPE_BOOL:
-			case TYPE_INT:
-				MustNumber (false);
-				info.val = token;
-				break;
-			case TYPE_STRING:
-				MustString ();
-				info.val = token;
-				break;
-			case TYPE_UNKNOWN:
-			case TYPE_VOID:
-				break;
+			break;
+
+			// ============================================================
+			//
+			case tk_int:
+			case tk_str:
+			case tk_void:
+			{
+				// For now, only globals are supported
+				if (g_CurMode != MODE_TOPLEVEL || g_CurState.len())
+					error ("variables must only be global for now");
+
+				type_e type =	(TOKEN == "int") ? TYPE_INT :
+								(TOKEN == "str") ? TYPE_STRING :
+								TYPE_BOOL;
+
+				m_lx->must_get_next();
+
+				// Var name must not be a number
+				if (TOKEN.is_numeric())
+					error ("variable name must not be a number");
+
+				string varname = TOKEN;
+				script_variable* var = DeclareGlobalVariable (this, type, varname);
+				m_lx->must_get_next (tk_semicolon);
 			}
-			
-			g_ConstInfo << info;
-			
-			MustNext (";");
-			continue;
-		}
-		
-		// ============================================================
-		if (token == "}") {
-			// Closing brace
-			
-			// If we're in the block stack, we're descending down from it now
-			if (g_ScopeCursor > 0) {
-				switch (SCOPE(0).type) {
-				case SCOPETYPE_IF:
-					// Adjust the closing mark.
-					w->MoveMark (SCOPE(0).mark1);
-					
-					// We're returning from if, thus else can be next
-					g_CanElse = true;
-					break;
-				case SCOPETYPE_ELSE:
-					// else instead uses mark1 for itself (so if expression
-					// fails, jump to else), mark2 means end of else
-					w->MoveMark (SCOPE(0).mark2);
-					break;
-				case SCOPETYPE_FOR:
-					// Write the incrementor at the end of the loop block
-					w->WriteBuffer (SCOPE(0).buffer1);
-					// fall-thru
-				case SCOPETYPE_WHILE:
-					// Write down the instruction to go back to the start of the loop
-					w->Write (DH_GOTO);
-					w->AddReference (SCOPE(0).mark1);
-					
-					// Move the closing mark here since we're at the end of the while loop
-					w->MoveMark (SCOPE(0).mark2);
-					break;
-				case SCOPETYPE_DO: { 
-					MustNext ("while");
-					MustNext ("(");
-					MustNext ();
-					DataBuffer* expr = ParseExpression (TYPE_INT);
-					MustNext (")");
-					MustNext (";");
-					
-					// If the condition runs true, go back to the start.
-					w->WriteBuffer (expr);
-					w->Write (DH_IFGOTO);
-					w->AddReference (SCOPE(0).mark1);
-					break;
+			break;
+
+			// ============================================================
+			//
+			case tk_goto:
+			{
+				check_not_toplevel();
+
+				// Get the name of the label
+				m_lx->must_get_next();
+
+				// Find the mark this goto statement points to
+				int m = w->find_byte_mark (TOKEN);
+
+				// If not set, define it
+				if (m == MAX_MARKS)
+				{
+					m = w->add_mark (TOKEN);
+					g_UndefinedLabels[m] = new string (TOKEN);
 				}
-				case SCOPETYPE_SWITCH: {
-					// Switch closes. Move down to the record buffer of
-					// the lower block.
-					if (SCOPE(1).casecursor != -1)
-						w->SwitchBuffer = SCOPE(1).casebuffers[SCOPE(1).casecursor];
-					else
-						w->SwitchBuffer = null;
-					
-					// If there was a default in the switch, write its header down now.
-					// If not, write instruction to jump to the end of switch after
-					// the headers (thus won't fall-through if no case matched)
-					if (SCOPE(0).buffer1)
-						w->WriteBuffer (SCOPE(0).buffer1);
-					else {
-						w->Write (DH_DROP);
-						w->Write (DH_GOTO);
-						w->AddReference (SCOPE(0).mark1);
-					}
-					
-					// Go through all of the buffers we
-					// recorded down and write them.
-					for (unsigned int u = 0; u < MAX_CASE; u++) {
-						if (!SCOPE(0).casebuffers[u])
-							continue;
-						
-						w->MoveMark (SCOPE(0).casemarks[u]);
-						w->WriteBuffer (SCOPE(0).casebuffers[u]);
-					}
-					
-					// Move the closing mark here
-					w->MoveMark (SCOPE(0).mark1);
-					break;
-				}
-				case SCOPETYPE_UNKNOWN:
-					break;
-				}
-				
-				// Descend down the stack
-				g_ScopeCursor--;
+
+				// Add a reference to the mark.
+				w->write (DH_GOTO);
+				w->add_reference (m);
+				m_lx->must_get_next (tk_semicolon);
 				continue;
 			}
-			
-			int dataheader =	(g_CurMode == MODE_EVENT) ? DH_ENDEVENT :
-						(g_CurMode == MODE_MAINLOOP) ? DH_ENDMAINLOOP :
-						(g_CurMode == MODE_ONENTER) ? DH_ENDONENTER :
-						(g_CurMode == MODE_ONEXIT) ? DH_ENDONEXIT : -1;
-			
-			if (dataheader == -1)
-				ParserError ("unexpected `}`");
-			
-			// Data header must be written before mode is changed because
-			// 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;
-			
-			if (PeekNext() == ";")
-				MustNext (";");
-			continue;
+
+			// ============================================================
+			//
+			case tk_if:
+			{
+				check_not_toplevel();
+				push_scope();
+
+				// Condition
+				m_lx->must_get_next (tk_paren_start);
+
+				// Read the expression and write it.
+				m_lx->must_get_next();
+				data_buffer* c = parse_expression (TYPE_INT);
+				w->write_buffer (c);
+
+				m_lx->must_get_next (tk_paren_end);
+				m_lx->must_get_next (tk_brace_start);
+
+				// Add a mark - to here temporarily - and add a reference to it.
+				// Upon a closing brace, the mark will be adjusted.
+				int marknum = w->add_mark ("");
+
+				// Use DH_IFNOTGOTO - if the expression is not true, we goto the mark
+				// we just defined - and this mark will be at the end of the scope block.
+				w->write (DH_IFNOTGOTO);
+				w->add_reference (marknum);
+
+				// Store it
+				SCOPE (0).mark1 = marknum;
+				SCOPE (0).type = SCOPETYPE_IF;
+			} break;
+
+			// ============================================================
+			//
+			case tk_else:
+			{
+				check_not_toplevel();
+				m_lx->must_get_next (tk_brace_start);
+
+				// Don't use PushScope as it resets the scope
+				g_ScopeCursor++;
+
+				if (g_ScopeCursor >= MAX_SCOPE)
+					error ("too deep scope");
+
+				if (SCOPE (0).type != SCOPETYPE_IF)
+					error ("else without preceding if");
+
+				// write down to jump to the end of the else statement
+				// Otherwise we have fall-throughs
+				SCOPE (0).mark2 = w->add_mark ("");
+
+				// Instruction to jump to the end after if block is complete
+				w->write (DH_GOTO);
+				w->add_reference (SCOPE (0).mark2);
+
+				// Move the ifnot mark here and set type to else
+				w->move_mark (SCOPE (0).mark1);
+				SCOPE (0).type = SCOPETYPE_ELSE;
+			}
+			break;
+
+			// ============================================================
+			//
+			case tk_while:
+			{
+				check_not_toplevel();
+				push_scope();
+
+				// While loops need two marks - one at the start of the loop and one at the
+				// end. The condition is checked at the very start of the loop, if it fails,
+				// we use goto to skip to the end of the loop. At the end, we loop back to
+				// the beginning with a go-to statement.
+				int mark1 = w->add_mark (""); // start
+				int mark2 = w->add_mark (""); // end
+
+				// Condition
+				m_lx->must_get_next (tk_paren_start);
+				m_lx->must_get_next();
+				data_buffer* expr = parse_expression (TYPE_INT);
+				m_lx->must_get_next (tk_paren_end);
+				m_lx->must_get_next (tk_brace_start);
+
+				// write condition
+				w->write_buffer (expr);
+
+				// Instruction to go to the end if it fails
+				w->write (DH_IFNOTGOTO);
+				w->add_reference (mark2);
+
+				// Store the needed stuff
+				SCOPE (0).mark1 = mark1;
+				SCOPE (0).mark2 = mark2;
+				SCOPE (0).type = SCOPETYPE_WHILE;
+			}
+			break;
+
+			// ============================================================
+			//
+			case tk_for:
+			{
+				check_not_toplevel();
+				push_scope();
+
+				// Initializer
+				m_lx->must_get_next (tk_paren_start);
+				m_lx->must_get_next();
+				data_buffer* init = parse_statement (w);
+
+				if (!init)
+					error ("bad statement for initializer of for");
+
+				m_lx->must_get_next (tk_semicolon);
+
+				// Condition
+				m_lx->must_get_next();
+				data_buffer* cond = parse_expression (TYPE_INT);
+
+				if (!cond)
+					error ("bad statement for condition of for");
+
+				m_lx->must_get_next (tk_semicolon);
+
+				// Incrementor
+				m_lx->must_get_next();
+				data_buffer* incr = parse_statement (w);
+
+				if (!incr)
+					error ("bad statement for incrementor of for");
+
+				m_lx->must_get_next (tk_paren_end);
+				m_lx->must_get_next (tk_brace_start);
+
+				// First, write out the initializer
+				w->write_buffer (init);
+
+				// Init two marks
+				int mark1 = w->add_mark ("");
+				int mark2 = w->add_mark ("");
+
+				// Add the condition
+				w->write_buffer (cond);
+				w->write (DH_IFNOTGOTO);
+				w->add_reference (mark2);
+
+				// Store the marks and incrementor
+				SCOPE (0).mark1 = mark1;
+				SCOPE (0).mark2 = mark2;
+				SCOPE (0).buffer1 = incr;
+				SCOPE (0).type = SCOPETYPE_FOR;
+			}
+			break;
+
+			// ============================================================
+			//
+			case tk_do:
+			{
+				check_not_toplevel();
+				push_scope();
+				m_lx->must_get_next (tk_brace_start);
+				SCOPE (0).mark1 = w->add_mark ("");
+				SCOPE (0).type = SCOPETYPE_DO;
+			}
+			break;
+
+			// ============================================================
+			//
+			case tk_switch:
+			{
+				// This gets a bit tricky. switch is structured in the
+				// bytecode followingly:
+				//
+				// (expression)
+				// case a: goto casemark1
+				// case b: goto casemark2
+				// case c: goto casemark3
+				// goto mark1 // jump to end if no matches
+				// casemark1: ...
+				// casemark2: ...
+				// casemark3: ...
+				// mark1: // end mark
+
+				check_not_toplevel();
+				push_scope();
+				m_lx->must_get_next (tk_paren_start);
+				m_lx->must_get_next();
+				w->write_buffer (parse_expression (TYPE_INT));
+				m_lx->must_get_next (tk_paren_end);
+				m_lx->must_get_next (tk_brace_start);
+				SCOPE (0).type = SCOPETYPE_SWITCH;
+				SCOPE (0).mark1 = w->add_mark (""); // end mark
+				SCOPE (0).buffer1 = null; // default header
+			}
+			break;
+
+			// ============================================================
+			//
+			case tk_case:
+			{
+				// case is only allowed inside switch
+				if (SCOPE (0).type != SCOPETYPE_SWITCH)
+					error ("case label outside switch");
+
+				// Get the literal (Zandronum does not support expressions here)
+				m_lx->must_get_next (tk_number);
+				int num = m_lx->get_token()->text.to_long();
+				m_lx->must_get_next (tk_colon);
+
+				for (int i = 0; i < MAX_CASE; i++)
+					if (SCOPE (0).casenumbers[i] == num)
+						error ("multiple case %d labels in one switch", num);
+
+				// write down the expression and case-go-to. This builds
+				// the case tree. The closing event will write the actual
+				// blocks and move the marks appropriately.
+				//	 AddSwitchCase will add the reference to the mark
+				// for the case block that this heralds, and takes care
+				// of buffering setup and stuff like that.
+				//	 null the switch buffer for the case-go-to statement,
+				// we want it all under the switch, not into the case-buffers.
+				w->SwitchBuffer = null;
+				w->write (DH_CASEGOTO);
+				w->write (num);
+				add_switch_case (w, null);
+				SCOPE (0).casenumbers[SCOPE (0).casecursor] = num;
+			}
+			break;
+
+			// ============================================================
+			//
+			case tk_default:
+			{
+				if (SCOPE (0).type != SCOPETYPE_SWITCH)
+					error ("default label outside switch");
+
+				if (SCOPE (0).buffer1)
+					error ("multiple default labels in one switch");
+
+				m_lx->must_get_next (tk_colon);
+
+				// The default header is buffered into buffer1, since
+				// it has to be the last of the case headers
+				//
+				// Since the expression is pushed into the switch
+				// and is only popped when case succeeds, we have
+				// to pop it with DH_DROP manually if we end up in
+				// a default.
+				data_buffer* b = new data_buffer;
+				SCOPE (0).buffer1 = b;
+				b->write (DH_DROP);
+				b->write (DH_GOTO);
+				add_switch_case (w, b);
+			}
+			break;
+
+			// ============================================================
+			//
+			case tk_break:
+			{
+				if (!g_ScopeCursor)
+					error ("unexpected `break`");
+
+				w->write (DH_GOTO);
+
+				// switch and if use mark1 for the closing point,
+				// for and while use mark2.
+				switch (SCOPE (0).type)
+				{
+					case SCOPETYPE_IF:
+					case SCOPETYPE_SWITCH:
+					{
+						w->add_reference (SCOPE (0).mark1);
+					} break;
+
+					case SCOPETYPE_FOR:
+					case SCOPETYPE_WHILE:
+					{
+						w->add_reference (SCOPE (0).mark2);
+					} break;
+
+					default:
+					{
+						error ("unexpected `break`");
+					} break;
+				}
+
+				m_lx->must_get_next (tk_semicolon);
+			}
+			break;
+
+			// ============================================================
+			//
+			case tk_continue:
+			{
+				m_lx->must_get_next (tk_semicolon);
+
+				int curs;
+				bool found = false;
+
+				// Fall through the scope until we find a loop block
+				for (curs = g_ScopeCursor; curs > 0 && !found; curs--)
+				{
+					switch (scopestack[curs].type)
+					{
+						case SCOPETYPE_FOR:
+						case SCOPETYPE_WHILE:
+						case SCOPETYPE_DO:
+						{
+							w->write (DH_GOTO);
+							w->add_reference (scopestack[curs].mark1);
+							found = true;
+						} break;
+
+						default:
+							break;
+					}
+				}
+
+				// No loop blocks
+				if (!found)
+					error ("`continue`-statement not inside a loop");
+			}
+			break;
+
+			case tk_brace_end:
+			{
+				// Closing brace
+				// If we're in the block stack, we're descending down from it now
+				if (g_ScopeCursor > 0)
+				{
+					switch (SCOPE (0).type)
+					{
+						case SCOPETYPE_IF:
+							// Adjust the closing mark.
+							w->move_mark (SCOPE (0).mark1);
+
+							// We're returning from if, thus else can be next
+							g_CanElse = true;
+							break;
+
+						case SCOPETYPE_ELSE:
+							// else instead uses mark1 for itself (so if expression
+							// fails, jump to else), mark2 means end of else
+							w->move_mark (SCOPE (0).mark2);
+							break;
+
+						case SCOPETYPE_FOR:
+							// write the incrementor at the end of the loop block
+							w->write_buffer (SCOPE (0).buffer1);
+
+							// fall-thru
+						case SCOPETYPE_WHILE:
+							// write down the instruction to go back to the start of the loop
+							w->write (DH_GOTO);
+							w->add_reference (SCOPE (0).mark1);
+
+							// Move the closing mark here since we're at the end of the while loop
+							w->move_mark (SCOPE (0).mark2);
+							break;
+
+						case SCOPETYPE_DO:
+						{
+							must_get_next (tk_while);
+							m_lx->must_get_next (tk_paren_start);
+							m_lx->must_get_next();
+							data_buffer* expr = parse_expression (TYPE_INT);
+							m_lx->must_get_next (tk_paren_end);
+							m_lx->must_get_next (tk_semicolon);
+
+							// If the condition runs true, go back to the start.
+							w->write_buffer (expr);
+							w->write (DH_IFGOTO);
+							w->add_reference (SCOPE (0).mark1);
+							break;
+						}
+
+						case SCOPETYPE_SWITCH:
+						{
+							// Switch closes. Move down to the record buffer of
+							// the lower block.
+							if (SCOPE (1).casecursor != -1)
+								w->SwitchBuffer = SCOPE (1).casebuffers[SCOPE (1).casecursor];
+							else
+								w->SwitchBuffer = null;
+
+							// If there was a default in the switch, write its header down now.
+							// If not, write instruction to jump to the end of switch after
+							// the headers (thus won't fall-through if no case matched)
+							if (SCOPE (0).buffer1)
+								w->write_buffer (SCOPE (0).buffer1);
+										else
+								{
+									w->write (DH_DROP);
+									w->write (DH_GOTO);
+									w->add_reference (SCOPE (0).mark1);
+								}
+
+							// Go through all of the buffers we
+							// recorded down and write them.
+							for (int u = 0; u < MAX_CASE; u++)
+							{
+								if (!SCOPE (0).casebuffers[u])
+									continue;
+
+								w->move_mark (SCOPE (0).casemarks[u]);
+								w->write_buffer (SCOPE (0).casebuffers[u]);
+							}
+
+							// Move the closing mark here
+							w->move_mark (SCOPE (0).mark1);
+							break;
+						}
+
+						case SCOPETYPE_UNKNOWN:
+							break;
+					}
+
+					// Descend down the stack
+					g_ScopeCursor--;
+					continue;
+				}
+
+				int dataheader =	(g_CurMode == MODE_EVENT) ? DH_ENDEVENT :
+									(g_CurMode == MODE_MAINLOOP) ? DH_ENDMAINLOOP :
+									(g_CurMode == MODE_ONENTER) ? DH_ENDONENTER :
+									(g_CurMode == MODE_ONEXIT) ? DH_ENDONEXIT : -1;
+
+				if (dataheader == -1)
+					error ("unexpected `}`");
+
+				// Data header must be written before mode is changed because
+				// 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;
+
+				if (PeekNext() == ";")
+					m_lx->must_get_next (tk_semicolon);
+			}
+			break;
+
+			// ============================================================
+			case tk_const:
+			{
+				constant_info info;
+
+				// Get the type
+				m_lx->must_get_next();
+				info.type = GetTypeByName (TOKEN);
+
+				if (info.type == TYPE_UNKNOWN || info.type == TYPE_VOID)
+					error ("unknown type `%s` for constant", TOKEN.c_str());
+
+				m_lx->must_get_next();
+				info.name = TOKEN;
+
+				m_lx->must_get_next (tk_assign);
+
+				switch (info.type)
+				{
+					case TYPE_BOOL:
+					case TYPE_INT:
+					{
+						m_lx->must_get_next (tk_number);
+						info.val = m_lx->get_token()->text.to_long();
+					} break;
+
+					case TYPE_STRING:
+					{
+						m_lx->must_get_next (tk_string);
+						info.val = m_lx->get_token()->text;
+					} break;
+
+					case TYPE_UNKNOWN:
+					case TYPE_VOID:
+						break;
+				}
+
+				g_ConstInfo << info;
+
+				m_lx->must_get_next (tk_semicolon);
+				continue;
+			}
+
+			default:
+			{
+				// ============================================================
+				// Label
+				lexer::token* next;
+				if (m_lx->get_token() == tk_symbol &&
+					m_lx->peek_next (next) &&
+					next->type == tk_colon)
+				{
+					check_not_toplevel();
+					string label_name = token_string();
+
+					// want no conflicts..
+					if (FindCommand (label_name))
+						error ("label name `%s` conflicts with command name\n", label_name);
+
+					if (FindGlobalVariable (label_name))
+						error ("label name `%s` conflicts with variable\n", label_name);
+
+					// See if a mark already exists for this label
+					int mark = -1;
+
+					for (int i = 0; i < MAX_MARKS; i++)
+					{
+						if (g_UndefinedLabels[i] && *g_UndefinedLabels[i] == label_name)
+						{
+							mark = i;
+							w->move_mark (i);
+
+							// No longer undefinde
+							delete g_UndefinedLabels[i];
+							g_UndefinedLabels[i] = null;
+						}
+					}
+
+					// Not found in unmarked lists, define it now
+					if (mark == -1)
+						w->add_mark (label_name);
+
+					m_lx->must_get_next (tk_colon);
+					continue;
+				}
+
+				// Check if it's a command
+				CommandDef* comm = FindCommand (TOKEN);
+
+				if (comm)
+				{
+					w->get_current_buffer()->merge (ParseCommand (comm));
+					m_lx->must_get_next (tk_semicolon);
+					continue;
+				}
+
+				// ============================================================
+				// If nothing else, parse it as a statement
+				data_buffer* b = parse_statement (w);
+
+				if (!b)
+					error ("unknown TOKEN `%s`", TOKEN.chars());
+
+				w->write_buffer (b);
+				m_lx->must_get_next (tk_semicolon);
+			}
+			break;
 		}
-		
-		// Check if it's a command
-		CommandDef* comm = FindCommand (token);
-		if (comm) {
-			w->GetCurrentBuffer()->Merge (ParseCommand (comm));
-			MustNext (";");
-			continue;
-		}
-		
-		// ============================================================
-		// If nothing else, parse it as a statement
-		DataBuffer* b = ParseStatement (w);
-		if (!b)
-			ParserError ("unknown token `%s`", token.chars());
-		
-		w->WriteBuffer (b);
-		MustNext (";");
 	}
-	
+
 	// ===============================================================================
 	// Script file ended. Do some last checks and write the last things to main buffer
 	if (g_CurMode != MODE_TOPLEVEL)
-		ParserError ("script did not end at top level; did you forget a `}`?");
-	
+		error ("script did not end at top level; a `}` is missing somewhere");
+
 	// stateSpawn must be defined!
 	if (!g_stateSpawnDefined)
-		ParserError ("script must have a state named `stateSpawn`!");
-	
+		error ("script must have a state named `stateSpawn`!");
+
 	for (int i = 0; i < MAX_MARKS; i++)
 		if (g_UndefinedLabels[i])
-			ParserError ("label `%s` is referenced via `goto` but isn't defined\n", g_UndefinedLabels[i]->chars());
-	
+			error ("label `%s` is referenced via `goto` but isn't defined\n", g_UndefinedLabels[i]->chars());
+
 	// Dump the last state's onenter and mainloop
-	w->WriteBuffers ();
-	
+	w->write_member_buffers();
+
 	// String table
-	w->WriteStringTable ();
+	w->write_string_table();
 }
 
 // ============================================================================
 // Parses a command call
-DataBuffer* ScriptReader::ParseCommand (CommandDef* comm) {
-	DataBuffer* r = new DataBuffer(64);
+data_buffer* botscript_parser::ParseCommand (CommandDef* comm)
+{
+	data_buffer* r = new data_buffer (64);
+
 	if (g_CurMode == MODE_TOPLEVEL)
-		ParserError ("command call at top level");
-	
-	MustNext ("(");
-	MustNext ();
-	
+		error ("command call at top level");
+
+	m_lx->must_get_next (tk_paren_start);
+	m_lx->must_get_next();
+
 	int curarg = 0;
-	while (1) {
-		if (token == ")") {
+
+	while (1)
+	{
+		if (m_lx->get_token() == tk_paren_end)
+		{
 			if (curarg < comm->numargs)
-				ParserError ("too few arguments passed to %s\n\tprototype: %s",
+				error ("too few arguments passed to %s\n\tprototype: %s",
 					comm->name.chars(), GetCommandPrototype (comm).chars());
+
 			break;
 			curarg++;
 		}
-		
+
 		if (curarg >= comm->maxargs)
-			ParserError ("too many arguments passed to %s\n\tprototype: %s",
+			error ("too many arguments passed to %s\n\tprototype: %s",
 				comm->name.chars(), GetCommandPrototype (comm).chars());
-		
-		r->Merge (ParseExpression (comm->argtypes[curarg]));
-		MustNext ();
-		
-		if (curarg < comm->numargs - 1) {
-			MustThis (",");
-			MustNext ();
-		} else if (curarg < comm->maxargs - 1) {
+
+		r->merge (parse_expression (comm->argtypes[curarg]));
+		m_lx->must_get_next();
+
+		if (curarg < comm->numargs - 1)
+		{
+			m_lx->must_be (tk_comma);
+			m_lx->must_get_next();
+		}
+		else if (curarg < comm->maxargs - 1)
+		{
 			// Can continue, but can terminate as well.
-			if (token == ")") {
+			if (m_lx->get_token() == tk_paren_end)
+			{
 				curarg++;
 				break;
-			} else {
-				MustThis (",");
-				MustNext ();
+			}
+			else
+			{
+				m_lx->must_be (tk_comma);
+				m_lx->must_get_next();
 			}
 		}
-		
+
 		curarg++;
 	}
-	
+
 	// If the script skipped any optional arguments, fill in defaults.
-	while (curarg < comm->maxargs) {
-		r->Write (DH_PUSHNUMBER);
-		r->Write (comm->defvals[curarg]);
+	while (curarg < comm->maxargs)
+	{
+		r->write (DH_PUSHNUMBER);
+		r->write (comm->defvals[curarg]);
 		curarg++;
 	}
-	
-	r->Write (DH_COMMAND);
-	r->Write (comm->number);
-	r->Write (comm->maxargs);
-	
+
+	r->write (DH_COMMAND);
+	r->write (comm->number);
+	r->write (comm->maxargs);
+
 	return r;
 }
 
 // ============================================================================
 // Is the given operator an assignment operator?
-static bool IsAssignmentOperator (int oper) {
-	switch (oper) {
-	case OPER_ASSIGNADD:
-	case OPER_ASSIGNSUB:
-	case OPER_ASSIGNMUL:
-	case OPER_ASSIGNDIV:
-	case OPER_ASSIGNMOD:
-	case OPER_ASSIGNLEFTSHIFT:
-	case OPER_ASSIGNRIGHTSHIFT:
-	case OPER_ASSIGN:
-		return true;
+static bool is_assignment_operator (int oper)
+{
+	switch (oper)
+	{
+		case OPER_ASSIGNADD:
+		case OPER_ASSIGNSUB:
+		case OPER_ASSIGNMUL:
+		case OPER_ASSIGNDIV:
+		case OPER_ASSIGNMOD:
+		case OPER_ASSIGNLEFTSHIFT:
+		case OPER_ASSIGNRIGHTSHIFT:
+		case OPER_ASSIGN:
+			return true;
 	}
+
 	return false;
 }
 
 // ============================================================================
 // Finds an operator's corresponding dataheader
-static word DataHeaderByOperator (ScriptVar* var, int oper) {
-	if (IsAssignmentOperator (oper)) {
+static word get_data_header_by_operator (script_variable* var, int oper)
+{
+	if (is_assignment_operator (oper))
+	{
 		if (!var)
 			error ("operator %d requires left operand to be a variable\n", oper);
-		
+
 		// TODO: At the moment, vars only are global
 		// OPER_ASSIGNLEFTSHIFT and OPER_ASSIGNRIGHTSHIFT do not
 		// have data headers, instead they are expanded out in
 		// the operator parser
-		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_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) {
+
+	switch (oper)
+	{
 	case OPER_ADD: return DH_ADD;
 	case OPER_SUBTRACT: return DH_SUBTRACT;
 	case OPER_MULTIPLY: return DH_MULTIPLY;
@@ -806,100 +957,109 @@
 	case OPER_BITWISEEOR: return DH_EORBITWISE;
 	case OPER_BITWISEAND: return DH_ANDBITWISE;
 	}
-	
+
 	error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper);
 	return 0;
 }
 
 // ============================================================================
 // Parses an expression, potentially recursively
-DataBuffer* ScriptReader::ParseExpression (type_e reqtype) {
-	DataBuffer* retbuf = new DataBuffer (64);
-	
+data_buffer* botscript_parser::parse_expression (type_e reqtype)
+{
+	data_buffer* retbuf = new data_buffer (64);
+
 	// Parse first operand
-	retbuf->Merge (ParseExprValue (reqtype));
-	
+	retbuf->merge (parse_expr_value (reqtype));
+
 	// Parse any and all operators we get
 	int oper;
-	while ((oper = ParseOperator (true)) != -1) {
+
+	while ( (oper = parse_operator (true)) != -1)
+	{
 		// We peeked the operator, move forward now
-		Next ();
-		
+		Next();
+
 		// Can't be an assignement operator, those belong in assignments.
-		if (IsAssignmentOperator (oper))
-			ParserError ("assignment operator inside expression");
-		
+		if (is_assignment_operator (oper))
+			error ("assignment operator inside expression");
+
 		// Parse the right operand.
-		MustNext ();
-		DataBuffer* rb = ParseExprValue (reqtype);
-		
-		if (oper == OPER_TERNARY) {
+		m_lx->must_get_next();
+		data_buffer* rb = parse_expr_value (reqtype);
+
+		if (oper == OPER_TERNARY)
+		{
 			// Ternary operator requires - naturally - a third operand.
-			MustNext (":");
-			MustNext ();
-			DataBuffer* tb = ParseExprValue (reqtype);
-			
+			m_lx->must_get_next (tk_colon);
+			m_lx->must_get_next();
+			data_buffer* tb = parse_expr_value (reqtype);
+
 			// It also is handled differently: there isn't a dataheader for ternary
 			// operator. Instead, we abuse PUSHNUMBER and IFNOTGOTO for this.
 			// Behold, big block of writing madness! :P
-			int mark1 = retbuf->AddMark (""); // start of "else" case
-			int mark2 = retbuf->AddMark (""); // end of expression
-			retbuf->Write (DH_IFNOTGOTO); // if the first operand (condition)
-			retbuf->AddMarkReference (mark1); // didn't eval true, jump into mark1
-			retbuf->Merge (rb); // otherwise, perform second operand (true case)
-			retbuf->Write (DH_GOTO); // afterwards, jump to the end, which is
-			retbuf->AddMarkReference (mark2); // marked by mark2.
-			retbuf->MoveMark (mark1); // move mark1 at the end of the true case
-			retbuf->Merge (tb); // perform third operand (false case)
-			retbuf->MoveMark (mark2); // move the ending mark2 here
-		} else {
-			// Write to buffer
-			retbuf->Merge (rb);
-			retbuf->Write (DataHeaderByOperator (null, oper));
+			int mark1 = retbuf->add_mark (""); // start of "else" case
+			int mark2 = retbuf->add_mark (""); // end of expression
+			retbuf->write (DH_IFNOTGOTO); // if the first operand (condition)
+			retbuf->add_reference (mark1); // didn't eval true, jump into mark1
+			retbuf->merge (rb); // otherwise, perform second operand (true case)
+			retbuf->write (DH_GOTO); // afterwards, jump to the end, which is
+			retbuf->add_reference (mark2); // marked by mark2.
+			retbuf->move_mark (mark1); // move mark1 at the end of the true case
+			retbuf->merge (tb); // perform third operand (false case)
+			retbuf->move_mark (mark2); // move the ending mark2 here
+		}
+		else
+		{
+			// write to buffer
+			retbuf->merge (rb);
+			retbuf->write (get_data_header_by_operator (null, oper));
 		}
 	}
-	
+
 	return retbuf;
 }
 
 // ============================================================================
 // Parses an operator string. Returns the operator number code.
 #define ISNEXT(C) (PeekNext (peek ? 1 : 0) == C)
-int ScriptReader::ParseOperator (bool peek) {
+int botscript_parser::parse_operator (bool peek)
+{
 	string oper;
+
 	if (peek)
-		oper += PeekNext ();
+		oper += PeekNext();
 	else
-		oper += token;
-	
+		oper += TOKEN;
+
 	if (-oper == "strlen")
 		return OPER_STRLEN;
-	
+
 	// Check one-char operators
 	bool equalsnext = ISNEXT ("=");
-	
+
 	int o =	(oper == "=" && !equalsnext) ? OPER_ASSIGN :
-		(oper == ">" && !equalsnext && !ISNEXT (">")) ? OPER_GREATERTHAN :
-		(oper == "<" && !equalsnext && !ISNEXT ("<")) ? OPER_LESSTHAN :
-		(oper == "&" && !ISNEXT ("&")) ? OPER_BITWISEAND :
-		(oper == "|" && !ISNEXT ("|")) ? OPER_BITWISEOR :
-		(oper == "+" && !equalsnext) ? OPER_ADD :
-		(oper == "-" && !equalsnext) ? OPER_SUBTRACT :
-		(oper == "*" && !equalsnext) ? OPER_MULTIPLY :
-		(oper == "/" && !equalsnext) ? OPER_DIVIDE :
-		(oper == "%" && !equalsnext) ? OPER_MODULUS :
-		(oper == "^") ? OPER_BITWISEEOR :
-		(oper == "?") ? OPER_TERNARY :
-		-1;
-	
-	if (o != -1) {
+			(oper == ">" && !equalsnext && !ISNEXT (">")) ? OPER_GREATERTHAN :
+			(oper == "<" && !equalsnext && !ISNEXT ("<")) ? OPER_LESSTHAN :
+			(oper == "&" && !ISNEXT ("&")) ? OPER_BITWISEAND :
+			(oper == "|" && !ISNEXT ("|")) ? OPER_BITWISEOR :
+			(oper == "+" && !equalsnext) ? OPER_ADD :
+			(oper == "-" && !equalsnext) ? OPER_SUBTRACT :
+			(oper == "*" && !equalsnext) ? OPER_MULTIPLY :
+			(oper == "/" && !equalsnext) ? OPER_DIVIDE :
+			(oper == "%" && !equalsnext) ? OPER_MODULUS :
+			(oper == "^") ? OPER_BITWISEEOR :
+			(oper == "?") ? OPER_TERNARY :
+			-1;
+
+	if (o != -1)
+	{
 		return o;
 	}
-	
+
 	// Two-char operators
 	oper += PeekNext (peek ? 1 : 0);
 	equalsnext = PeekNext (peek ? 2 : 1) == ("=");
-	
+
 	o =	(oper == "+=") ? OPER_ASSIGNADD :
 		(oper == "-=") ? OPER_ASSIGNSUB :
 		(oper == "*=") ? OPER_ASSIGNMUL :
@@ -914,39 +1074,44 @@
 		(oper == "<<" && !equalsnext) ? OPER_LEFTSHIFT :
 		(oper == ">>" && !equalsnext) ? OPER_RIGHTSHIFT :
 		-1;
-	
-	if (o != -1) {
-		MustNext ();
+
+	if (o != -1)
+	{
+		m_lx->must_get_next();
 		return o;
 	}
-	
+
 	// Three-char opers
 	oper += PeekNext (peek ? 2 : 1);
 	o =	oper == "<<=" ? OPER_ASSIGNLEFTSHIFT :
 		oper == ">>=" ? OPER_ASSIGNRIGHTSHIFT :
 		-1;
-	
-	if (o != -1) {
-		MustNext ();
-		MustNext ();
+
+	if (o != -1)
+	{
+		m_lx->must_get_next();
+		m_lx->must_get_next();
 	}
-	
+
 	return o;
 }
 
 // ============================================================================
-string ScriptReader::ParseFloat () {
-	MustNumber (true);
-	string floatstring = token;
-	
+string botscript_parser::parse_float()
+{
+	m_lx->must_be (tk_number);
+	string floatstring = TOKEN;
+	lexer::token* tok;
+
 	// Go after the decimal point
-	if (PeekNext () == ".") {
-		Next (".");
-		MustNumber (false);
+	if (m_lx->peek_next (tok) && tok->type == tk_dot)
+	{
+		m_lx->skip();
+		m_lx->must_get_next (tk_number);
 		floatstring += ".";
-		floatstring += token;
+		floatstring += token_string();
 	}
-	
+
 	return floatstring;
 }
 
@@ -954,204 +1119,264 @@
 // 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 (type_e reqtype) {
-	DataBuffer* b = new DataBuffer(16);
-	
-	ScriptVar* g;
-	
+data_buffer* botscript_parser::parse_expr_value (type_e reqtype)
+{
+	data_buffer* b = new data_buffer (16);
+
+	script_variable* g;
+
 	// Prefixing "!" means negation.
-	bool negate = (token == "!");
+	bool negate = (m_lx->get_token() == tk_exclamation_mark);
+
 	if (negate) // Jump past the "!"
-		Next ();
-	
+		m_lx->skip();
+
 	// Handle strlen
-	if (token == "strlen") {
-		MustNext ("(");
-		MustNext ();
-		
-		// By this token we should get a string constant.
-		constinfo_t* constant = FindConstant (token);
+	if (TOKEN == "strlen")
+	{
+		m_lx->must_get_next (tk_paren_start);
+		m_lx->must_get_next();
+
+		// By this TOKEN we should get a string constant.
+		constant_info* constant = find_constant (TOKEN);
+
 		if (!constant || constant->type != TYPE_STRING)
-			ParserError ("strlen only works with const str");
-		
+			error ("strlen only works with const str");
+
 		if (reqtype != TYPE_INT)
-			ParserError ("strlen returns int but %s is expected\n", GetTypeName (reqtype).c_str());
-		
-		b->Write (DH_PUSHNUMBER);
-		b->Write (constant->val.len ());
-		
-		MustNext (")");
-	} else if (token == "(") {
+			error ("strlen returns int but %s is expected\n", GetTypeName (reqtype).c_str());
+
+		b->write (DH_PUSHNUMBER);
+		b->write (constant->val.len());
+
+		m_lx->must_get_next (tk_paren_end);
+	}
+	else if (TOKEN == "(")
+	{
 		// Expression
-		MustNext ();
-		DataBuffer* c = ParseExpression (reqtype);
-		b->Merge (c);
-		MustNext (")");
-	} else if (CommandDef* comm = FindCommand (token)) {
+		m_lx->must_get_next();
+		data_buffer* c = parse_expression (reqtype);
+		b->merge (c);
+		m_lx->must_get_next (tk_paren_end);
+	}
+	else if (CommandDef* comm = FindCommand (TOKEN))
+	{
 		delete b;
-		
+
 		// Command
 		if (reqtype && comm->returnvalue != reqtype)
-			ParserError ("%s returns an incompatible data type", comm->name.chars());
+			error ("%s returns an incompatible data type", comm->name.chars());
+
 		b = ParseCommand (comm);
-	} else if (constinfo_t* constant = FindConstant (token)) {
+	}
+	else if (constant_info* constant = find_constant (TOKEN))
+	{
 		// Type check
 		if (reqtype != constant->type)
-			ParserError ("constant `%s` is %s, expression requires %s\n",
+			error ("constant `%s` is %s, expression requires %s\n",
 				constant->name.c_str(), GetTypeName (constant->type).c_str(),
 				GetTypeName (reqtype).c_str());
-		
-		switch (constant->type) {
+
+		switch (constant->type)
+		{
+			case TYPE_BOOL:
+			case TYPE_INT:
+				b->write (DH_PUSHNUMBER);
+				b->write (atoi (constant->val));
+				break;
+
+			case TYPE_STRING:
+				b->write_string (constant->val);
+				break;
+
+			case TYPE_VOID:
+			case TYPE_UNKNOWN:
+				break;
+		}
+	}
+	else if ((g = FindGlobalVariable (TOKEN)))
+	{
+		// Global variable
+		b->write (DH_PUSHGLOBALVAR);
+		b->write (g->index);
+	}
+	else
+	{
+		// If nothing else, check for literal
+		switch (reqtype)
+		{
+		case TYPE_VOID:
+		case TYPE_UNKNOWN:
+			error ("unknown identifier `%s` (expected keyword, function or variable)", TOKEN.chars());
+			break;
+
 		case TYPE_BOOL:
 		case TYPE_INT:
-			b->Write (DH_PUSHNUMBER);
-			b->Write (atoi (constant->val));
-			break;
-		case TYPE_STRING:
-			b->WriteString (constant->val);
-			break;
-		case TYPE_VOID:
-		case TYPE_UNKNOWN:
+		{
+			m_lx->must_be (tk_number);
+
+			// All values are written unsigned - thus we need to write the value's
+			// absolute value, followed by an unary minus for negatives.
+			b->write (DH_PUSHNUMBER);
+
+			long v = atol (TOKEN);
+			b->write (static_cast<word> (abs (v)));
+
+			if (v < 0)
+				b->write (DH_UNARYMINUS);
+
 			break;
 		}
-	} else if ((g = FindGlobalVariable (token))) {
-		// Global variable
-		b->Write (DH_PUSHGLOBALVAR);
-		b->Write (g->index);
-	} else {
-		// If nothing else, check for literal
-		switch (reqtype) {
-		case TYPE_VOID:
-		case TYPE_UNKNOWN:
-			ParserError ("unknown identifier `%s` (expected keyword, function or variable)", token.chars());
-			break;
-		case TYPE_BOOL:
-		case TYPE_INT: {
-			MustNumber (true);
-			
-			// All values are written unsigned - thus we need to write the value's
-			// absolute value, followed by an unary minus for negatives.
-			b->Write (DH_PUSHNUMBER);
-			
-			long v = atol (token);
-			b->Write (static_cast<word> (abs (v)));
-			if (v < 0)
-				b->Write (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.
 			MustString (true);
-			b->WriteString (token);
+			b->write_string (TOKEN);
 			break;
 		}
 	}
-	
+
 	// Negate it now if desired
 	if (negate)
-		b->Write (DH_NEGATELOGICAL);
-	
+		b->write (DH_NEGATELOGICAL);
+
 	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) {
-	bool global = !var->statename.len ();
-	
+// TOKEN to be the name of the variable, and expects the variable to be given.
+data_buffer* botscript_parser::ParseAssignment (script_variable* var)
+{
+	bool global = !var->statename.len();
+
 	// Get an operator
-	MustNext ();
-	int oper = ParseOperator ();
-	if (!IsAssignmentOperator (oper))
-		ParserError ("expected assignment operator");
-	
+	m_lx->must_get_next();
+	int oper = parse_operator();
+
+	if (!is_assignment_operator (oper))
+		error ("expected assignment operator");
+
 	if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction
-		ParserError ("can't alter variables at top level");
-	
+		error ("can't alter variables at top level");
+
 	// Parse the right operand
-	MustNext ();
-	DataBuffer* retbuf = new DataBuffer;
-	DataBuffer* expr = ParseExpression (var->type);
-	
+	m_lx->must_get_next();
+	data_buffer* retbuf = new data_buffer;
+	data_buffer* expr = parse_expression (var->type);
+
 	// <<= and >>= do not have data headers. Solution: expand them.
 	// a <<= b -> a = a << b
 	// a >>= b -> a = a >> b
-	if (oper == OPER_ASSIGNLEFTSHIFT || oper == OPER_ASSIGNRIGHTSHIFT) {
-		retbuf->Write (global ? DH_PUSHGLOBALVAR : DH_PUSHLOCALVAR);
-		retbuf->Write (var->index);
-		retbuf->Merge (expr);
-		retbuf->Write ((oper == OPER_ASSIGNLEFTSHIFT) ? DH_LSHIFT : DH_RSHIFT);
-		retbuf->Write (global ? DH_ASSIGNGLOBALVAR : DH_ASSIGNLOCALVAR);
-		retbuf->Write (var->index);
-	} else {
-		retbuf->Merge (expr);
-		long dh = DataHeaderByOperator (var, oper);
-		retbuf->Write (dh);
-		retbuf->Write (var->index);
+	if (oper == OPER_ASSIGNLEFTSHIFT || oper == OPER_ASSIGNRIGHTSHIFT)
+	{
+		retbuf->write (global ? DH_PUSHGLOBALVAR : DH_PUSHLOCALVAR);
+		retbuf->write (var->index);
+		retbuf->merge (expr);
+		retbuf->write ( (oper == OPER_ASSIGNLEFTSHIFT) ? DH_LSHIFT : DH_RSHIFT);
+		retbuf->write (global ? DH_ASSIGNGLOBALVAR : DH_ASSIGNLOCALVAR);
+		retbuf->write (var->index);
 	}
-	
+	else
+	{
+		retbuf->merge (expr);
+		long dh = get_data_header_by_operator (var, oper);
+		retbuf->write (dh);
+		retbuf->write (var->index);
+	}
+
 	return retbuf;
 }
 
-void ScriptReader::PushScope () {
+void botscript_parser::push_scope()
+{
 	g_ScopeCursor++;
+
 	if (g_ScopeCursor >= MAX_SCOPE)
-		ParserError ("too deep scope");
-	
-	ScopeInfo* info = &SCOPE(0);
+		error ("too deep scope");
+
+	ScopeInfo* info = &SCOPE (0);
 	info->type = SCOPETYPE_UNKNOWN;
 	info->mark1 = 0;
 	info->mark2 = 0;
 	info->buffer1 = null;
 	info->casecursor = -1;
-	for (int i = 0; i < MAX_CASE; i++) {
+
+	for (int i = 0; i < MAX_CASE; i++)
+	{
 		info->casemarks[i] = MAX_MARKS;
 		info->casebuffers[i] = null;
 		info->casenumbers[i] = -1;
 	}
 }
 
-DataBuffer* ScriptReader::ParseStatement (ObjWriter* w) {
-	if (FindConstant (token)) // There should not be constants here.
-		ParserError ("invalid use for constant\n");
-	
+data_buffer* botscript_parser::parse_statement (object_writer* w)
+{
+	if (find_constant (TOKEN)) // There should not be constants here.
+		error ("invalid use for constant\n");
+
 	// If it's a variable, expect assignment.
-	if (ScriptVar* var = FindGlobalVariable (token))
+	if (script_variable* var = FindGlobalVariable (TOKEN))
 		return ParseAssignment (var);
-	
+
 	return null;
 }
 
-void ScriptReader::AddSwitchCase (ObjWriter* w, DataBuffer* b) {
-	ScopeInfo* info = &SCOPE(0);
-	
+void botscript_parser::add_switch_case (object_writer* w, data_buffer* b)
+{
+	ScopeInfo* info = &SCOPE (0);
+
 	info->casecursor++;
+
 	if (info->casecursor >= MAX_CASE)
-		ParserError ("too many cases in one switch");
-	
+		error ("too many cases in one switch");
+
 	// Init a mark for the case buffer
-	int m = w->AddMark ("");
+	int m = w->add_mark ("");
 	info->casemarks[info->casecursor] = m;
-	
+
 	// Add a reference to the mark. "case" and "default" both
 	// add the necessary bytecode before the reference.
 	if (b)
-		b->AddMarkReference (m);
+		b->add_reference (m);
 	else
-		w->AddReference (m);
-	
+		w->add_reference (m);
+
 	// Init a buffer for the case block and tell the object
 	// writer to record all written data to it.
-	info->casebuffers[info->casecursor] = w->SwitchBuffer = new DataBuffer;
+	info->casebuffers[info->casecursor] = w->SwitchBuffer = new data_buffer;
+}
+
+constant_info* find_constant (string TOKEN)
+{
+	for (int i = 0; i < g_ConstInfo.size(); i++)
+		if (g_ConstInfo[i].name == TOKEN)
+			return &g_ConstInfo[i];
+
+	return null;
 }
 
-constinfo_t* FindConstant (string token) {
-	for (uint i = 0; i < g_ConstInfo.size(); i++)
-		if (g_ConstInfo[i].name == token)
-			return &g_ConstInfo[i];
-	return null;
+// ============================================================================
+//
+bool botscript_parser::token_is (e_token a)
+{
+	return (m_lx->get_token() == a);
+}
+
+// ============================================================================
+//
+string botscript_parser::token_string()
+{
+	return m_lx->get_token()->text;
+}
+
+// ============================================================================
+//
+string botscript_parser::describe_position() const
+{
+	lexer::token* tok = m_lx->get_token();
+	return tok->file + ":" + string (tok->line) + ":" + string (tok->column);
 }
\ No newline at end of file

mercurial