- reformatting

Sun, 02 Feb 2014 17:06:39 +0200

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sun, 02 Feb 2014 17:06:39 +0200
changeset 88
5def6ff8b466
parent 87
8f65914e7046
child 89
029a330a9bef

- reformatting

.gitignore file | annotate | diff | comparison | revisions
CMakeLists.txt file | annotate | diff | comparison | revisions
src/BotStuff.h file | annotate | diff | comparison | revisions
src/Commands.cc file | annotate | diff | comparison | revisions
src/Commands.h file | annotate | diff | comparison | revisions
src/Containers.h file | annotate | diff | comparison | revisions
src/DataBuffer.cc file | annotate | diff | comparison | revisions
src/DataBuffer.h file | annotate | diff | comparison | revisions
src/Events.cc file | annotate | diff | comparison | revisions
src/Events.h file | annotate | diff | comparison | revisions
src/Format.cc file | annotate | diff | comparison | revisions
src/Format.h file | annotate | diff | comparison | revisions
src/Lexer.cc file | annotate | diff | comparison | revisions
src/Lexer.h file | annotate | diff | comparison | revisions
src/LexerScanner.cc file | annotate | diff | comparison | revisions
src/LexerScanner.h file | annotate | diff | comparison | revisions
src/Main.cc file | annotate | diff | comparison | revisions
src/Main.h file | annotate | diff | comparison | revisions
src/Parser.cc file | annotate | diff | comparison | revisions
src/Parser.h file | annotate | diff | comparison | revisions
src/Property.h file | annotate | diff | comparison | revisions
src/String.cc file | annotate | diff | comparison | revisions
src/String.h file | annotate | diff | comparison | revisions
src/StringTable.cc file | annotate | diff | comparison | revisions
src/StringTable.h file | annotate | diff | comparison | revisions
src/Tokens.h file | annotate | diff | comparison | revisions
src/Types.h file | annotate | diff | comparison | revisions
src/Variables.cc file | annotate | diff | comparison | revisions
src/Variables.h file | annotate | diff | comparison | revisions
src/botstuff.h file | annotate | diff | comparison | revisions
src/commands.cc file | annotate | diff | comparison | revisions
src/commands.h file | annotate | diff | comparison | revisions
src/containers.h file | annotate | diff | comparison | revisions
src/data_buffer.cc file | annotate | diff | comparison | revisions
src/data_buffer.h file | annotate | diff | comparison | revisions
src/events.cc file | annotate | diff | comparison | revisions
src/events.h file | annotate | diff | comparison | revisions
src/format.cc file | annotate | diff | comparison | revisions
src/format.h file | annotate | diff | comparison | revisions
src/lexer.cc file | annotate | diff | comparison | revisions
src/lexer.h file | annotate | diff | comparison | revisions
src/lexer_scanner.cc file | annotate | diff | comparison | revisions
src/lexer_scanner.h file | annotate | diff | comparison | revisions
src/main.cc file | annotate | diff | comparison | revisions
src/main.h file | annotate | diff | comparison | revisions
src/parser.cc file | annotate | diff | comparison | revisions
src/parser.h file | annotate | diff | comparison | revisions
src/str.cc file | annotate | diff | comparison | revisions
src/str.h file | annotate | diff | comparison | revisions
src/stringtable.cc file | annotate | diff | comparison | revisions
src/stringtable.h file | annotate | diff | comparison | revisions
src/tokens.h file | annotate | diff | comparison | revisions
src/types.h file | annotate | diff | comparison | revisions
src/variables.cc file | annotate | diff | comparison | revisions
src/variables.h file | annotate | diff | comparison | revisions
updaterevision/CMakeLists.txt file | annotate | diff | comparison | revisions
--- a/.gitignore	Sun Feb 02 01:50:23 2014 +0200
+++ b/.gitignore	Sun Feb 02 17:06:39 2014 +0200
@@ -1,3 +1,5 @@
 build
-gitinfo.h
-untracked
\ No newline at end of file
+GitInformation.h
+untracked
+botc.kdev4
+.kdev4
\ No newline at end of file
--- a/CMakeLists.txt	Sun Feb 02 01:50:23 2014 +0200
+++ b/CMakeLists.txt	Sun Feb 02 17:06:39 2014 +0200
@@ -2,23 +2,23 @@
 
 add_subdirectory (updaterevision)
 add_executable (botc
-	src/commands.cc
-	src/data_buffer.cc
-	src/events.cc
-	src/format.cc
-	src/lexer.cc
-	src/lexer_scanner.cc
-	src/main.cc
-	src/parser.cc
-	src/str.cc
-	src/stringtable.cc
-	src/variables.cc
+	src/Commands.cc
+	src/DataBuffer.cc
+	src/Events.cc
+	src/Format.cc
+	src/Lexer.cc
+	src/LexerScanner.cc
+	src/Main.cc
+	src/Parser.cc
+	src/String.cc
+	src/StringTable.cc
+	src/Variables.cc
 )
 
 get_target_property (UPDATEREVISION_EXE updaterevision LOCATION)
 
 add_custom_target (revision_check ALL
-    COMMAND ${UPDATEREVISION_EXE} src/gitinfo.h
+    COMMAND ${UPDATEREVISION_EXE} src/GitInformation.h
     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
     DEPENDS updaterevision)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/BotStuff.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,196 @@
+/*
+	Copyright 2000-2010 Brad Carney
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+// Numeric values and stuff from zandronum bots.h
+
+#ifndef BOTC_BOTSTUFF_H
+#define BOTC_BOTSTUFF_H
+
+static const int gMaxStates			= 256;
+static const int gMaxEvents			= 32;
+static const int gMaxGlobalEvents	= 32;
+static const int gMaxGlobalVars		= 128;
+static const int gMaxGlobalArrays	= 16;
+static const int gMaxArraySize		= 65536;
+static const int gMaxStateVars		= 16;
+static const int gMaxStringlistSize	= 128;
+static const int gMaxStringLength	= 256;
+static const int gMaxReactionTime	= 52;
+static const int gMaxStoredEvents	= 64;
+
+enum EDataHeader
+{
+	dhCommand,
+	dhStateIndex,
+	dhStateName,
+	dhOnEnter,
+	dhMainLoop,
+	dhOnExit,
+	dhEvent,
+	dhEndOnEnter,
+	dhEndMainLoop,
+	dhEndOnExit,
+	dhEndEvent,
+	dhIfGoto,
+	dhIfNotGoto,
+	dhGoto,
+	dhOrLogical,
+	dhAndLogical,
+	dhOrBitwise,
+	dhEorBitwise,
+	dhAndBitwise,
+	dhEquals,
+	dhNotEquals,
+	dhLessThan,
+	dhAtMost,
+	dhGreaterThan,
+	dhAtLeast,
+	dhNegateLogical,
+	dhLeftShift,
+	dhRightShift,
+	dhAdd,
+	dhSubtract,
+	dhUnaryMinus,
+	dhMultiply,
+	dhDivide,
+	dhModulus,
+	dhPushNumber,
+	dhPushStringIndex,
+	dhPushGlobalVar,
+	dhPushLocalVar,
+	dhDropStackPosition,
+	dhScriptVarList,
+	dhStringList,
+	dhIncreaseGlobalVar,
+	dhDecreaseGlobalVar,
+	dhAssignGlobalVar,
+	dhAddGlobalVar,
+	dhSubtractGlobalVar,
+	dhMultiplyGlobalVar,
+	dhDivideGlobalVar,
+	dhModGlobalVar,
+	dhIncreaseLocalVar,
+	dhDecreaseLocalVar,
+	dhAssignLocalVar,
+	dhAddLocalVar,
+	dhSubtractLocalVar,
+	dhMultiplyLocalVar,
+	dhDivideLocalVar,
+	dhModLocalVar,
+	dhCaseGoto,
+	dhDrop,
+	dhIncreaseGlobalArray,
+	dhDecreaseGlobalArray,
+	dhAssignGlobalArray,
+	dhAddGlobalArray,
+	dhSubtractGlobalArray,
+	dhMultiplyGlobalArray,
+	dhDivideGlobalArray,
+	dhModGlobalArray,
+	dhPushGlobalArray,
+	dhSwap,
+	dhDup,
+	dhArraySet,
+	numDataHeaders
+};
+
+//*****************************************************************************
+//	These are the different bot events that can be posted to a bot's state.
+enum eEvent
+{
+	evKilledByEnemy,
+	evKilledByPlayer,
+	evKilledBySelf,
+	evKilledByEnvironment,
+	evReachedGoal,
+	evGoalRemoved,
+	evDamagedByPlayer,
+	evPlayerSay,
+	evEnemyKilled,
+	evRespawned,
+	evIntermission,
+	evNewMaps,
+	evEnemyUsedFist,
+	evEnemyUsedChainsaw,
+	evEnemyFiredPistol,
+	evEnemyFiredShotgun,
+	evEnemyFiredSsg,
+	evEnemyFiredChaingun,
+	evEnemyFiredMinigun,
+	evEnemyFiredRocket,
+	evEnemyFiredGrenade,
+	evEnemyFiredRailgun,
+	evEnemyFiredPlasma,
+	evEnemyFiredBfg,
+	evEnemyFiredBfg10k,
+	evPlayerUsedFist,
+	evPlayerUsedChainsaw,
+	evPlayerFiredPistol,
+	evPlayerFiredShotgun,
+	evPlayerFiredSsg,
+	evPlayerFiredChaingun,
+	evPlayerFiredMinigun,
+	evPlayerFiredRocket,
+	evPlayerFiredGrenade,
+	evPlayerFiredRailgun,
+	evPlayerFiredPlasma,
+	evPlayerFiredBfg,
+	evPlayerFiredBfg10k,
+	evUsedFist,
+	evUsedChainsaw,
+	evFiredPistol,
+	evFiredShotgun,
+	evFiredSsg,
+	evFiredChaingun,
+	evFiredMinigun,
+	evFiredRocket,
+	evFiredGrenade,
+	evFiredRailgun,
+	evFiredPlasma,
+	evFiredBfg,
+	evFiredBfg10k,
+	evPlayerJoinedGame,
+	evJoinedGame,
+	evDuelStartingCountdown,
+	evDuelFight,
+	evDuelWinSequence,
+	evSpectating,
+	evLmsStartingCountdown,
+	evLmsFight,
+	evLmsWinSequence,
+	evWeaponChange,
+	evEnemyBfgExplode,
+	evPlayerBfgExplode,
+	evBfgExplode,
+	evRecievedMedal,
+
+	numBotEvents
+};
+
+#endif	// BOTC_BOTSTUFF_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Commands.cc	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,124 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "Main.h"
+#include "String.h"
+#include "Commands.h"
+#include "Lexer.h"
+
+static List<CommandInfo*> gCommands;
+
+// ============================================================================
+//
+void AddCommandDefinition (CommandInfo* comm)
+{
+	// Ensure that there is no conflicts
+	for (CommandInfo* it : gCommands)
+		if (it->number == comm->number)
+			Error ("Attempted to redefine command #%1 (%2) as %3",
+				   gCommands[comm->number]->name, comm->name);
+
+	gCommands << comm;
+}
+
+// ============================================================================
+// Finds a command by name
+CommandInfo* FindCommandByName (String fname)
+{
+	for (CommandInfo* comm : gCommands)
+	{
+		if (fname.ToUppercase() == comm->name.ToUppercase())
+			return comm;
+	}
+
+	return null;
+}
+
+// ============================================================================
+//
+// Returns the prototype of the command
+//
+String CommandInfo::GetSignature()
+{
+	String text;
+	text += GetTypeName (returnvalue);
+	text += ' ';
+	text += name;
+
+	if (maxargs != 0)
+		text += ' ';
+
+	text += '(';
+
+	bool hasoptionals = false;
+
+	for (int i = 0; i < maxargs; i++)
+	{
+		if (i == numargs)
+		{
+			hasoptionals = true;
+			text += '[';
+		}
+
+		if (i)
+			text += ", ";
+
+		text += GetTypeName (args[i].type);
+		text += ' ';
+		text += args[i].name;
+
+		if (i >= numargs)
+		{
+			text += " = ";
+
+			bool is_string = args[i].type == EStringType;
+
+			if (is_string)
+				text += '"';
+
+			text += String::FromNumber (args[i].defvalue);
+
+			if (is_string)
+				text += '"';
+		}
+	}
+
+	if (hasoptionals)
+		text += ']';
+
+	text += ')';
+	return text;
+}
+
+const List<CommandInfo*>& GetCommands()
+{
+	return gCommands;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Commands.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,58 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_COMMANDS_H
+#define BOTC_COMMANDS_H
+
+#include "Main.h"
+#include "String.h"
+
+struct CommandArgument
+{
+	EType					type;
+	String					name;
+	int						defvalue;
+};
+
+struct CommandInfo
+{
+	String					name;
+	int						number;
+	int						numargs;
+	int						maxargs;
+	EType					returnvalue;
+	List<CommandArgument>	args;
+
+	String	GetSignature();
+};
+
+void						AddCommandDefinition (CommandInfo* comm);
+CommandInfo*				FindCommandByName (String a);
+const List<CommandInfo*>&	GetCommands();
+
+#endif // BOTC_COMMANDS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Containers.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,348 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_CONTAINERS_H
+#define BOTC_CONTAINERS_H
+
+#include <cassert>
+#include <algorithm>
+#include <deque>
+#include <initializer_list>
+
+template<class T>
+class List
+{
+	public:
+		using ListType					= typename std::deque<T>;
+		using Iterator					= typename ListType::iterator;
+		using ConstIterator				= typename ListType::const_iterator;
+		using ReverseIterator			= typename ListType::reverse_iterator;
+		using ConstReverseIterator		= typename ListType::const_reverse_iterator;
+		using ValueType					= T;
+		using SelfType					= List<T>;
+
+		// =====================================================================
+		//
+		List() {}
+
+		// =====================================================================
+		//
+		List (std::initializer_list<ValueType> vals)
+		{
+			m_data = vals;
+		}
+
+		// =====================================================================
+		//
+		List (const ListType& a) :
+			m_data (a) {}
+
+		// =====================================================================
+		//
+		Iterator begin()
+		{
+			return m_data.begin();
+		}
+
+		// =====================================================================
+		//
+		ConstIterator begin() const
+		{
+			return m_data.cbegin();
+		}
+
+		// =====================================================================
+		//
+		Iterator end()
+		{
+			return m_data.end();
+		}
+
+		// =====================================================================
+		//
+		ConstIterator end() const
+		{
+			return m_data.cend();
+		}
+
+		// =====================================================================
+		//
+		ReverseIterator rbegin()
+		{
+			return m_data.rbegin();
+		}
+
+		// =====================================================================
+		//
+		ConstReverseIterator crbegin() const
+		{
+			return m_data.crbegin();
+		}
+
+		// =====================================================================
+		//
+		ReverseIterator rend()
+		{
+			return m_data.rend();
+		}
+
+		// =====================================================================
+		//
+		ConstReverseIterator crend() const
+		{
+			return m_data.crend();
+		}
+
+		// =====================================================================
+		//
+		inline void RemoveAt (int pos)
+		{
+			assert (pos < Size());
+			m_data.erase (m_data.begin() + pos);
+		}
+
+		// =====================================================================
+		//
+		ValueType& Prepend (const ValueType& value)
+		{
+			m_data.push_front (value);
+			return m_data[0];
+		}
+
+		// =====================================================================
+		//
+		ValueType& Append (const ValueType& value)
+		{
+			m_data.push_back (value);
+			return m_data[m_data.size() - 1];
+		}
+
+		// =====================================================================
+		//
+		void Merge (const SelfType& vals)
+		{
+			for (const T & val : vals)
+				Append (val);
+		}
+
+		// =====================================================================
+		//
+		bool Pop (T& val)
+		{
+			if (IsEmpty())
+				return false;
+
+			val = m_data[Size() - 1];
+			m_data.erase (m_data.end() - 1);
+			return true;
+		}
+
+		// =====================================================================
+		//
+		T& operator<< (const T& value)
+		{
+			return Append (value);
+		}
+
+		// =====================================================================
+		//
+		void operator<< (const SelfType& vals)
+		{
+			Merge (vals);
+		}
+
+		// =====================================================================
+		//
+		bool operator>> (T& value)
+		{
+			return Pop (value);
+		}
+
+		// =====================================================================
+		//
+		SelfType Reverse() const
+		{
+			SelfType rev;
+
+			for (const T & val : *this)
+				val >> rev;
+
+			return rev;
+		}
+
+		// =====================================================================
+		//
+		void Clear()
+		{
+			m_data.clear();
+		}
+
+		// =====================================================================
+		//
+		void Insert (int pos, const ValueType& value)
+		{
+			m_data.insert (m_data.begin() + pos, value);
+		}
+
+		// =====================================================================
+		//
+		void RemoveDuplicates()
+		{
+			// Remove duplicate entries. For this to be effective, the vector must be
+			// sorted first.
+			Sort();
+			Iterator pos = std::unique (begin(), end());
+			Resize (std::distance (begin(), pos));
+		}
+
+		// =====================================================================
+		//
+		int Size() const
+		{
+			return m_data.size();
+		}
+
+		// =====================================================================
+		//
+		ValueType& operator[] (int n)
+		{
+			assert (n < Size());
+			return m_data[n];
+		}
+
+		// =====================================================================
+		//
+		const ValueType& operator[] (int n) const
+		{
+			assert (n < Size());
+			return m_data[n];
+		}
+
+		// =====================================================================
+		//
+		void Resize (int size)
+		{
+			m_data.resize (size);
+		}
+
+		// =====================================================================
+		//
+		void Sort()
+		{
+			std::sort (begin(), end());
+		}
+
+		// =====================================================================
+		//
+		int Find (const ValueType& needle) const
+		{
+			int i = 0;
+
+			for (const ValueType & hay : *this)
+			{
+				if (&hay == &needle)
+					return i;
+
+				i++;
+			}
+
+			return -1;
+		}
+
+		// =====================================================================
+		//
+		void Remove (const ValueType& it)
+		{
+			int idx;
+
+			if ((idx = Find (it)) != -1)
+				RemoveAt (idx);
+		}
+
+		// =====================================================================
+		//
+		inline bool IsEmpty() const
+		{
+			return Size() == 0;
+		}
+
+		// =====================================================================
+		//
+		SelfType Mid (int a, int b) const
+		{
+			assert (a >= 0 && b >= 0 && a < Size() && b < Size() && a <= b);
+			SelfType result;
+
+			for (int i = a; i <= b; ++i)
+				result << operator[] (i);
+
+			return result;
+		}
+
+		// =====================================================================
+		//
+		inline const ListType& GetDeque() const
+		{
+			return m_data;
+		}
+
+		// =====================================================================
+		//
+		inline const ValueType& First() const
+		{
+			return *m_data.begin();
+		}
+
+		// =====================================================================
+		//
+		inline const ValueType& Last() const
+		{
+			return *(m_data.end() - 1);
+		}
+
+		// =====================================================================
+		//
+		inline bool Contains (const ValueType& a) const
+		{
+			return Find (a) != -1;
+		}
+
+	private:
+		ListType m_data;
+};
+
+// =============================================================================
+//
+template<class T>
+List<T>& operator>> (const T& value, List<T>& haystack)
+{
+	haystack.push_front (value);
+	return haystack;
+}
+
+#endif // BOTC_CONTAINERS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DataBuffer.cc	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,243 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 "DataBuffer.h"
+
+// ============================================================================
+//
+DataBuffer::DataBuffer (int size)
+{
+	SetBuffer (new byte[size]);
+	SetPosition (&GetBuffer()[0]);
+	SetAllocatedSize (size);
+}
+
+// ============================================================================
+//
+DataBuffer::~DataBuffer()
+{
+	assert (CountMarks() == 0 && CountReferences() == 0);
+	delete GetBuffer();
+}
+
+// ============================================================================
+//
+void DataBuffer::MergeAndDestroy (DataBuffer* other)
+{
+	if (!other)
+		return;
+
+	int oldsize = GetWrittenSize();
+	CopyBuffer (other);
+
+	// Assimilate in its marks and references
+	for (ByteMark* mark : other->GetMarks())
+	{
+		mark->pos += oldsize;
+		PushToMarks (mark);
+	}
+
+	for (MarkReference* ref : other->GetReferences())
+	{
+		ref->pos += oldsize;
+		PushToReferences (ref);
+	}
+
+	ClearMarks();
+	ClearReferences();
+	delete other;
+}
+
+// ============================================================================
+//
+DataBuffer* DataBuffer::Clone()
+{
+	DataBuffer* other = new DataBuffer;
+	other->CopyBuffer (this);
+	return other;
+}
+
+// ============================================================================
+//
+void DataBuffer::CopyBuffer (const DataBuffer* buf)
+{
+	CheckSpace (buf->GetWrittenSize());
+	memcpy (mPosition, buf->GetBuffer(), buf->GetWrittenSize());
+	mPosition += buf->GetWrittenSize();
+}
+
+// ============================================================================
+//
+ByteMark* DataBuffer::AddMark (String name)
+{
+	ByteMark* mark = new ByteMark;
+	mark->name = name;
+	mark->pos = GetWrittenSize();
+	PushToMarks (mark);
+	return mark;
+}
+
+// ============================================================================
+//
+MarkReference* DataBuffer::AddReference (ByteMark* mark, bool writePlaceholder)
+{
+	MarkReference* ref = new MarkReference;
+	ref->target = mark;
+	ref->pos = GetWrittenSize();
+	PushToReferences (ref);
+
+	// Write a dummy placeholder for the reference
+	if (writePlaceholder)
+		WriteDWord (0xBEEFCAFE);
+
+	return ref;
+}
+
+// ============================================================================
+//
+void DataBuffer::AdjustMark (ByteMark* mark)
+{
+	mark->pos = GetWrittenSize();
+}
+
+// ============================================================================
+//
+void DataBuffer::OffsetMark (ByteMark* mark, int offset)
+{
+	mark->pos += offset;
+}
+
+// ============================================================================
+//
+void DataBuffer::WriteFloat (float a)
+{
+	// TODO: Find a way to store the number without decimal loss.
+	WriteDWord (dhPushNumber);
+	WriteDWord (abs (a));
+
+	if (a < 0)
+		WriteDWord (dhUnaryMinus);
+}
+
+// ============================================================================
+//
+void DataBuffer::WriteStringIndex (const String& a)
+{
+	WriteDWord (dhPushStringIndex);
+	WriteDWord (GetStringTableIndex (a));
+}
+
+// ============================================================================
+//
+void DataBuffer::Dump()
+{
+	for (int i = 0; i < GetWrittenSize(); ++i)
+		printf ("%d. [0x%X]\n", i, GetBuffer()[i]);
+}
+
+// ============================================================================
+//
+void DataBuffer::CheckSpace (int bytes)
+{
+	int writesize = GetWrittenSize();
+
+	if (writesize + bytes < GetAllocatedSize())
+		return;
+
+	// We don't have enough space in the buffer to write
+	// the stuff - thus resize. First, store the old
+	// buffer temporarily:
+	char* copy = new char[GetAllocatedSize()];
+	memcpy (copy, GetBuffer(), GetAllocatedSize());
+
+	// Remake the buffer with the new size. Have enough space
+	// for the stuff we're going to write, as well as a bit
+	// of leeway so we don't have to resize immediately again.
+	int newsize = GetAllocatedSize() + bytes + 512;
+
+	delete GetBuffer();
+	SetBuffer (new byte[newsize]);
+	SetAllocatedSize (newsize);
+
+	// Now, copy the stuff back.
+	memcpy (mBuffer, copy, GetAllocatedSize());
+	SetPosition (GetBuffer() + writesize);
+	delete copy;
+}
+
+// =============================================================================
+//
+void DataBuffer::WriteByte (int8_t data)
+{
+	CheckSpace (1);
+	*mPosition++ = data;
+}
+
+// =============================================================================
+//
+void DataBuffer::WriteWord (int16_t data)
+{
+	CheckSpace (2);
+
+	for (int i = 0; i < 2; ++i)
+		*mPosition++ = (data >> (i * 8)) & 0xFF;
+}
+
+// =============================================================================
+//
+void DataBuffer::WriteDWord (int32_t data)
+{
+	CheckSpace (4);
+
+	for (int i = 0; i < 4; ++i)
+		*mPosition++ = (data >> (i * 8)) & 0xFF;
+}
+
+// =============================================================================
+//
+void DataBuffer::WriteString (const String& a)
+{
+	CheckSpace (a.Length() + 1);
+
+	for (char c : a)
+		WriteByte (c);
+
+	WriteByte ('\0');
+}
+
+
+// =============================================================================
+//
+ByteMark* DataBuffer::FindMarkByName (const String& target)
+{
+	for (ByteMark* mark : GetMarks())
+		if (mark->name == target)
+			return mark;
+
+	return null;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DataBuffer.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,94 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_DATABUFFER_H
+#define BOTC_DATABUFFER_H
+
+#include <stdio.h>
+#include <string.h>
+#include "Main.h"
+#include "StringTable.h"
+
+#define MAX_MARKS 512
+
+// ============================================================================
+// data_buffer: A dynamic data buffer.
+//
+// Notes:
+//
+// - A mark is a "pointer" to a particular position in the bytecode. The actual
+//   permanent position cannot be predicted in any way or form, thus these things
+//   are used to "mark" a position like that for future use.
+//
+// - A reference is another "mark" that references a mark. When the bytecode
+//   is written to file, they are changed into their marks' current
+//   positions. Marks themselves are never written to files, only refs are
+//
+class DataBuffer
+{
+	PROPERTY (private, byte*,					Buffer,			NO_OPS,		STOCK_WRITE)
+	PROPERTY (private, int,						AllocatedSize,	NUM_OPS,	STOCK_WRITE)
+	PROPERTY (private, byte*,					Position,		NO_OPS,		STOCK_WRITE)
+	PROPERTY (private, List<ByteMark*>,			Marks,			LIST_OPS,	STOCK_WRITE)
+	PROPERTY (private, List<MarkReference*>,	References,		LIST_OPS,	STOCK_WRITE)
+
+	public:
+		DataBuffer (int size = 128);
+		~DataBuffer();
+
+		// ====================================================================
+		// Merge another data buffer into this one.
+		// Note: @other is destroyed in the process!
+		void MergeAndDestroy (DataBuffer* other);
+
+		// Clones this databuffer to a new one and returns it.
+		DataBuffer* Clone();
+
+		ByteMark*		AddMark (String name);
+		MarkReference*	AddReference (ByteMark* mark, bool write_placeholder = true);
+		void			CheckSpace (int bytes);
+		void			DeleteMark (int marknum);
+		void			AdjustMark (ByteMark* mark);
+		void			OffsetMark (ByteMark* mark, int offset);
+		ByteMark*		FindMarkByName (const String& target);
+		void			Dump();
+		void			WriteFloat (float a);
+		void			WriteStringIndex (const String& a);
+		void			WriteString (const String& a);
+		void			WriteByte (int8_t data);
+		void			WriteWord (int16_t data);
+		void			WriteDWord (int32_t data);
+		void			CopyBuffer (const DataBuffer* buf);
+
+		inline int GetWrittenSize() const
+		{
+			return GetPosition() - GetBuffer();
+		}
+};
+
+#endif // BOTC_DATABUFFER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Events.cc	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,65 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <stdlib.h>
+#include <stdio.h>
+#include "Main.h"
+#include "String.h"
+#include "Events.h"
+#include "Lexer.h"
+
+static List<EventDefinition*> gEvents;
+
+// ============================================================================
+//
+void AddEvent (EventDefinition* e)
+{
+	gEvents << e;
+}
+
+// ============================================================================
+//
+// Finds an event definition by index
+//
+EventDefinition* FindEventByIndex (int idx)
+{
+	return gEvents[idx];
+}
+
+// ============================================================================
+//
+// Finds an event definition by name
+//
+EventDefinition* FindEventByName (String a)
+{
+	for (EventDefinition* e : gEvents)
+		if (a.ToUppercase() == e->name.ToUppercase())
+			return e;
+
+	return null;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Events.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,44 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_EVENTS_H
+#define BOTC_EVENTS_H
+
+#include "String.h"
+
+struct EventDefinition
+{
+	String name;
+	int number;
+};
+
+void AddEvent (EventDefinition* e);
+EventDefinition* FindEventByIndex (int idx);
+EventDefinition* FindEventByName (String a);
+
+#endif // BOTC_EVENTS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Format.cc	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,138 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <cstdio>
+#include "Main.h"
+#include "Format.h"
+#include "Lexer.h"
+
+// =============================================================================
+//
+static void DrawPosition (const String& fmt, int pos)
+{
+	String rep (fmt);
+	rep.Replace ("\n", "↵");
+	rep.Replace ("\t", "⇥");
+
+	fprintf (stderr, "%s\n", rep.CString());
+
+	for (int x = 0; x < pos; ++x)
+		fprintf (stderr, "-");
+
+	fprintf (stderr, "^\n");
+}
+
+// =============================================================================
+//
+String FormatArgs (const List<FormatArgument>& args)
+{
+	const String& fmtstr = args[0].AsString();
+	assert (args.Size() >= 1);
+
+	if (args.Size() == 1)
+		return args[0].AsString();
+
+	String fmt = fmtstr;
+	String out;
+	int pos = 0;
+
+	while ((pos = fmt.FirstIndexOf ("%", pos)) != -1)
+	{
+		if (fmt[pos + 1] == '%')
+		{
+			fmt.Replace (pos, 2, "%");
+			pos++;
+			continue;
+		}
+
+		int ofs = 1;
+		char mod = '\0';
+
+		// handle modifiers
+		if (fmt[pos + ofs] == 's' || fmt[pos + ofs] == 'x')
+		{
+			mod = fmt[pos + ofs];
+			ofs++;
+		}
+
+		if (!isdigit (fmt[pos + ofs]))
+		{
+			fprintf (stderr, "bad format string, expected digit with optional "
+				"modifier after '%%':\n");
+			DrawPosition (fmt, pos);
+			return fmt;
+		}
+
+		int i = fmt[pos + ofs]  - '0';
+
+		if (i >= args.Size())
+		{
+			fprintf (stderr, "format arg #%d used but not defined: %s\n", i, fmtstr.CString());
+			return fmt;
+		}
+
+		String repl = args[i].AsString();
+
+		switch (mod)
+		{
+			case 's': repl = (repl == "1") ? "" : "s";			break;
+			case 'd': repl.SPrintf ("%d", repl[0]);				break;
+			case 'x': repl.SPrintf ("0x%X", repl.ToLong());	break;
+			default: break;
+		}
+
+		fmt.Replace (pos, 1 + ofs, repl);
+		pos += repl.Length();
+	}
+
+	return fmt;
+}
+
+// =============================================================================
+//
+void PrintArgs (FILE* fp, const List<FormatArgument>& args)
+{
+	String out = FormatArgs (args);
+	fprintf (fp, "%s", out.CString());
+}
+
+// =============================================================================
+//
+void DoError (String msg)
+{
+	Lexer* lx = Lexer::GetCurrentLexer();
+	String fileinfo;
+
+	if (lx != null && lx->HasValidToken())
+	{
+		Lexer::Token* tk = lx->GetToken();
+		fileinfo = Format ("%1:%2:%3: ", tk->file, tk->line, tk->column);
+	}
+
+	throw ScriptError (fileinfo + msg);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Format.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,132 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_FORMAT_H
+#define BOTC_FORMAT_H
+
+#include "String.h"
+#include "Containers.h"
+
+class FormatArgument
+{
+	public:
+		FormatArgument (const String& a) :
+			mText (a) {}
+
+		FormatArgument (char a) :
+			mText (a) {}
+
+		FormatArgument (int a) :
+			mText (String::FromNumber (a)) {}
+
+		FormatArgument (long a) :
+			mText (String::FromNumber (a)) {}
+
+		FormatArgument (const char* a) :
+			mText (a) {}
+
+		FormatArgument (void* a)
+		{
+			mText.SPrintf ("%p", a);
+		}
+
+		FormatArgument (const void* a)
+		{
+			mText.SPrintf ("%p", a);
+		}
+
+		template<class T> FormatArgument (const List<T>& list)
+		{
+			if (list.IsEmpty())
+			{
+				mText = "{}";
+				return;
+			}
+
+			mText = "{ ";
+
+			for (const T & a : list)
+			{
+				if (&a != &list[0])
+					mText += ", ";
+
+				mText += FormatArgument (a).AsString();
+			}
+
+			mText += " }";
+		}
+
+		inline const String& AsString() const
+		{
+			return mText;
+		}
+
+	private:
+		String mText;
+};
+
+template<class T> String custom_format (T a, const char* fmtstr)
+{
+	String out;
+	out.SPrintf (fmtstr, a);
+	return out;
+}
+
+String FormatArgs (const List<FormatArgument>& args);
+void PrintArgs (FILE* fp, const List<FormatArgument>& args);
+void DoError (String msg);
+
+#ifndef IN_IDE_PARSER
+# define Format(...) FormatArgs ({__VA_ARGS__})
+# define PrintTo(A, ...) PrintArgs (A, {__VA_ARGS__})
+# define Print(...) PrintArgs (stdout, {__VA_ARGS__})
+# define Error(...) DoError (Format (__VA_ARGS__))
+#else
+String Format (void, ...);
+void PrintTo (FILE* fp, ...);
+void Print (void, ...);
+void Error (void, ...);
+#endif
+
+#ifndef IN_IDE_PARSER
+# ifdef DEBUG
+#  define devf(...) fprint (stderr, __VA_ARGS__)
+#  define dvalof( A ) fprint (stderr, "value of '%1' = %2\n", #A, A)
+# else
+#  define devf(...)
+#  define dvalof( A )
+# endif // DEBUG
+#else
+// print something in debug builds
+void devf (void, ...);
+
+// print the value of @a
+void dvalof (void a);
+#endif // IN_IDE_PARSER
+
+#endif // BOTC_FORMAT_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Lexer.cc	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,306 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <cstring>
+#include "Lexer.h"
+
+static StringList	gFileNameStack;
+static Lexer*		gMainLexer = null;
+
+// =============================================================================
+//
+Lexer::Lexer()
+{
+	assert (gMainLexer == null);
+	gMainLexer = this;
+}
+
+// =============================================================================
+//
+Lexer::~Lexer()
+{
+	gMainLexer = null;
+}
+
+// =============================================================================
+//
+void Lexer::ProcessFile (String fileName)
+{
+	gFileNameStack << fileName;
+	FILE* fp = fopen (fileName, "r");
+
+	if (fp == null)
+		Error ("couldn't open %1 for reading: %2", fileName, strerror (errno));
+
+	LexerScanner sc (fp);
+	CheckFileHeader (sc);
+
+	while (sc.GetNextToken())
+	{
+		// Preprocessor commands:
+		if (sc.GetTokenType() == tkHash)
+		{
+			MustGetFromScanner (sc, tkSymbol);
+
+			if (sc.GetTokenText() == "include")
+			{
+				MustGetFromScanner (sc, tkString);
+				String fileName = sc.GetTokenText();
+
+				if (gFileNameStack.Contains (fileName))
+					Error ("attempted to #include %1 recursively", sc.GetTokenText());
+
+				ProcessFile (fileName);
+			}
+			else
+				Error ("unknown preprocessor directive \"#%1\"", sc.GetTokenText());
+		}
+		else
+		{
+			Token tok;
+			tok.file = fileName;
+			tok.line = sc.GetLine();
+			tok.column = sc.GetColumn();
+			tok.type = sc.GetTokenType();
+			tok.text = sc.GetTokenText();
+
+			// devf ("Token #%1: %2:%3:%4: %5 (%6)\n", mTokens.size(),
+			//	tok.file, tok.line, tok.column, DescribeToken (&tok), DescribeTokenType (tok.type));
+
+			mTokens << tok;
+		}
+	}
+
+	mTokenPosition = mTokens.begin() - 1;
+	gFileNameStack.Remove (fileName);
+}
+
+// ============================================================================
+//
+static bool IsValidHeader (String header)
+{
+	if (header.EndsWith ("\n"))
+		header.RemoveFromEnd (1);
+
+	StringList tokens = header.Split (" ");
+
+	if (tokens.Size() != 2 || tokens[0] != "#!botc" || tokens[1].IsEmpty())
+		return false;
+
+	StringList nums = tokens[1].Split (".");
+
+	if (nums.Size() == 2)
+		nums << "0";
+	elif (nums.Size() != 3)
+		return false;
+
+	bool okA, okB, okC;
+	long major = nums[0].ToLong (&okA);
+	long minor = nums[1].ToLong (&okB);
+	long patch = nums[2].ToLong (&okC);
+
+	if (!okA || !okB || !okC)
+		return false;
+
+	if (VERSION_NUMBER < MAKE_VERSION_NUMBER (major, minor, patch))
+		Error ("The script file requires " APPNAME " v%1, this is v%2",
+			MakeVersionString (major, minor, patch), GetVersionString (EShortForm));
+
+	return true;
+}
+
+// ============================================================================
+//
+void Lexer::CheckFileHeader (LexerScanner& sc)
+{
+	if (!IsValidHeader (sc.ReadLine()))
+		Error ("Not a valid botscript file! File must start with '#!botc <version>'");
+}
+
+// =============================================================================
+//
+bool Lexer::GetNext (EToken req)
+{
+	Iterator pos = mTokenPosition;
+
+	if (mTokens.IsEmpty())
+		return false;
+
+	mTokenPosition++;
+
+	if (IsAtEnd() || (req != tkAny && GetTokenType() != req))
+	{
+		mTokenPosition = pos;
+		return false;
+	}
+
+	return true;
+}
+
+// =============================================================================
+//
+void Lexer::MustGetNext (EToken tt)
+{
+	if (!GetNext())
+		Error ("unexpected EOF");
+
+	if (tt != tkAny)
+		TokenMustBe (tt);
+}
+
+// =============================================================================
+// eugh..
+//
+void Lexer::MustGetFromScanner (LexerScanner& sc, EToken tt)
+{
+	if (!sc.GetNextToken())
+		Error ("unexpected EOF");
+
+	if (tt != tkAny && sc.GetTokenType() != tt)
+	{
+		// TODO
+		Token tok;
+		tok.type = sc.GetTokenType();
+		tok.text = sc.GetTokenText();
+
+		Error ("at %1:%2: expected %3, got %4",
+			   gFileNameStack.Last(),
+			sc.GetLine(),
+			DescribeTokenType (tt),
+			DescribeToken (&tok));
+	}
+}
+
+// =============================================================================
+//
+void Lexer::MustGetAnyOf (const List< EToken >& toks)
+{
+	if (!GetNext())
+		Error ("unexpected EOF");
+
+	for (EToken tok : toks)
+		if (GetTokenType() == tok)
+			return;
+
+	String toknames;
+
+	for (const EToken& tokType : toks)
+	{
+		if (&tokType == &toks.Last())
+			toknames += " or ";
+		elif (toknames.IsEmpty() == false)
+			toknames += ", ";
+
+		toknames += DescribeTokenType (tokType);
+	}
+
+	Error ("expected %1, got %2", toknames, DescribeToken (GetToken()));
+}
+
+// =============================================================================
+//
+int Lexer::GetOneSymbol (const StringList& syms)
+{
+	if (!GetNext())
+		Error ("unexpected EOF");
+
+	if (GetTokenType() == tkSymbol)
+	{
+		for (int i = 0; i < syms.Size(); ++i)
+		{
+			if (syms[i] == GetToken()->text)
+				return i;
+		}
+	}
+
+	Error ("expected one of %1, got %2", syms, DescribeToken (GetToken()));
+	return -1;
+}
+
+// =============================================================================
+//
+void Lexer::TokenMustBe (EToken tok)
+{
+	if (GetTokenType() != tok)
+		Error ("expected %1, got %2", DescribeTokenType (tok),
+			DescribeToken (GetToken()));
+}
+
+// =============================================================================
+//
+String Lexer::DescribeTokenPrivate (EToken tokType, Lexer::Token* tok)
+{
+	if (tokType < tkLastNamedToken)
+		return "\"" + LexerScanner::GetTokenString (tokType) + "\"";
+
+	switch (tokType)
+	{
+		case tkSymbol:	return tok ? tok->text : "a symbol";
+		case tkNumber:	return tok ? tok->text : "a number";
+		case tkString:	return tok ? ("\"" + tok->text + "\"") : "a string";
+		case tkAny:	return tok ? tok->text : "any token";
+		default: break;
+	}
+
+	return "";
+}
+
+// =============================================================================
+//
+bool Lexer::PeekNext (Lexer::Token* tk)
+{
+	Iterator pos = mTokenPosition;
+	bool r = GetNext();
+
+	if (r && tk != null)
+		*tk = *mTokenPosition;
+
+	mTokenPosition = pos;
+	return r;
+}
+
+// =============================================================================
+//
+Lexer* Lexer::GetCurrentLexer()
+{
+	return gMainLexer;
+}
+
+// =============================================================================
+//
+String Lexer::PeekNextString (int a)
+{
+	if (mTokenPosition + a >= mTokens.end())
+		return "";
+
+	Iterator oldpos = mTokenPosition;
+	mTokenPosition += a;
+	String result = GetToken()->text;
+	mTokenPosition = oldpos;
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Lexer.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,114 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_LEXER_H
+#define BOTC_LEXER_H
+
+#include "Main.h"
+#include "LexerScanner.h"
+
+class Lexer
+{
+types:
+	struct Token
+	{
+		EToken		type;
+		String		text;
+		String		file;
+		int			line;
+		int			column;
+	};
+
+	using TokenList	= List<Token>;
+	using Iterator	= TokenList::Iterator;
+
+public:
+	Lexer();
+	~Lexer();
+
+	void	ProcessFile (String file_name);
+	bool	GetNext (EToken req = tkAny);
+	void	MustGetNext (EToken tok = tkAny);
+	void	MustGetAnyOf (const List<EToken>& toks);
+	int		GetOneSymbol (const StringList& syms);
+	void	TokenMustBe (EToken tok);
+	bool	PeekNext (Token* tk = null);
+
+	inline bool HasValidToken() const
+	{
+		return (mTokenPosition < mTokens.end() && mTokenPosition >= mTokens.begin());
+	}
+
+	inline Token* GetToken() const
+	{
+		assert (HasValidToken() == true);
+		return &(*mTokenPosition);
+	}
+
+	inline bool IsAtEnd() const
+	{
+		return mTokenPosition == mTokens.end();
+	}
+
+	inline EToken GetTokenType() const
+	{
+		return GetToken()->type;
+	}
+
+	// If @tok is given, describes the token. If not, describes @tok_type.
+	static inline String DescribeTokenType (EToken toktype)
+	{
+		return DescribeTokenPrivate (toktype, null);
+	}
+
+	static inline String DescribeToken (Token* tok)
+	{
+		return DescribeTokenPrivate (tok->type, tok);
+	}
+
+	static Lexer* GetCurrentLexer();
+
+	inline void Skip (int a = 1)
+	{
+		mTokenPosition += a;
+	}
+
+	String PeekNextString (int a = 1);
+
+private:
+	TokenList		mTokens;
+	Iterator		mTokenPosition;
+
+	// read a mandatory token from scanner
+	void MustGetFromScanner (LexerScanner& sc, EToken tt = tkAny);
+	void CheckFileHeader (LexerScanner& sc);
+
+	static String DescribeTokenPrivate (EToken tok_type, Token* tok);
+};
+
+#endif // BOTC_LEXER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/LexerScanner.cc	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,290 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <cstdio>
+#include <cstdlib>
+#include <cassert>
+#include <cstring>
+#include <string>
+#include "LexerScanner.h"
+#include "Lexer.h"
+
+static const String gTokenStrings[] =
+{
+	"==",
+	"[]",
+	"+=",
+	"-=",
+	"*=",
+	"/=",
+	"%=",
+	"'",
+	"$",
+	"(",
+	")",
+	"[",
+	"]",
+	"{",
+	"}",
+	"=",
+	"+",
+	"-",
+	"*",
+	"/",
+	"%",
+	",",
+	"<",
+	">",
+	".",
+	":",
+	";",
+	"#",
+	"!",
+	"->",
+	"bool",
+	"break",
+	"case",
+	"continue",
+	"const",
+	"default",
+	"do",
+	"else",
+	"event",
+	"eventdef",
+	"for",
+	"funcdef",
+	"goto",
+	"if",
+	"int",
+	"mainloop",
+	"onenter",
+	"onexit",
+	"state",
+	"switch",
+	"str",
+	"void",
+	"while",
+	"enum",
+	"func",
+	"return",
+};
+
+static_assert (countof (gTokenStrings) == (int) tkLastNamedToken + 1,
+	"Count of gTokenStrings is not the same as the amount of named token identifiers.");
+
+// =============================================================================
+//
+LexerScanner::LexerScanner (FILE* fp) :
+	mLine (1)
+{
+	long fsize, bytes;
+
+	fseek (fp, 0l, SEEK_END);
+	fsize = ftell (fp);
+	rewind (fp);
+	mData = new char[fsize];
+	mPosition = mLineBreakPosition = &mData[0];
+	bytes = fread (mData, 1, fsize, fp);
+	assert (bytes >= fsize);
+}
+
+// =============================================================================
+//
+LexerScanner::~LexerScanner()
+{
+	delete mData;
+}
+
+// =============================================================================
+//
+bool LexerScanner::CheckString (const char* c, int flags)
+{
+	bool r = strncmp (mPosition, c, strlen (c)) == 0;
+
+	// There is to be a non-symbol character after words
+	if (r && (flags & FCheckWord) && IsSymbolChar (mPosition[strlen (c)], true))
+		r = false;
+
+	// Advance the cursor unless we want to just peek
+	if (r && !(flags & FCheckPeek))
+		mPosition += strlen (c);
+
+	return r;
+}
+
+// =============================================================================
+//
+bool LexerScanner::GetNextToken()
+{
+	mTokenText = "";
+
+	while (isspace (*mPosition))
+		Skip();
+
+	// Check for comments
+	if (strncmp (mPosition, "//", 2) == 0)
+	{
+		mPosition += 2;
+
+		while (*mPosition != '\n')
+			Skip();
+
+		return GetNextToken();
+	}
+	elif (strncmp (mPosition, "/*", 2) == 0)
+	{
+		Skip (2); // skip the start symbols
+
+		while (strncmp (mPosition, "*/", 2) != 0)
+			Skip();
+
+		Skip (2); // skip the end symbols
+		return GetNextToken();
+	}
+
+	if (*mPosition == '\0')
+		return false;
+
+	// Check tokens
+	for (int i = 0; i < countof (gTokenStrings); ++i)
+	{
+		int flags = 0;
+
+		if (i >= tkFirstNamedToken)
+			flags |= FCheckWord;
+
+		if (CheckString (gTokenStrings[i], flags))
+		{
+			mTokenText = gTokenStrings[i];
+			mTokenType = (EToken) i;
+			return true;
+		}
+	}
+
+	// Check and parse string
+	if (*mPosition == '\"')
+	{
+		mPosition++;
+
+		while (*mPosition != '\"')
+		{
+			if (!*mPosition)
+				Error ("unterminated string");
+
+			if (CheckString ("\\n"))
+			{
+				mTokenText += '\n';
+				continue;
+			}
+			elif (CheckString ("\\t"))
+			{
+				mTokenText += '\t';
+				continue;
+			}
+			elif (CheckString ("\\\""))
+			{
+				mTokenText += '"';
+				continue;
+			}
+
+			mTokenText += *mPosition++;
+		}
+
+		mTokenType = tkString;
+		Skip(); // skip the final quote
+		return true;
+	}
+
+	if (isdigit (*mPosition))
+	{
+		while (isdigit (*mPosition))
+			mTokenText += *mPosition++;
+
+		mTokenType = tkNumber;
+		return true;
+	}
+
+	if (IsSymbolChar (*mPosition, false))
+	{
+		mTokenType = tkSymbol;
+
+		do
+		{
+			if (!IsSymbolChar (*mPosition, true))
+				break;
+
+			mTokenText += *mPosition++;
+		} while (*mPosition != '\0');
+
+		return true;
+	}
+
+	Error ("unknown character \"%1\"", *mPosition);
+	return false;
+}
+
+// =============================================================================
+//
+void LexerScanner::Skip()
+{
+	if (*mPosition == '\n')
+	{
+		mLine++;
+		mLineBreakPosition = mPosition;
+	}
+
+	mPosition++;
+}
+
+// =============================================================================
+//
+void LexerScanner::Skip (int chars)
+{
+	for (int i = 0; i < chars; ++i)
+		Skip();
+}
+
+// =============================================================================
+//
+String LexerScanner::GetTokenString (EToken a)
+{
+	assert ((int) a <= tkLastNamedToken);
+	return gTokenStrings[a];
+}
+
+// =============================================================================
+//
+String LexerScanner::ReadLine()
+{
+	String line;
+
+	while (*mPosition != '\n')
+		line += *(mPosition++);
+
+	return line;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/LexerScanner.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,114 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_LEXER_SCANNER_H
+#define BOTC_LEXER_SCANNER_H
+
+#include <climits>
+#include "Main.h"
+
+class LexerScanner
+{
+	types:
+		struct PositionInfo
+		{
+			char*	pos;
+			char*	line_break_pos;
+			int		line;
+		};
+
+		// Flags for check_string()
+		enum
+		{
+			FCheckWord = (1 << 0),   // must be followed by whitespace
+			FCheckPeek = (1 << 1),   // don't advance cursor
+		};
+
+	public:
+		static inline bool IsSymbolChar (char c, bool allownumbers)
+		{
+			if (allownumbers && (c >= '0' && c <= '9'))
+				return true;
+
+			return (c >= 'a' && c <= 'z') ||
+				   (c >= 'A' && c <= 'Z') ||
+				   (c == '_');
+		}
+
+		LexerScanner (FILE* fp);
+		~LexerScanner();
+		bool GetNextToken();
+		String ReadLine();
+
+		inline const String& GetTokenText() const
+		{
+			return mTokenText;
+		}
+
+		inline int GetLine() const
+		{
+			return mLine;
+		}
+
+		inline int GetColumn() const
+		{
+			return mPosition - mLineBreakPosition;
+		}
+
+		inline EToken GetTokenType() const
+		{
+			return mTokenType;
+		}
+
+		static String GetTokenString (EToken a);
+
+	private:
+		char*			mData;
+		char*			mPosition;
+		char*			mLineBreakPosition;
+		String			mTokenText,
+						mLastToken;
+		EToken			mTokenType;
+		int				mLine;
+
+		bool			CheckString (const char* c, int flags = 0);
+
+		// Yields a copy of the current position information.
+		PositionInfo	GetPosition() const;
+
+		// Sets the current position based on given data.
+		void			SetPosition (const PositionInfo& a);
+
+		// Skips one character
+		void			Skip();
+
+		// Skips many characters
+		void			Skip (int chars);
+};
+
+#endif // BOTC_LEXER_SCANNER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Main.cc	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,195 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 "Main.h"
+#include "Events.h"
+#include "Commands.h"
+#include "StringTable.h"
+#include "Variables.h"
+#include "DataBuffer.h"
+#include "Parser.h"
+#include "Lexer.h"
+#include "GitInformation.h"
+
+int main (int argc, char** argv)
+{
+	try
+	{
+		// Intepret command-line parameters:
+		// -l: list commands
+		// I guess there should be a better way to do this.
+		if (argc == 2 && String (argv[1]) == "-l")
+		{
+			Print ("Begin list of commands:\n");
+			Print ("------------------------------------------------------\n");
+
+			BotscriptParser parser;
+			parser.SetReadOnly (true);
+			parser.ParseBotscript ("botc_defs.bts");
+
+			for (CommandInfo* comm : GetCommands())
+				Print ("%1\n", comm->GetSignature());
+
+			Print ("------------------------------------------------------\n");
+			Print ("End of command list\n");
+			exit (0);
+		}
+
+		// Print header
+		String header;
+		String headerline;
+		header = Format (APPNAME " version %1", GetVersionString (ELongForm));
+
+#ifdef DEBUG
+		header += " (debug build)";
+#endif
+
+		for (int i = 0; i < header.Length() / 2; ++i)
+			headerline += "-=";
+
+		headerline += '-';
+		Print ("%2\n\n%1\n\n%2\n\n", header, headerline);
+
+		if (argc < 2)
+		{
+			fprintf (stderr, "usage: %s <infile> [outfile] # compiles botscript\n", argv[0]);
+			fprintf (stderr, "       %s -l                 # lists commands\n", argv[0]);
+			exit (1);
+		}
+
+		String outfile;
+
+		if (argc < 3)
+			outfile = MakeObjectFileName (argv[1]);
+		else
+			outfile = argv[2];
+
+		// Prepare reader and writer
+		BotscriptParser* parser = new BotscriptParser;
+
+		// We're set, begin parsing :)
+		Print ("Parsing script...\n");
+		parser->ParseBotscript (argv[1]);
+		Print ("Script parsed successfully.\n");
+
+		// Parse done, print statistics and write to file
+		int globalcount = g_GlobalVariables.Size();
+		int stringcount = CountStringsInTable();
+		Print ("%1 / %2 strings written\n", stringcount, gMaxStringlistSize);
+		Print ("%1 / %2 global variables\n", globalcount, gMaxGlobalVars);
+		Print ("%1 / %2 events\n", parser->GetNumEvents(), gMaxEvents);
+		Print ("%1 state%s1\n", parser->GetNumStates());
+
+		parser->WriteToFile (outfile);
+
+		// Clear out the junk
+		delete parser;
+
+		// Done!
+		exit (0);
+	}
+	catch (ScriptError& e)
+	{
+		PrintTo (stderr, "error: %1\n", e.what());
+	}
+}
+
+// ============================================================================
+//
+// Mutates given filename to an object filename
+//
+String MakeObjectFileName (String s)
+{
+	// Locate the extension and chop it out
+	int extdot = s.LastIndexOf (".");
+
+	if (extdot >= s.Length() - 4)
+		s -= (s.Length() - extdot);
+
+	s += ".o";
+	return s;
+}
+
+// ============================================================================
+//
+EType GetTypeByName (String t)
+{
+	t = t.ToLowercase();
+	return	(t == "int") ? EIntType :
+			(t == "str") ? EStringType :
+			(t == "void") ? EVoidType :
+			(t == "bool") ? EBoolType :
+			EUnknownType;
+}
+
+
+// ============================================================================
+//
+// Inverse operation - type name by value
+//
+String GetTypeName (EType type)
+{
+	switch (type)
+	{
+		case EIntType: return "int"; break;
+		case EStringType: return "str"; break;
+		case EVoidType: return "void"; break;
+		case EBoolType: return "bool"; break;
+		case EUnknownType: return "???"; break;
+	}
+
+	return "";
+}
+
+// =============================================================================
+//
+String MakeVersionString (int major, int minor, int patch)
+{
+	String ver = Format ("%1.%2", major, minor);
+
+	if (patch != 0)
+	{
+		ver += ".";
+		ver += patch;
+	}
+
+	return ver;
+}
+
+// =============================================================================
+//
+String GetVersionString (EFormLength len)
+{
+	String tag (GIT_DESCRIPTION);
+	String version = tag;
+
+	if (len == ELongForm && tag.EndsWith ("-pre"))
+		version += "-" + String (GIT_HASH).Mid (0, 8);
+
+	return version;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Main.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,86 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_MAIN_H
+#define BOTC_MAIN_H
+
+#if !defined (__cplusplus) || __cplusplus < 201103L
+# error botc requires a C++11-compliant compiler to be built
+#endif
+
+#include <cstdio>
+#include <cstdarg>
+#include <cstdint>
+#include "Property.h"
+#include "Types.h"
+#include "Containers.h"
+#include "String.h"
+#include "Format.h"
+#include "BotStuff.h"
+#include "Tokens.h"
+
+// Application name and version
+#define APPNAME "botc"
+#define VERSION_MAJOR	1
+#define VERSION_MINOR	0
+#define VERSION_PATCH 	0
+
+#define MAKE_VERSION_NUMBER(MAJ, MIN, PAT) ((MAJ * 10000) + (MIN * 100) + PAT)
+#define VERSION_NUMBER MAKE_VERSION_NUMBER (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)
+
+// On Windows, files are case-insensitive
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)
+#define FILE_CASEINSENSITIVE
+#endif
+
+#define elif else if
+
+#define types public
+#define countof(A) ((int) (sizeof A / sizeof *A))
+
+// Shortcut for zeroing something
+#define ZERO(obj) memset (&obj, 0, sizeof (obj));
+
+enum EFormLength
+{
+	ELongForm,
+	EShortForm
+};
+
+String MakeObjectFileName (String s);
+EType GetTypeByName (String token);
+String GetTypeName (EType type);
+String GetVersionString (EFormLength len);
+String MakeVersionString (int major, int minor, int patch);
+
+#ifndef __GNUC__
+#define __attribute__(X)
+#endif
+#define deprecated __attribute__ ((deprecated))
+
+#endif // BOTC_MAIN_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Parser.cc	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,1640 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 "Parser.h"
+#include "Events.h"
+#include "Commands.h"
+#include "StringTable.h"
+#include "Variables.h"
+#include "Containers.h"
+#include "Lexer.h"
+#include "DataBuffer.h"
+
+#define SCOPE(n) (mScopeStack[mScopeCursor - n])
+
+// ============================================================================
+//
+BotscriptParser::BotscriptParser() :
+	mReadOnly (false),
+	mLexer (new Lexer) {}
+
+// ============================================================================
+//
+BotscriptParser::~BotscriptParser()
+{
+	delete mLexer;
+}
+
+// ============================================================================
+//
+void BotscriptParser::CheckToplevel()
+{
+	if (mCurrentMode != ETopLevelMode)
+		Error ("%1-statements may only be defined at top level!", GetTokenString());
+}
+
+// ============================================================================
+//
+void BotscriptParser::CheckNotToplevel()
+{
+	if (mCurrentMode == ETopLevelMode)
+		Error ("%1-statements must not be defined at top level!", GetTokenString());
+}
+
+// ============================================================================
+// 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 BotscriptParser::ParseBotscript (String fileName)
+{
+	// Lex and preprocess the file
+	mLexer->ProcessFile (fileName);
+
+	mCurrentMode = ETopLevelMode;
+	mNumStates = 0;
+	mNumEvents = 0;
+	mScopeCursor = 0;
+	mStateSpawnDefined = false;
+	mGotMainLoop = false;
+	mIfExpression = null;
+	mCanElse = false;
+
+	// Zero the entire block stack first
+	// TODO: this shouldn't be necessary
+	for (int i = 0; i < MAX_SCOPE; i++)
+		ZERO (mScopeStack[i]);
+
+	while (mLexer->GetNext())
+	{
+		// Check if else is potentically valid
+		if (TokenIs (tkElse) && mCanElse == false)
+			Error ("else without preceding if");
+
+		if (TokenIs (tkElse) == false)
+			mCanElse = false;
+
+		switch (mLexer->GetToken()->type)
+		{
+			case tkState:
+				ParseStateBlock();
+				break;
+
+			case tkEvent:
+				ParseEventBlock();
+				break;
+
+			case tkMainloop:
+				ParseMainloop();
+				break;
+
+			case tkOnenter:
+			case tkOnexit:
+				ParseOnEnterExit();
+				break;
+
+			case tkInt:
+			case tkStr:
+			case tkVoid:
+				ParseVariableDeclaration();
+				break;
+
+			case tkGoto:
+				ParseGoto();
+				break;
+
+			case tkIf:
+				ParseIf();
+				break;
+
+			case tkElse:
+				ParseElse();
+				break;
+
+			case tkWhile:
+				ParseWhileBlock();
+				break;
+
+			case tkFor:
+				ParseForBlock();
+				break;
+
+			case tkDo:
+				ParseDoBlock();
+				break;
+
+			case tkSwitch:
+				ParseSwitchBlock();
+				break;
+
+			case tkCase:
+				ParseSwitchCase();
+				break;
+
+			case tkDefault:
+				ParseSwitchDefault();
+				break;
+
+			case tkBreak:
+				ParseBreak();
+				break;
+
+			case tkContinue:
+				ParseContinue();
+				break;
+
+			case tkBraceEnd:
+				ParseBlockEnd();
+				break;
+
+			case tkConst:
+				ParseConst();
+				break;
+
+			case tkEventdef:
+				ParseEventdef();
+				break;
+
+			case tkFuncdef:
+				ParseFuncdef();
+				break;
+
+			default:
+			{
+				// Check for labels
+				Lexer::Token next;
+
+				if (TokenIs (tkSymbol) &&
+					mLexer->PeekNext (&next) &&
+					next.type == tkColon)
+				{
+					ParseLabel();
+					break;
+				}
+
+				// Check if it's a command
+				CommandInfo* comm = FindCommandByName (GetTokenString());
+
+				if (comm)
+				{
+					buffer()->MergeAndDestroy (ParseCommand (comm));
+					mLexer->MustGetNext (tkSemicolon);
+					continue;
+				}
+
+				// If nothing else, parse it as a statement
+				DataBuffer* b = ParseStatement();
+
+				if (b == false)
+					Error ("unknown token `%1`", GetTokenString());
+
+				buffer()->MergeAndDestroy (b);
+				mLexer->MustGetNext (tkSemicolon);
+			}
+			break;
+		}
+	}
+
+	// ===============================================================================
+	// Script file ended. Do some last checks and write the last things to main buffer
+	if (mCurrentMode != ETopLevelMode)
+		Error ("script did not end at top level; a `}` is missing somewhere");
+
+	if (IsReadOnly() == false)
+	{
+		// stateSpawn must be defined!
+		if (mStateSpawnDefined == false)
+			Error ("script must have a state named `stateSpawn`!");
+
+		// Ensure no goto target is left undefined
+		if (mUndefinedLabels.IsEmpty() == false)
+		{
+			StringList names;
+
+			for (UndefinedLabel& undf : mUndefinedLabels)
+				names << undf.name;
+
+			Error ("labels `%1` are referenced via `goto` but are not defined\n", names);
+		}
+
+		// Dump the last state's onenter and mainloop
+		writeMemberBuffers();
+
+		// String table
+		WriteStringTable();
+	}
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseStateBlock()
+{
+	CheckToplevel();
+	mLexer->MustGetNext (tkString);
+	String statename = GetTokenString();
+
+	// State name must be a word.
+	if (statename.FirstIndexOf (" ") != -1)
+		Error ("state name must be a single word, got `%1`", statename);
+
+	// stateSpawn is special - it *must* be defined. If we
+	// encountered it, then mark down that we have it.
+	if (-statename == "statespawn")
+		mStateSpawnDefined = true;
+
+	// Must end in a colon
+	mLexer->MustGetNext (tkColon);
+
+	// write the previous state's onenter and
+	// mainloop buffers to file now
+	if (mCurrentState.IsEmpty() == false)
+		writeMemberBuffers();
+
+	buffer()->WriteDWord (dhStateName);
+	buffer()->WriteString (statename);
+	buffer()->WriteDWord (dhStateIndex);
+	buffer()->WriteDWord (mNumStates);
+
+	mNumStates++;
+	mCurrentState = statename;
+	mGotMainLoop = false;
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseEventBlock()
+{
+	CheckToplevel();
+	mLexer->MustGetNext (tkString);
+
+	EventDefinition* e = FindEventByName (GetTokenString());
+
+	if (e == null)
+		Error ("bad event, got `%1`\n", GetTokenString());
+
+	mLexer->MustGetNext (tkBraceStart);
+	mCurrentMode = EEventMode;
+	buffer()->WriteDWord (dhEvent);
+	buffer()->WriteDWord (e->number);
+	mNumEvents++;
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseMainloop()
+{
+	CheckToplevel();
+	mLexer->MustGetNext (tkBraceStart);
+
+	mCurrentMode = EMainLoopMode;
+	mMainLoopBuffer->WriteDWord (dhMainLoop);
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseOnEnterExit()
+{
+	CheckToplevel();
+	bool onenter = (TokenIs (tkOnenter));
+	mLexer->MustGetNext (tkBraceStart);
+
+	mCurrentMode = onenter ? EOnenterMode : EOnexitMode;
+	buffer()->WriteDWord (onenter ? dhOnEnter : dhOnExit);
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseVariableDeclaration()
+{
+	// For now, only globals are supported
+	if (mCurrentMode != ETopLevelMode || mCurrentState.IsEmpty() == false)
+		Error ("variables must only be global for now");
+
+	EType type =	(TokenIs (tkInt)) ? EIntType :
+					(TokenIs (tkStr)) ? EStringType :
+					EBoolType;
+
+	mLexer->MustGetNext();
+	String varname = GetTokenString();
+
+	// Var name must not be a number
+	if (varname.IsNumeric())
+		Error ("variable name must not be a number");
+
+	ScriptVariable* var = DeclareGlobalVariable (type, varname);
+	(void) var;
+	mLexer->MustGetNext (tkSemicolon);
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseGoto()
+{
+	CheckNotToplevel();
+
+	// Get the name of the label
+	mLexer->MustGetNext();
+
+	// Find the mark this goto statement points to
+	String target = GetTokenString();
+	ByteMark* mark = buffer()->FindMarkByName (target);
+
+	// If not set, define it
+	if (mark == null)
+	{
+		UndefinedLabel undf;
+		undf.name = target;
+		undf.target = buffer()->AddMark (target);
+		mUndefinedLabels << undf;
+	}
+
+	// Add a reference to the mark.
+	buffer()->WriteDWord (dhGoto);
+	buffer()->AddReference (mark);
+	mLexer->MustGetNext (tkSemicolon);
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseIf()
+{
+	CheckNotToplevel();
+	PushScope();
+
+	// Condition
+	mLexer->MustGetNext (tkParenStart);
+
+	// Read the expression and write it.
+	mLexer->MustGetNext();
+	DataBuffer* c = ParseExpression (EIntType);
+	buffer()->MergeAndDestroy (c);
+
+	mLexer->MustGetNext (tkParenEnd);
+	mLexer->MustGetNext (tkBraceStart);
+
+	// Add a mark - to here temporarily - and add a reference to it.
+	// Upon a closing brace, the mark will be adjusted.
+	ByteMark* mark = buffer()->AddMark ("");
+
+	// Use dhIfNotGoto - 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.
+	buffer()->WriteDWord (dhIfNotGoto);
+	buffer()->AddReference (mark);
+
+	// Store it
+	SCOPE (0).mark1 = mark;
+	SCOPE (0).type = eIfScope;
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseElse()
+{
+	CheckNotToplevel();
+	mLexer->MustGetNext (tkBraceStart);
+
+	// Don't use PushScope as it resets the scope
+	mScopeCursor++;
+
+	if (mScopeCursor >= MAX_SCOPE)
+		Error ("too deep scope");
+
+	if (SCOPE (0).type != eIfScope)
+		Error ("else without preceding if");
+
+	// write down to jump to the end of the else statement
+	// Otherwise we have fall-throughs
+	SCOPE (0).mark2 = buffer()->AddMark ("");
+
+	// Instruction to jump to the end after if block is complete
+	buffer()->WriteDWord (dhGoto);
+	buffer()->AddReference (SCOPE (0).mark2);
+
+	// Move the ifnot mark here and set type to else
+	buffer()->AdjustMark (SCOPE (0).mark1);
+	SCOPE (0).type = eElseScope;
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseWhileBlock()
+{
+	CheckNotToplevel();
+	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.
+	ByteMark* mark1 = buffer()->AddMark (""); // start
+	ByteMark* mark2 = buffer()->AddMark (""); // end
+
+	// Condition
+	mLexer->MustGetNext (tkParenStart);
+	mLexer->MustGetNext();
+	DataBuffer* expr = ParseExpression (EIntType);
+	mLexer->MustGetNext (tkParenEnd);
+	mLexer->MustGetNext (tkBraceStart);
+
+	// write condition
+	buffer()->MergeAndDestroy (expr);
+
+	// Instruction to go to the end if it fails
+	buffer()->WriteDWord (dhIfNotGoto);
+	buffer()->AddReference (mark2);
+
+	// Store the needed stuff
+	SCOPE (0).mark1 = mark1;
+	SCOPE (0).mark2 = mark2;
+	SCOPE (0).type = eWhileScope;
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseForBlock()
+{
+	CheckNotToplevel();
+	PushScope();
+
+	// Initializer
+	mLexer->MustGetNext (tkParenStart);
+	mLexer->MustGetNext();
+	DataBuffer* init = ParseStatement();
+
+	if (init == null)
+		Error ("bad statement for initializer of for");
+
+	mLexer->MustGetNext (tkSemicolon);
+
+	// Condition
+	mLexer->MustGetNext();
+	DataBuffer* cond = ParseExpression (EIntType);
+
+	if (cond == null)
+		Error ("bad statement for condition of for");
+
+	mLexer->MustGetNext (tkSemicolon);
+
+	// Incrementor
+	mLexer->MustGetNext();
+	DataBuffer* incr = ParseStatement();
+
+	if (incr == null)
+		Error ("bad statement for incrementor of for");
+
+	mLexer->MustGetNext (tkParenEnd);
+	mLexer->MustGetNext (tkBraceStart);
+
+	// First, write out the initializer
+	buffer()->MergeAndDestroy (init);
+
+	// Init two marks
+	ByteMark* mark1 = buffer()->AddMark ("");
+	ByteMark* mark2 = buffer()->AddMark ("");
+
+	// Add the condition
+	buffer()->MergeAndDestroy (cond);
+	buffer()->WriteDWord (dhIfNotGoto);
+	buffer()->AddReference (mark2);
+
+	// Store the marks and incrementor
+	SCOPE (0).mark1 = mark1;
+	SCOPE (0).mark2 = mark2;
+	SCOPE (0).buffer1 = incr;
+	SCOPE (0).type = eForScope;
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseDoBlock()
+{
+	CheckNotToplevel();
+	PushScope();
+	mLexer->MustGetNext (tkBraceStart);
+	SCOPE (0).mark1 = buffer()->AddMark ("");
+	SCOPE (0).type = eDoScope;
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseSwitchBlock()
+{
+	// 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
+
+	CheckNotToplevel();
+	PushScope();
+	mLexer->MustGetNext (tkParenStart);
+	mLexer->MustGetNext();
+	buffer()->MergeAndDestroy (ParseExpression (EIntType));
+	mLexer->MustGetNext (tkParenEnd);
+	mLexer->MustGetNext (tkBraceStart);
+	SCOPE (0).type = eSwitchScope;
+	SCOPE (0).mark1 = buffer()->AddMark (""); // end mark
+	SCOPE (0).buffer1 = null; // default header
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseSwitchCase()
+{
+	// case is only allowed inside switch
+	if (SCOPE (0).type != eSwitchScope)
+		Error ("case label outside switch");
+
+	// Get the literal (Zandronum does not support expressions here)
+	mLexer->MustGetNext (tkNumber);
+	int num = mLexer->GetToken()->text.ToLong();
+	mLexer->MustGetNext (tkColon);
+
+	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.
+	//
+	// We null the switch buffer for the case-go-to statement as
+	// we want it all under the switch, not into the case-buffers.
+	mSwitchBuffer = null;
+	buffer()->WriteDWord (dhCaseGoto);
+	buffer()->WriteDWord (num);
+	AddSwitchCase (null);
+	SCOPE (0).casenumbers[SCOPE (0).casecursor] = num;
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseSwitchDefault()
+{
+	if (SCOPE (0).type != eSwitchScope)
+		Error ("default label outside switch");
+
+	if (SCOPE (0).buffer1)
+		Error ("multiple default labels in one switch");
+
+	mLexer->MustGetNext (tkColon);
+
+	// 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 dhDrop manually if we end up in
+	// a default.
+	DataBuffer* b = new DataBuffer;
+	SCOPE (0).buffer1 = b;
+	b->WriteDWord (dhDrop);
+	b->WriteDWord (dhGoto);
+	AddSwitchCase (b);
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseBreak()
+{
+	if (mScopeCursor == 0)
+		Error ("unexpected `break`");
+
+	buffer()->WriteDWord (dhGoto);
+
+	// switch and if use mark1 for the closing point,
+	// for and while use mark2.
+	switch (SCOPE (0).type)
+	{
+		case eIfScope:
+		case eSwitchScope:
+		{
+			buffer()->AddReference (SCOPE (0).mark1);
+		} break;
+
+		case eForScope:
+		case eWhileScope:
+		{
+			buffer()->AddReference (SCOPE (0).mark2);
+		} break;
+
+		default:
+		{
+			Error ("unexpected `break`");
+		} break;
+	}
+
+	mLexer->MustGetNext (tkSemicolon);
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseContinue()
+{
+	mLexer->MustGetNext (tkSemicolon);
+
+	int curs;
+	bool found = false;
+
+	// Fall through the scope until we find a loop block
+	for (curs = mScopeCursor; curs > 0 && !found; curs--)
+	{
+		switch (mScopeStack[curs].type)
+		{
+			case eForScope:
+			case eWhileScope:
+			case eDoScope:
+			{
+				buffer()->WriteDWord (dhGoto);
+				buffer()->AddReference (mScopeStack[curs].mark1);
+				found = true;
+			} break;
+
+			default:
+				break;
+		}
+	}
+
+	// No loop blocks
+	if (found == false)
+		Error ("`continue`-statement not inside a loop");
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseBlockEnd()
+{
+	// Closing brace
+	// If we're in the block stack, we're descending down from it now
+	if (mScopeCursor > 0)
+	{
+		switch (SCOPE (0).type)
+		{
+			case eIfScope:
+				// Adjust the closing mark.
+				buffer()->AdjustMark (SCOPE (0).mark1);
+
+				// We're returning from if, thus else can be next
+				mCanElse = true;
+				break;
+
+			case eElseScope:
+				// else instead uses mark1 for itself (so if expression
+				// fails, jump to else), mark2 means end of else
+				buffer()->AdjustMark (SCOPE (0).mark2);
+				break;
+
+			case eForScope:
+				// write the incrementor at the end of the loop block
+				buffer()->MergeAndDestroy (SCOPE (0).buffer1);
+			case eWhileScope:
+				// write down the instruction to go back to the start of the loop
+				buffer()->WriteDWord (dhGoto);
+				buffer()->AddReference (SCOPE (0).mark1);
+
+				// Move the closing mark here since we're at the end of the while loop
+				buffer()->AdjustMark (SCOPE (0).mark2);
+				break;
+
+			case eDoScope:
+			{
+				mLexer->MustGetNext (tkWhile);
+				mLexer->MustGetNext (tkParenStart);
+				mLexer->MustGetNext();
+				DataBuffer* expr = ParseExpression (EIntType);
+				mLexer->MustGetNext (tkParenEnd);
+				mLexer->MustGetNext (tkSemicolon);
+
+				// If the condition runs true, go back to the start.
+				buffer()->MergeAndDestroy (expr);
+				buffer()->WriteDWord (dhIfGoto);
+				buffer()->AddReference (SCOPE (0).mark1);
+				break;
+			}
+
+			case eSwitchScope:
+			{
+				// Switch closes. Move down to the record buffer of
+				// the lower block.
+				if (SCOPE (1).casecursor != -1)
+					mSwitchBuffer = SCOPE (1).casebuffers[SCOPE (1).casecursor];
+				else
+					mSwitchBuffer = 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)
+					buffer()->MergeAndDestroy (SCOPE (0).buffer1);
+				else
+				{
+					buffer()->WriteDWord (dhDrop);
+					buffer()->WriteDWord (dhGoto);
+					buffer()->AddReference (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] == null)
+						continue;
+
+					buffer()->AdjustMark (SCOPE (0).casemarks[u]);
+					buffer()->MergeAndDestroy (SCOPE (0).casebuffers[u]);
+				}
+
+				// Move the closing mark here
+				buffer()->AdjustMark (SCOPE (0).mark1);
+				break;
+			}
+
+			case eUnknownScope:
+				break;
+		}
+
+		// Descend down the stack
+		mScopeCursor--;
+		return;
+	}
+
+	int dataheader =	(mCurrentMode == EEventMode) ? dhEndEvent :
+						(mCurrentMode == EMainLoopMode) ? dhEndMainLoop :
+						(mCurrentMode == EOnenterMode) ? dhEndOnEnter :
+						(mCurrentMode == EOnexitMode) ? dhEndOnExit : -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.
+	buffer()->WriteDWord (dataheader);
+	mCurrentMode = ETopLevelMode;
+	mLexer->GetNext (tkSemicolon);
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseConst()
+{
+	ConstantInfo info;
+
+	// Get the type
+	mLexer->MustGetNext();
+	String typestring = GetTokenString();
+	info.type = GetTypeByName (typestring);
+
+	if (info.type == EUnknownType || info.type == EVoidType)
+		Error ("unknown type `%1` for constant", typestring);
+
+	mLexer->MustGetNext();
+	info.name = GetTokenString();
+
+	mLexer->MustGetNext (tkAssign);
+
+	switch (info.type)
+	{
+		case EBoolType:
+		case EIntType:
+		{
+			mLexer->MustGetNext (tkNumber);
+		} break;
+
+		case EStringType:
+		{
+			mLexer->MustGetNext (tkString);
+		} break;
+
+		case EUnknownType:
+		case EVoidType:
+			break;
+	}
+
+	info.val = mLexer->GetToken()->text;
+	mConstants << info;
+
+	mLexer->MustGetNext (tkSemicolon);
+}
+
+// ============================================================================
+//
+void BotscriptParser::ParseLabel()
+{
+	CheckNotToplevel();
+	String labelName = GetTokenString();
+	ByteMark* mark = null;
+
+	// want no conflicts..
+	if (FindCommandByName (labelName))
+		Error ("label name `%1` conflicts with command name\n", labelName);
+
+	if (FindGlobalVariable (labelName))
+		Error ("label name `%1` conflicts with variable\n", labelName);
+
+	// See if a mark already exists for this label
+	for (UndefinedLabel& undf : mUndefinedLabels)
+	{
+		if (undf.name != labelName)
+			continue;
+
+		mark = undf.target;
+		buffer()->AdjustMark (mark);
+
+		// No longer undefined
+		mUndefinedLabels.Remove (undf);
+		break;
+	}
+
+	// Not found in unmarked lists, define it now
+	if (mark == null)
+		buffer()->AddMark (labelName);
+
+	mLexer->MustGetNext (tkColon);
+}
+
+// =============================================================================
+//
+void BotscriptParser::ParseEventdef()
+{
+	EventDefinition* e = new EventDefinition;
+
+	mLexer->MustGetNext (tkNumber);
+	e->number = GetTokenString().ToLong();
+	mLexer->MustGetNext (tkColon);
+	mLexer->MustGetNext (tkSymbol);
+	e->name = mLexer->GetToken()->text;
+	mLexer->MustGetNext (tkParenStart);
+	mLexer->MustGetNext (tkParenEnd);
+	mLexer->MustGetNext (tkSemicolon);
+	AddEvent (e);
+}
+
+// =============================================================================
+//
+void BotscriptParser::ParseFuncdef()
+{
+	CommandInfo* comm = new CommandInfo;
+
+	// Number
+	mLexer->MustGetNext (tkNumber);
+	comm->number = mLexer->GetToken()->text.ToLong();
+
+	mLexer->MustGetNext (tkColon);
+
+	// Name
+	mLexer->MustGetNext (tkSymbol);
+	comm->name = mLexer->GetToken()->text;
+
+	mLexer->MustGetNext (tkColon);
+
+	// Return value
+	mLexer->MustGetAnyOf ({tkInt, tkVoid, tkBool, tkStr});
+	comm->returnvalue = GetTypeByName (mLexer->GetToken()->text); // TODO
+	assert (comm->returnvalue != -1);
+
+	mLexer->MustGetNext (tkColon);
+
+	// Num args
+	mLexer->MustGetNext (tkNumber);
+	comm->numargs = mLexer->GetToken()->text.ToLong();
+
+	mLexer->MustGetNext (tkColon);
+
+	// Max args
+	mLexer->MustGetNext (tkNumber);
+	comm->maxargs = mLexer->GetToken()->text.ToLong();
+
+	// Argument types
+	int curarg = 0;
+
+	while (curarg < comm->maxargs)
+	{
+		CommandArgument arg;
+		mLexer->MustGetNext (tkColon);
+		mLexer->MustGetAnyOf ({tkInt, tkBool, tkStr});
+		EType type = GetTypeByName (mLexer->GetToken()->text);
+		assert (type != -1 && type != EVoidType);
+		arg.type = type;
+
+		mLexer->MustGetNext (tkParenStart);
+		mLexer->MustGetNext (tkSymbol);
+		arg.name = mLexer->GetToken()->text;
+
+		// If this is an optional parameter, we need the default value.
+		if (curarg >= comm->numargs)
+		{
+			mLexer->MustGetNext (tkAssign);
+
+			switch (type)
+			{
+				case EIntType:
+				case EBoolType:
+					mLexer->MustGetNext (tkNumber);
+					break;
+
+				case EStringType:
+					mLexer->MustGetNext (tkString);
+					break;
+
+				case EUnknownType:
+				case EVoidType:
+					break;
+			}
+
+			arg.defvalue = mLexer->GetToken()->text.ToLong();
+		}
+
+		mLexer->MustGetNext (tkParenEnd);
+		comm->args << arg;
+		curarg++;
+	}
+
+	mLexer->MustGetNext (tkSemicolon);
+	AddCommandDefinition (comm);
+}
+
+// ============================================================================
+// Parses a command call
+DataBuffer* BotscriptParser::ParseCommand (CommandInfo* comm)
+{
+	DataBuffer* r = new DataBuffer (64);
+
+	if (mCurrentMode == ETopLevelMode)
+		Error ("command call at top level");
+
+	mLexer->MustGetNext (tkParenStart);
+	mLexer->MustGetNext();
+
+	int curarg = 0;
+
+	while (1)
+	{
+		if (TokenIs (tkParenEnd))
+		{
+			if (curarg < comm->numargs)
+				Error ("too few arguments passed to %1\n\tusage is: %2",
+					comm->name, comm->GetSignature());
+
+			break;
+			curarg++;
+		}
+
+		if (curarg >= comm->maxargs)
+			Error ("too many arguments passed to %1\n\tusage is: %2",
+				comm->name, comm->GetSignature());
+
+		r->MergeAndDestroy (ParseExpression (comm->args[curarg].type));
+		mLexer->MustGetNext();
+
+		if (curarg < comm->numargs - 1)
+		{
+			mLexer->TokenMustBe (tkComma);
+			mLexer->MustGetNext();
+		}
+		else if (curarg < comm->maxargs - 1)
+		{
+			// Can continue, but can terminate as well.
+			if (TokenIs (tkParenEnd))
+			{
+				curarg++;
+				break;
+			}
+			else
+			{
+				mLexer->TokenMustBe (tkComma);
+				mLexer->MustGetNext();
+			}
+		}
+
+		curarg++;
+	}
+
+	// If the script skipped any optional arguments, fill in defaults.
+	while (curarg < comm->maxargs)
+	{
+		r->WriteDWord (dhPushNumber);
+		r->WriteDWord (comm->args[curarg].defvalue);
+		curarg++;
+	}
+
+	r->WriteDWord (dhCommand);
+	r->WriteDWord (comm->number);
+	r->WriteDWord (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;
+	}
+
+	return false;
+}
+
+// ============================================================================
+// Finds an operator's corresponding dataheader
+//
+static word GetDataHeaderByOperator (ScriptVariable* var, int oper)
+{
+	if (IsAssignmentOperator (oper))
+	{
+		if (var == null)
+			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 dhAddGlobalVar;
+			case OPER_ASSIGNSUB: return dhSubtractGlobalVar;
+			case OPER_ASSIGNMUL: return dhMultiplyGlobalVar;
+			case OPER_ASSIGNDIV: return dhDivideGlobalVar;
+			case OPER_ASSIGNMOD: return dhModGlobalVar;
+			case OPER_ASSIGN: return dhAssignGlobalVar;
+
+			default: Error ("bad assignment operator!\n");
+		}
+	}
+
+	switch (oper)
+	{
+		case OPER_ADD: return dhAdd;
+		case OPER_SUBTRACT: return dhSubtract;
+		case OPER_MULTIPLY: return dhMultiply;
+		case OPER_DIVIDE: return dhDivide;
+		case OPER_MODULUS: return dhModulus;
+		case OPER_EQUALS: return dhEquals;
+		case OPER_NOTEQUALS: return dhNotEquals;
+		case OPER_LESSTHAN: return dhLessThan;
+		case OPER_GREATERTHAN: return dhGreaterThan;
+		case OPER_LESSTHANEQUALS: return dhAtMost;
+		case OPER_GREATERTHANEQUALS: return dhAtLeast;
+		case OPER_LEFTSHIFT: return dhLeftShift;
+		case OPER_RIGHTSHIFT: return dhRightShift;
+		case OPER_OR: return dhOrLogical;
+		case OPER_AND: return dhAndLogical;
+		case OPER_BITWISEOR: return dhOrBitwise;
+		case OPER_BITWISEEOR: return dhEorBitwise;
+		case OPER_BITWISEAND: return dhAndBitwise;
+	}
+
+	Error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper);
+	return 0;
+}
+
+// ============================================================================
+// Parses an expression, potentially recursively
+//
+DataBuffer* BotscriptParser::ParseExpression (EType reqtype)
+{
+	DataBuffer* retbuf = new DataBuffer (64);
+
+	// Parse first operand
+	retbuf->MergeAndDestroy (ParseExprValue (reqtype));
+
+	// Parse any and all operators we get
+	int oper;
+
+	while ( (oper = ParseOperator (true)) != -1)
+	{
+		// We peeked the operator, move forward now
+		mLexer->Skip();
+
+		// Can't be an assignement operator, those belong in assignments.
+		if (IsAssignmentOperator (oper))
+			Error ("assignment operator inside expression");
+
+		// Parse the right operand.
+		mLexer->MustGetNext();
+		DataBuffer* rb = ParseExprValue (reqtype);
+
+		if (oper == OPER_TERNARY)
+		{
+			// Ternary operator requires - naturally - a third operand.
+			mLexer->MustGetNext (tkColon);
+			mLexer->MustGetNext();
+			DataBuffer* tb = ParseExprValue (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
+			ByteMark* mark1 = retbuf->AddMark (""); // start of "else" case
+			ByteMark* mark2 = retbuf->AddMark (""); // end of expression
+			retbuf->WriteDWord (dhIfNotGoto); // if the first operand (condition)
+			retbuf->AddReference (mark1); // didn't eval true, jump into mark1
+			retbuf->MergeAndDestroy (rb); // otherwise, perform second operand (true case)
+			retbuf->WriteDWord (dhGoto); // afterwards, jump to the end, which is
+			retbuf->AddReference (mark2); // marked by mark2.
+			retbuf->AdjustMark (mark1); // move mark1 at the end of the true case
+			retbuf->MergeAndDestroy (tb); // perform third operand (false case)
+			retbuf->AdjustMark (mark2); // move the ending mark2 here
+		}
+		else
+		{
+			// write to buffer
+			retbuf->MergeAndDestroy (rb);
+			retbuf->WriteDWord (GetDataHeaderByOperator (null, oper));
+		}
+	}
+
+	return retbuf;
+}
+
+// ============================================================================
+// Parses an operator string. Returns the operator number code.
+//
+#define ISNEXT(C) (mLexer->PeekNextString (peek ? 1 : 0) == C)
+
+int BotscriptParser::ParseOperator (bool peek)
+{
+	String oper;
+
+	if (peek)
+		oper += mLexer->PeekNextString();
+	else
+		oper += GetTokenString();
+
+	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)
+	{
+		return o;
+	}
+
+	// Two-char operators
+	oper += mLexer->PeekNextString (peek ? 1 : 0);
+	equalsnext = mLexer->PeekNextString (peek ? 2 : 1) == ("=");
+
+	o =	(oper == "+=") ? OPER_ASSIGNADD :
+		(oper == "-=") ? OPER_ASSIGNSUB :
+		(oper == "*=") ? OPER_ASSIGNMUL :
+		(oper == "/=") ? OPER_ASSIGNDIV :
+		(oper == "%=") ? OPER_ASSIGNMOD :
+		(oper == "==") ? OPER_EQUALS :
+		(oper == "!=") ? OPER_NOTEQUALS :
+		(oper == ">=") ? OPER_GREATERTHANEQUALS :
+		(oper == "<=") ? OPER_LESSTHANEQUALS :
+		(oper == "&&") ? OPER_AND :
+		(oper == "||") ? OPER_OR :
+		(oper == "<<" && !equalsnext) ? OPER_LEFTSHIFT :
+		(oper == ">>" && !equalsnext) ? OPER_RIGHTSHIFT :
+		-1;
+
+	if (o != -1)
+	{
+		mLexer->MustGetNext();
+		return o;
+	}
+
+	// Three-char opers
+	oper += mLexer->PeekNextString (peek ? 2 : 1);
+	o =	oper == "<<=" ? OPER_ASSIGNLEFTSHIFT :
+		oper == ">>=" ? OPER_ASSIGNRIGHTSHIFT :
+		-1;
+
+	if (o != -1)
+	{
+		mLexer->MustGetNext();
+		mLexer->MustGetNext();
+	}
+
+	return o;
+}
+
+// ============================================================================
+//
+String BotscriptParser::ParseFloat()
+{
+	mLexer->TokenMustBe (tkNumber);
+	String floatstring = GetTokenString();
+	Lexer::Token tok;
+
+	// Go after the decimal point
+	if (mLexer->PeekNext (&tok) && tok.type == tkDot)
+	{
+		mLexer->Skip();
+		mLexer->MustGetNext (tkNumber);
+		floatstring += ".";
+		floatstring += GetTokenString();
+	}
+
+	return floatstring;
+}
+
+// ============================================================================
+// 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* BotscriptParser::ParseExprValue (EType reqtype)
+{
+	DataBuffer* b = new DataBuffer (16);
+	ScriptVariable* g;
+
+	// Prefixing "!" means negation.
+	bool negate = TokenIs (tkExclamationMark);
+
+	if (negate) // Jump past the "!"
+		mLexer->Skip();
+
+	if (TokenIs (tkParenStart))
+	{
+		// Expression
+		mLexer->MustGetNext();
+		DataBuffer* c = ParseExpression (reqtype);
+		b->MergeAndDestroy (c);
+		mLexer->MustGetNext (tkParenEnd);
+	}
+	else if (CommandInfo* comm = FindCommandByName (GetTokenString()))
+	{
+		delete b;
+
+		// Command
+		if (reqtype && comm->returnvalue != reqtype)
+			Error ("%1 returns an incompatible data type", comm->name);
+
+		b = ParseCommand (comm);
+	}
+	else if (ConstantInfo* constant = FindConstant (GetTokenString()))
+	{
+		// Type check
+		if (reqtype != constant->type)
+			Error ("constant `%1` is %2, expression requires %3\n",
+				constant->name, GetTypeName (constant->type),
+				GetTypeName (reqtype));
+
+		switch (constant->type)
+		{
+			case EBoolType:
+			case EIntType:
+			{
+				b->WriteDWord (dhPushNumber);
+				b->WriteDWord (constant->val.ToLong());
+				break;
+			}
+
+			case EStringType:
+			{
+				b->WriteStringIndex (constant->val);
+				break;
+			}
+
+			case EVoidType:
+			case EUnknownType:
+				break;
+		}
+	}
+	else if ((g = FindGlobalVariable (GetTokenString())))
+	{
+		// Global variable
+		b->WriteDWord (dhPushGlobalVar);
+		b->WriteDWord (g->index);
+	}
+	else
+	{
+		// If nothing else, check for literal
+		switch (reqtype)
+		{
+			case EVoidType:
+			case EUnknownType:
+			{
+				Error ("unknown identifier `%1` (expected keyword, function or variable)", GetTokenString());
+				break;
+			}
+
+			case EBoolType:
+			case EIntType:
+			{
+				mLexer->TokenMustBe (tkNumber);
+
+				// All values are written unsigned - thus we need to write the value's
+				// absolute value, followed by an unary minus for negatives.
+				b->WriteDWord (dhPushNumber);
+
+				long v = GetTokenString().ToLong();
+				b->WriteDWord (static_cast<word> (abs (v)));
+
+				if (v < 0)
+					b->WriteDWord (dhUnaryMinus);
+
+				break;
+			}
+
+			case EStringType:
+			{
+				// 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.
+				mLexer->TokenMustBe (tkString);
+				b->WriteStringIndex (GetTokenString());
+				break;
+			}
+		}
+	}
+
+	// Negate it now if desired
+	if (negate)
+		b->WriteDWord (dhNegateLogical);
+
+	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* BotscriptParser::ParseAssignment (ScriptVariable* var)
+{
+	// Get an operator
+	mLexer->MustGetNext();
+	int oper = ParseOperator();
+
+	if (IsAssignmentOperator (oper) == false)
+		Error ("expected assignment operator");
+
+	if (mCurrentMode == ETopLevelMode)
+		Error ("can't alter variables at top level");
+
+	// Parse the right operand
+	mLexer->MustGetNext();
+	DataBuffer* retbuf = new DataBuffer;
+	DataBuffer* expr = ParseExpression (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->WriteDWord (var->IsGlobal() ? dhPushGlobalVar : dhPushLocalVar);
+		retbuf->WriteDWord (var->index);
+		retbuf->MergeAndDestroy (expr);
+		retbuf->WriteDWord ((oper == OPER_ASSIGNLEFTSHIFT) ? dhLeftShift : dhRightShift);
+		retbuf->WriteDWord (var->IsGlobal() ? dhAssignGlobalVar : dhAssignLocalVar);
+		retbuf->WriteDWord (var->index);
+	}
+	else
+	{
+		retbuf->MergeAndDestroy (expr);
+		long dh = GetDataHeaderByOperator (var, oper);
+		retbuf->WriteDWord (dh);
+		retbuf->WriteDWord (var->index);
+	}
+
+	return retbuf;
+}
+
+// ============================================================================
+//
+void BotscriptParser::PushScope()
+{
+	mScopeCursor++;
+
+	if (mScopeCursor >= MAX_SCOPE)
+		Error ("too deep scope");
+
+	ScopeInfo* info = &SCOPE (0);
+	info->type = eUnknownScope;
+	info->mark1 = null;
+	info->mark2 = null;
+	info->buffer1 = null;
+	info->casecursor = -1;
+
+	for (int i = 0; i < MAX_CASE; i++)
+	{
+		info->casemarks[i] = null;
+		info->casebuffers[i] = null;
+		info->casenumbers[i] = -1;
+	}
+}
+
+// ============================================================================
+//
+DataBuffer* BotscriptParser::ParseStatement()
+{
+	if (FindConstant (GetTokenString())) // There should not be constants here.
+		Error ("invalid use for constant\n");
+
+	// If it's a variable, expect assignment.
+	if (ScriptVariable* var = FindGlobalVariable (GetTokenString()))
+		return ParseAssignment (var);
+
+	return null;
+}
+
+// ============================================================================
+//
+void BotscriptParser::AddSwitchCase (DataBuffer* b)
+{
+	ScopeInfo* info = &SCOPE (0);
+
+	info->casecursor++;
+
+	if (info->casecursor >= MAX_CASE)
+		Error ("too many cases in one switch");
+
+	// Init a mark for the case buffer
+	ByteMark* casemark = buffer()->AddMark ("");
+	info->casemarks[info->casecursor] = casemark;
+
+	// Add a reference to the mark. "case" and "default" both
+	// add the necessary bytecode before the reference.
+	if (b)
+		b->AddReference (casemark);
+	else
+		buffer()->AddReference (casemark);
+
+	// Init a buffer for the case block and tell the object
+	// writer to record all written data to it.
+	info->casebuffers[info->casecursor] = mSwitchBuffer = new DataBuffer;
+}
+
+// ============================================================================
+//
+ConstantInfo* BotscriptParser::FindConstant (const String& tok)
+{
+	for (int i = 0; i < mConstants.Size(); i++)
+		if (mConstants[i].name == tok)
+			return &mConstants[i];
+
+	return null;
+}
+
+// ============================================================================
+//
+bool BotscriptParser::TokenIs (EToken a)
+{
+	return (mLexer->GetTokenType() == a);
+}
+
+// ============================================================================
+//
+String BotscriptParser::GetTokenString()
+{
+	return mLexer->GetToken()->text;
+}
+
+// ============================================================================
+//
+String BotscriptParser::DescribePosition() const
+{
+	Lexer::Token* tok = mLexer->GetToken();
+	return tok->file + ":" + String (tok->line) + ":" + String (tok->column);
+}
+
+// ============================================================================
+//
+DataBuffer* BotscriptParser::buffer()
+{
+	if (mSwitchBuffer != null)
+		return mSwitchBuffer;
+
+	if (mCurrentMode == EMainLoopMode)
+		return mMainLoopBuffer;
+
+	if (mCurrentMode == EOnenterMode)
+		return mOnEnterBuffer;
+
+	return mMainBuffer;
+}
+
+// ============================================================================
+//
+void BotscriptParser::writeMemberBuffers()
+{
+	// If there was no mainloop defined, write a dummy one now.
+	if (mGotMainLoop == false)
+	{
+		mMainLoopBuffer->WriteDWord (dhMainLoop);
+		mMainLoopBuffer->WriteDWord (dhEndMainLoop);
+	}
+
+	// Write the onenter and mainloop buffers, in that order in particular.
+	for (DataBuffer** bufp : List<DataBuffer**> ({&mOnEnterBuffer, &mMainLoopBuffer}))
+	{
+		buffer()->MergeAndDestroy (*bufp);
+
+		// Clear the buffer afterwards for potential next state
+		*bufp = new DataBuffer;
+	}
+
+	// Next state definitely has no mainloop yet
+	mGotMainLoop = false;
+}
+
+// ============================================================================
+//
+// Write string table
+//
+void BotscriptParser::WriteStringTable()
+{
+	int stringcount = CountStringsInTable();
+
+	if (stringcount == 0)
+		return;
+
+	// Write header
+	mMainBuffer->WriteDWord (dhStringList);
+	mMainBuffer->WriteDWord (stringcount);
+
+	// Write all strings
+	for (int i = 0; i < stringcount; i++)
+		mMainBuffer->WriteString (GetStringTable()[i]);
+}
+
+// ============================================================================
+//
+// Write the compiled bytecode to a file
+//
+void BotscriptParser::WriteToFile (String outfile)
+{
+	FILE* fp = fopen (outfile, "w");
+
+	if (fp == null)
+		Error ("couldn't open %1 for writing: %2", outfile, strerror (errno));
+
+	// First, resolve references
+	for (MarkReference* ref : mMainBuffer->GetReferences())
+	{
+		// Substitute the placeholder with the mark position
+		for (int v = 0; v < 4; v++)
+			mMainBuffer->GetBuffer()[ref->pos + v] = ((ref->target->pos) << (8 * v)) & 0xFF;
+
+		Print ("reference at %1 resolved to mark at %2\n", ref->pos, ref->target->pos);
+	}
+
+	// Then, dump the main buffer to the file
+	fwrite (mMainBuffer->GetBuffer(), 1, mMainBuffer->GetWrittenSize(), fp);
+	Print ("-- %1 byte%s1 written to %2\n", mMainBuffer->GetWrittenSize(), outfile);
+	fclose (fp);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Parser.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,265 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_PARSER_H
+#define BOTC_PARSER_H
+
+#include <stdio.h>
+#include "Main.h"
+#include "Commands.h"
+#include "LexerScanner.h"
+#include "Tokens.h"
+
+// TODO: get rid of this
+#define MAX_SCOPE 32
+
+// TODO: get rid of this too?
+#define MAX_CASE 64
+
+// TODO: get rid of this
+#define MAX_MARKS 512
+
+class DataBuffer;
+class Lexer;
+class ScriptVariable;
+
+// ============================================================================
+//
+struct UndefinedLabel
+{
+	String		name;
+	ByteMark*	target;
+};
+
+// ============================================================================
+// Operators
+//
+enum EOperator
+{
+	OPER_ADD,
+	OPER_SUBTRACT,
+	OPER_MULTIPLY,
+	OPER_DIVIDE,
+	OPER_MODULUS,
+	OPER_ASSIGN,
+	OPER_ASSIGNADD,
+	OPER_ASSIGNSUB,
+	OPER_ASSIGNMUL,
+	OPER_ASSIGNDIV,
+	OPER_ASSIGNMOD, // -- 10
+	OPER_EQUALS,
+	OPER_NOTEQUALS,
+	OPER_LESSTHAN,
+	OPER_GREATERTHAN,
+	OPER_LESSTHANEQUALS,
+	OPER_GREATERTHANEQUALS,
+	OPER_LEFTSHIFT,
+	OPER_RIGHTSHIFT,
+	OPER_ASSIGNLEFTSHIFT,
+	OPER_ASSIGNRIGHTSHIFT, // -- 20
+	OPER_OR,
+	OPER_AND,
+	OPER_BITWISEOR,
+	OPER_BITWISEAND,
+	OPER_BITWISEEOR,
+	OPER_TERNARY,
+	OPER_STRLEN,
+};
+
+// ============================================================================
+//
+struct operatorInfo
+{
+	EOperator		opercode;
+	EDataHeader		dataheader;
+	EToken			token;
+};
+
+// ============================================================================
+// Mark types
+//
+enum eMarkType
+{
+	eLabelMark,
+	eIfMark,
+	eInternalMark, // internal structures
+};
+
+// ============================================================================
+// Scope types
+//
+enum EScopeType
+{
+	eUnknownScope,
+	eIfScope,
+	eWhileScope,
+	eForScope,
+	eDoScope,
+	eSwitchScope,
+	eElseScope,
+};
+
+// ============================================================================
+// Meta-data about scopes
+//
+struct ScopeInfo
+{
+	ByteMark*		mark1;
+	ByteMark*		mark2;
+	EScopeType		type;
+	DataBuffer*		buffer1;
+
+	// switch-related stuff
+	// Which case are we at?
+	int				casecursor;
+
+	// Marks to case-blocks
+	ByteMark*		casemarks[MAX_CASE];
+
+	// Numbers of the case labels
+	int				casenumbers[MAX_CASE];
+
+	// actual case blocks
+	DataBuffer*	casebuffers[MAX_CASE];
+
+	// What is the current buffer of the block?
+	DataBuffer*	recordbuffer;
+};
+
+// ============================================================================
+//
+struct ConstantInfo
+{
+	String name;
+	EType type;
+	String val;
+};
+
+// ============================================================================
+//
+class BotscriptParser
+{
+	PROPERTY (public, bool, ReadOnly, BOOL_OPS, STOCK_WRITE)
+
+	public:
+		// ====================================================================
+		// METHODS
+		BotscriptParser();
+		~BotscriptParser();
+		void			ParseBotscript (String fileName);
+		DataBuffer*		ParseCommand (CommandInfo* comm);
+		DataBuffer*		ParseExpression (EType reqtype);
+		DataBuffer*		ParseAssignment (ScriptVariable* var);
+		int				ParseOperator (bool peek = false);
+		DataBuffer*		ParseExprValue (EType reqtype);
+		String			ParseFloat();
+		void			PushScope();
+		DataBuffer*		ParseStatement();
+		void			AddSwitchCase (DataBuffer* b);
+		void			CheckToplevel();
+		void			CheckNotToplevel();
+		bool			TokenIs (EToken a);
+		String			GetTokenString();
+		String			DescribePosition() const;
+		void			WriteToFile (String outfile);
+
+		inline int GetNumEvents() const
+		{
+			return mNumEvents;
+		}
+
+		inline int GetNumStates() const
+		{
+			return mNumStates;
+		}
+
+	private:
+		// The main buffer - the contents of this is what we
+		// write to file after parsing is complete
+		DataBuffer*				mMainBuffer;
+
+		// onenter buffer - the contents of the onenter{} block
+		// is buffered here and is merged further at the end of state
+		DataBuffer*				mOnEnterBuffer;
+
+		// Mainloop buffer - the contents of the mainloop{} block
+		// is buffered here and is merged further at the end of state
+		DataBuffer*				mMainLoopBuffer;
+
+		// Switch buffer - switch case data is recorded to this
+		// buffer initially, instead of into main buffer.
+		DataBuffer*				mSwitchBuffer;
+
+		Lexer*					mLexer;
+		int						mNumStates;
+		int						mNumEvents;
+		EParserMode				mCurrentMode;
+		String					mCurrentState;
+		bool					mStateSpawnDefined;
+		bool					mGotMainLoop;
+		int						mScopeCursor;
+		DataBuffer*				mIfExpression;
+		bool					mCanElse;
+		List<UndefinedLabel>	mUndefinedLabels;
+		List<ConstantInfo>		mConstants;
+
+		// How many bytes have we written to file?
+		int						mNumWrittenBytes;
+
+		// Scope data
+		// TODO: make a List
+		ScopeInfo				mScopeStack[MAX_SCOPE];
+
+		DataBuffer*	buffer();
+		ConstantInfo*	FindConstant (const String& tok);
+		void			ParseStateBlock();
+		void			ParseEventBlock();
+		void			ParseMainloop();
+		void			ParseOnEnterExit();
+		void			ParseVariableDeclaration();
+		void			ParseGoto();
+		void			ParseIf();
+		void			ParseElse();
+		void			ParseWhileBlock();
+		void			ParseForBlock();
+		void			ParseDoBlock();
+		void			ParseSwitchBlock();
+		void			ParseSwitchCase();
+		void			ParseSwitchDefault();
+		void			ParseBreak();
+		void			ParseContinue();
+		void			ParseBlockEnd();
+		void			ParseConst();
+		void			ParseLabel();
+		void			ParseEventdef();
+		void			ParseFuncdef();
+		void			writeMemberBuffers();
+		void			WriteStringTable();
+};
+
+#endif // BOTC_PARSER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Property.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,213 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_PROPERTY_H
+#define BOTC_PROPERTY_H
+
+// =============================================================================
+//
+// Identifier names
+//
+#define PROPERTY_SET_ACCESSOR(NAME)			Set##NAME
+#define PROPERTY_GET_ACCESSOR(NAME)			Get##NAME
+#define PROPERTY_IS_ACCESSOR(NAME)			Is##NAME // for bool types
+#define PROPERTY_MEMBER_NAME(NAME)			m##NAME
+
+// Names of operations
+#define PROPERTY_APPEND_OPERATION(NAME)		AppendTo##NAME
+#define PROPERTY_PREPEND_OPERATION(NAME)	PrependTo##NAME
+#define PROPERTY_REPLACE_OPERATION(NAME)	ReplaceIn##NAME
+#define PROPERTY_INCREASE_OPERATION(NAME)	Increase##NAME
+#define PROPERTY_DECREASE_OPERATION(NAME)	Decrease##NAME
+#define PROPERTY_TOGGLE_OPERATION(NAME)		Toggle##NAME
+#define PROPERTY_PUSH_OPERATION(NAME)		PushTo##NAME
+#define PROPERTY_REMOVE_OPERATION(NAME)		RemoveFrom##NAME
+#define PROPERTY_CLEAR_OPERATION(NAME)		Clear##NAME
+#define PROPERTY_COUNT_OPERATION(NAME)		Count##NAME
+
+// Operation definitions
+// These are the methods of the list type that are called in the operations.
+#define PROPERTY_APPEND_METHOD_NAME			Append		// String::Append
+#define PROPERTY_PREPEND_METHOD_NAME		Prepend		// String::Prepend
+#define PROPERTY_REPLACE_METHOD_NAME		Replace		// String::Replace
+#define PROPERTY_PUSH_METHOD_NAME			Append		// List<T>::Append
+#define PROPERTY_REMOVE_METHOD_NAME			Remove		// List<T>::Remove
+#define PROPERTY_CLEAR_METHOD_NAME			Clear		// List<T>::Clear
+
+// =============================================================================
+//
+// Main PROPERTY macro
+//
+#define PROPERTY(ACCESS, TYPE, NAME, OPS, WRITETYPE)							\
+	private:																	\
+		TYPE PROPERTY_MEMBER_NAME(NAME);										\
+																				\
+	public:																		\
+		inline TYPE const& PROPERTY_GET_READ_METHOD (NAME, OPS) const			\
+		{																		\
+			return PROPERTY_MEMBER_NAME(NAME); 									\
+		}																		\
+																				\
+	ACCESS:																		\
+		PROPERTY_MAKE_WRITE (TYPE, NAME, WRITETYPE)								\
+		PROPERTY_DEFINE_OPERATIONS (TYPE, NAME, OPS)
+
+// =============================================================================
+//
+// PROPERTY_GET_READ_METHOD
+//
+// This macro uses the OPS argument to construct the name of the actual
+// macro which returns the name of the get accessor. This is so that the
+// bool properties get is<NAME>() accessors while non-bools get get<NAME>()
+//
+#define PROPERTY_GET_READ_METHOD(NAME, OPS)										\
+	PROPERTY_GET_READ_METHOD_##OPS (NAME)
+
+#define PROPERTY_GET_READ_METHOD_BOOL_OPS(NAME) PROPERTY_IS_ACCESSOR (NAME)()
+#define PROPERTY_GET_READ_METHOD_NO_OPS(NAME) PROPERTY_GET_ACCESSOR (NAME)()
+#define PROPERTY_GET_READ_METHOD_STR_OPS(NAME) PROPERTY_GET_ACCESSOR (NAME)()
+#define PROPERTY_GET_READ_METHOD_NUM_OPS(NAME) PROPERTY_GET_ACCESSOR (NAME)()
+#define PROPERTY_GET_READ_METHOD_LIST_OPS(NAME) PROPERTY_GET_ACCESSOR (NAME)()
+
+// =============================================================================
+//
+// PROPERTY_MAKE_WRITE
+//
+// This macro uses the WRITETYPE argument to construct the set accessor of the
+// property. If WRITETYPE is STOCK_WRITE, an inline method is defined to just
+// set the new value of the property. If WRITETYPE is CUSTOM_WRITE, the accessor
+// is merely declared and is left for the user to define.
+//
+#define PROPERTY_MAKE_WRITE(TYPE, NAME, WRITETYPE)								\
+	PROPERTY_MAKE_WRITE_##WRITETYPE (TYPE, NAME)
+
+#define PROPERTY_MAKE_WRITE_STOCK_WRITE(TYPE, NAME)								\
+		inline void PROPERTY_SET_ACCESSOR(NAME) (TYPE const& a)					\
+		{																		\
+			PROPERTY_MEMBER_NAME(NAME) = a;										\
+		}
+
+#define PROPERTY_MAKE_WRITE_CUSTOM_WRITE(TYPE, NAME)							\
+		void PROPERTY_SET_ACCESSOR(NAME) (TYPE const& NAME);					\
+
+// =============================================================================
+//
+// PROPERTY_DEFINE_OPERATIONS
+//
+// This macro may expand into methods defining additional operations for the
+// method. 
+
+#define PROPERTY_DEFINE_OPERATIONS(TYPE, NAME, OPS)								\
+	DEFINE_PROPERTY_##OPS (TYPE, NAME)
+
+// =============================================================================
+//
+// DEFINE_PROPERTY_NO_OPS
+//
+// Obviously NO_OPS expands into no operations.
+//
+#define DEFINE_PROPERTY_NO_OPS(TYPE, NAME)
+
+// =============================================================================
+//
+// DEFINE_PROPERTY_STR_OPS
+//
+#define DEFINE_PROPERTY_STR_OPS(TYPE, NAME)										\
+		void PROPERTY_APPEND_OPERATION(NAME) (const TYPE& a)					\
+		{																		\
+			TYPE tmp (PROPERTY_MEMBER_NAME(NAME));								\
+			tmp.PROPERTY_APPEND_METHOD_NAME (a);								\
+			PROPERTY_SET_ACCESSOR(NAME) (tmp);									\
+		}																		\
+																				\
+		void PROPERTY_PREPEND_OPERATION(NAME) (const TYPE& a)					\
+		{																		\
+			TYPE tmp (PROPERTY_MEMBER_NAME(NAME));								\
+			tmp.PROPERTY_PREPEND_METHOD_NAME (a);								\
+			PROPERTY_SET_ACCESSOR(NAME) (tmp);									\
+		}																		\
+																				\
+		void PROPERTY_REPLACE_OPERATION(NAME) (const TYPE& a, const TYPE& b)	\
+		{																		\
+			TYPE tmp (PROPERTY_MEMBER_NAME(NAME));								\
+			tmp.PROPERTY_REPLACE_METHOD_NAME (a, b);							\
+			PROPERTY_SET_ACCESSOR(NAME) (tmp);									\
+		}
+
+// =============================================================================
+//
+// DEFINE_PROPERTY_NUM_OPS
+//
+#define DEFINE_PROPERTY_NUM_OPS(TYPE, NAME)										\
+		inline void PROPERTY_INCREASE_OPERATION(NAME) (TYPE a = 1)				\
+		{																		\
+			PROPERTY_SET_ACCESSOR(NAME) (PROPERTY_MEMBER_NAME(NAME) + a);		\
+		}																		\
+																				\
+		inline void PROPERTY_DECREASE_OPERATION(NAME) (TYPE a = 1)				\
+		{																		\
+			PROPERTY_SET_ACCESSOR(NAME) (PROPERTY_MEMBER_NAME(NAME) - a);		\
+		}
+
+// =============================================================================
+//
+// DEFINE_PROPERTY_BOOL_OPS
+//
+#define DEFINE_PROPERTY_BOOL_OPS(TYPE, NAME)									\
+		inline void PROPERTY_TOGGLE_OPERATION(NAME)()							\
+		{																		\
+			PROPERTY_SET_ACCESSOR(NAME) (!PROPERTY_MEMBER_NAME(NAME));			\
+		}
+
+// =============================================================================
+//
+// DEFINE_PROPERTY_LIST_OPS
+//
+#define DEFINE_PROPERTY_LIST_OPS(TYPE, NAME)									\
+		void PROPERTY_PUSH_OPERATION(NAME) (const TYPE::ValueType& a)			\
+		{																		\
+			PROPERTY_MEMBER_NAME(NAME).PROPERTY_PUSH_METHOD_NAME (a);			\
+		}																		\
+																				\
+		void PROPERTY_REMOVE_OPERATION(NAME) (const TYPE::ValueType& a)			\
+		{																		\
+			PROPERTY_MEMBER_NAME(NAME).PROPERTY_REMOVE_METHOD_NAME (a);			\
+		}																		\
+																				\
+		inline void PROPERTY_CLEAR_OPERATION(NAME)()							\
+		{																		\
+			PROPERTY_MEMBER_NAME(NAME).PROPERTY_CLEAR_METHOD_NAME();			\
+		}																		\
+																				\
+	public:																		\
+		inline int PROPERTY_COUNT_OPERATION(NAME)() const						\
+		{																		\
+			return PROPERTY_GET_ACCESSOR (NAME)().Size();						\
+		}
+
+#endif // BOTC_PROPERTY_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/String.cc	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,450 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <cstring>
+#include "Main.h"
+#include "String.h"
+
+// =============================================================================
+//
+int String::Compare (const String& other) const
+{
+	return mString.compare (other.STDString());
+}
+
+// =============================================================================
+//
+void String::Trim (int n)
+{
+	if (n > 0)
+		mString = Mid (0, Length() - n).STDString();
+	else
+		mString = Mid (n, -1).STDString();
+}
+
+// =============================================================================
+//
+String String::Strip (const List< char >& unwanted)
+{
+	String copy (mString);
+
+	for (char c : unwanted)
+		for (int i = 0; i < copy.Length(); ++i)
+			if (copy[i] == c)
+				copy.RemoveAt (i);
+
+	/*
+	while(( pos = copy.first( c )) != -1 )
+		copy.erase( pos );
+	*/
+
+	return copy;
+}
+
+// =============================================================================
+//
+String String::ToUppercase() const
+{
+	String newstr = mString;
+
+	for (char & c : newstr)
+		if (c >= 'a' && c <= 'z')
+			c -= 'a' - 'A';
+
+	return newstr;
+}
+
+// =============================================================================
+//
+String String::ToLowercase() const
+{
+	String newstr = mString;
+
+	for (char & c : newstr)
+		if (c >= 'A' && c <= 'Z')
+			c += 'a' - 'A';
+
+	return newstr;
+}
+
+// =============================================================================
+//
+StringList String::Split (char del) const
+{
+	String delimstr;
+	delimstr += del;
+	return Split (delimstr);
+}
+
+// =============================================================================
+//
+StringList String::Split (String del) const
+{
+	StringList res;
+	long a = 0;
+
+	// Find all separators and store the text left to them.
+	for (;;)
+	{
+		long b = FirstIndexOf (del, a);
+
+		if (b == -1)
+			break;
+
+		String sub = Mid (a, b);
+
+		if (sub.Length() > 0)
+			res.Append (Mid (a, b));
+
+		a = b + del.Length();
+	}
+
+	// Add the string at the right of the last separator
+	if (a < (int) Length())
+		res.Append (Mid (a, Length()));
+
+	return res;
+}
+
+// =============================================================================
+//
+void String::Replace (const char* a, const char* b)
+{
+	long pos;
+
+	while ( (pos = FirstIndexOf (a)) != -1)
+		mString = mString.replace (pos, strlen (a), b);
+}
+
+// =============================================================================
+//
+int String::Count (char needle) const
+{
+	int needles = 0;
+
+	for (const char & c : mString)
+		if (c == needle)
+			needles++;
+
+	return needles;
+}
+
+// =============================================================================
+//
+String String::Mid (long a, long b) const
+{
+	if (b == -1)
+		b = Length();
+
+	if (b == a)
+		return "";
+
+	if (b < a)
+	{
+		// Swap the variables
+		int c = a;
+		a = b;
+		b = c;
+	}
+
+	char* newstr = new char[b - a + 1];
+	strncpy (newstr, mString.c_str() + a, b - a);
+	newstr[b - a] = '\0';
+
+	String other (newstr);
+	delete[] newstr;
+	return other;
+}
+
+// =============================================================================
+//
+int String::WordPosition (int n) const
+{
+	int count = 0;
+
+	for (int i = 0; i < Length(); ++i)
+	{
+		if (mString[i] != ' ')
+			continue;
+
+		if (++count < n)
+			continue;
+
+		return i;
+	}
+
+	return -1;
+}
+
+// =============================================================================
+//
+int String::FirstIndexOf (const char* c, int a) const
+{
+	for (; a < Length(); a++)
+		if (mString[a] == c[0] && strncmp (mString.c_str() + a, c, strlen (c)) == 0)
+			return a;
+
+	return -1;
+}
+
+// =============================================================================
+//
+int String::LastIndexOf (const char* c, int a) const
+{
+	if (a == -1 || a >= Length())
+		a = Length() - 1;
+
+	for (; a > 0; a--)
+		if (mString[a] == c[0] && strncmp (mString.c_str() + a, c, strlen (c)) == 0)
+			return a;
+
+	return -1;
+}
+
+// =============================================================================
+//
+void String::Dump() const
+{
+	Print ("`%1`:\n", CString());
+	int i = 0;
+
+	for (char u : mString)
+		Print ("\t%1. [%d2] `%3`\n", i++, u, String (u));
+}
+
+// =============================================================================
+//
+long String::ToLong (bool* ok, int base) const
+{
+	errno = 0;
+	char* endptr;
+	long i = strtol (mString.c_str(), &endptr, base);
+
+	if (ok)
+		*ok = (errno == 0 && *endptr == '\0');
+
+	return i;
+}
+
+// =============================================================================
+//
+float String::ToFloat (bool* ok) const
+{
+	errno = 0;
+	char* endptr;
+	float i = strtof (mString.c_str(), &endptr);
+
+	if (ok)
+		*ok = (errno == 0 && *endptr == '\0');
+
+	return i;
+}
+
+// =============================================================================
+//
+double String::ToDouble (bool* ok) const
+{
+	errno = 0;
+	char* endptr;
+	double i = strtod (mString.c_str(), &endptr);
+
+	if (ok)
+		*ok = (errno == 0 && *endptr == '\0');
+
+	return i;
+}
+
+// =============================================================================
+//
+String String::operator+ (const String& data) const
+{
+	String newString = *this;
+	newString.Append (data);
+	return newString;
+}
+
+// =============================================================================
+//
+String String::operator+ (const char* data) const
+{
+	String newstr = *this;
+	newstr.Append (data);
+	return newstr;
+}
+
+// =============================================================================
+//
+bool String::IsNumeric() const
+{
+	bool gotDot = false;
+
+	for (const char & c : mString)
+	{
+		// Allow leading hyphen for negatives
+		if (&c == &mString[0] && c == '-')
+			continue;
+
+		// Check for decimal point
+		if (!gotDot && c == '.')
+		{
+			gotDot = true;
+			continue;
+		}
+
+		if (c >= '0' && c <= '9')
+			continue; // Digit
+
+		// If the above cases didn't catch this character, it was
+		// illegal and this is therefore not a number.
+		return false;
+	}
+
+	return true;
+}
+
+// =============================================================================
+//
+bool String::EndsWith (const String& other)
+{
+	if (Length() < other.Length())
+		return false;
+
+	const int ofs = Length() - other.Length();
+	return strncmp (CString() + ofs, other.CString(), other.Length()) == 0;
+}
+
+// =============================================================================
+//
+bool String::StartsWith (const String& other)
+{
+	if (Length() < other.Length())
+		return false;
+
+	return strncmp (CString(), other.CString(), other.Length()) == 0;
+}
+
+// =============================================================================
+//
+void String::SPrintf (const char* fmtstr, ...)
+{
+	char* buf;
+	int bufsize = 256;
+	va_list va;
+	va_start (va, fmtstr);
+
+	do
+		buf = new char[bufsize];
+	while (vsnprintf (buf, bufsize, fmtstr, va) >= bufsize);
+
+	va_end (va);
+	mString = buf;
+	delete[] buf;
+}
+
+// =============================================================================
+//
+String StringList::Join (const String& delim)
+{
+	String result;
+
+	for (const String& it : GetDeque())
+	{
+		if (result.IsEmpty() == false)
+			result += delim;
+
+		result += it;
+	}
+
+	return result;
+}
+
+// =============================================================================
+//
+bool String::MaskAgainst (const String& pattern) const
+{
+	// Elevate to uppercase for case-insensitive matching
+	String pattern_upper = pattern.ToUppercase();
+	String this_upper = ToUppercase();
+	const char* maskstring = pattern_upper.CString();
+	const char* mptr = &maskstring[0];
+
+	for (const char* sptr = this_upper.CString(); *sptr != '\0'; sptr++)
+	{
+		if (*mptr == '?')
+		{
+			if (*(sptr + 1) == '\0')
+			{
+				// ? demands that there's a character here and there wasn't.
+				// Therefore, mask matching fails
+				return false;
+			}
+		}
+		elif (*mptr == '*')
+		{
+			char end = *(++mptr);
+
+			// If '*' is the final character of the message, all of the remaining
+			// string matches against the '*'. We don't need to bother checking
+			// the string any further.
+			if (end == '\0')
+				return true;
+
+			// Skip to the end character
+			while (*sptr != end && *sptr != '\0')
+				sptr++;
+
+			// String ended while the mask still had stuff
+			if (*sptr == '\0')
+				return false;
+		}
+		elif (*sptr != *mptr)
+			return false;
+
+		mptr++;
+	}
+
+	return true;
+}
+
+// =============================================================================
+//
+String String::FromNumber (int a)
+{
+	char buf[32];
+	sprintf (buf, "%d", a);
+	return String (buf);
+}
+
+// =============================================================================
+//
+String String::FromNumber (long a)
+{
+	char buf[32];
+	sprintf (buf, "%ld", a);
+	return String (buf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/String.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,372 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_STRING_H
+#define BOTC_STRING_H
+
+#include <deque>
+#include <string>
+#include <stdarg.h>
+#include "Types.h"
+#include "Containers.h"
+
+class String;
+class StringList;
+
+// =============================================================================
+//
+class String
+{
+	public:
+		using StringType	= std::string;
+		using Iterator		= typename StringType::iterator;
+		using ConstIterator	= StringType::const_iterator;
+
+		String() {}
+
+		explicit String (char a) :
+			mString (&a) {}
+
+		String (const char* data) :
+			mString (data) {}
+
+		String (const StringType& data) :
+			mString (data) {}
+
+		void				Dump() const;
+		int					Compare (const String& other) const;
+		bool				EndsWith (const String& other);
+		int					Count (char needle) const;
+		int					FirstIndexOf (const char* c, int a = 0) const;
+		int					LastIndexOf (const char* c, int a = -1) const;
+		String				ToLowercase() const;
+		bool				IsNumeric() const;
+		bool				MaskAgainst (const String& pattern) const;
+		int					WordPosition (int n) const;
+		void				Replace (const char* a, const char* b);
+		StringList			Split (String del) const;
+		StringList			Split (char del) const;
+		void				SPrintf (const char* fmtstr, ...);
+		bool				StartsWith (const String& other);
+		String				Strip (const List<char>& unwanted);
+		String				Mid (long a, long b = -1) const;
+		double				ToDouble (bool* ok = nullptr) const;
+		float				ToFloat (bool* ok = nullptr) const;
+		long				ToLong (bool* ok = nullptr, int base = 10) const;
+		void				Trim (int n);
+		String				ToUppercase() const;
+
+		String				operator+ (const String& data) const;
+		String				operator+ (const char* data) const;
+
+		static String		FromNumber (int a);
+		static String		FromNumber (long a);
+
+		inline bool IsEmpty() const
+		{
+			return mString[0] == '\0';
+		}
+
+		inline void Append (const char* data)
+		{
+			mString.append (data);
+		}
+
+		inline void Append (const char data)
+		{
+			mString.push_back (data);
+		}
+
+		inline void Append (const String& data)
+		{
+			mString.append (data.CString());
+		}
+
+		inline Iterator begin()
+		{
+			return mString.begin();
+		}
+
+		inline ConstIterator begin() const
+		{
+			return mString.cbegin();
+		}
+
+		inline const char* CString() const
+		{
+			return mString.c_str();
+		}
+
+		inline const char* c_str() const
+		{
+			return mString.c_str();
+		}
+
+		inline Iterator end()
+		{
+			return mString.end();
+		}
+
+		inline ConstIterator end() const
+		{
+			return mString.end();
+		}
+
+		inline void Clear()
+		{
+			mString.clear();
+		}
+
+		inline void RemoveAt (int pos)
+		{
+			mString.erase (mString.begin() + pos);
+		}
+
+		inline void Insert (int pos, char c)
+		{
+			mString.insert (mString.begin() + pos, c);
+		}
+
+		inline int Length() const
+		{
+			return mString.length();
+		}
+
+		inline void Remove (int pos, int len)
+		{
+			mString.replace (pos, len, "");
+		}
+
+		inline void RemoveFromStart (int len)
+		{
+			Remove (0, len);
+		}
+
+		inline void RemoveFromEnd (int len)
+		{
+			Remove (Length() - len, len);
+		}
+
+		inline void Replace (int pos, int n, const String& a)
+		{
+			mString.replace (pos, n, a.CString());
+		}
+
+		inline void ShrinkToFit()
+		{
+			mString.shrink_to_fit();
+		}
+
+		inline const StringType& STDString() const
+		{
+			return mString;
+		}
+
+		inline String Strip (char unwanted)
+		{
+			return Strip ({unwanted});
+		}
+
+		// =============================================================================
+		//
+		inline String operator+ (int num) const
+		{
+			return *this + String::FromNumber (num);
+		}
+
+		// =============================================================================
+		//
+		inline String& operator+= (const String data)
+		{
+			Append (data);
+			return *this;
+		}
+
+		// =============================================================================
+		//
+		inline String& operator+= (const char* data)
+		{
+			Append (data);
+			return *this;
+		}
+
+		// =============================================================================
+		//
+		inline String& operator+= (int num)
+		{
+			return operator+= (String::FromNumber (num));
+		}
+
+		// =============================================================================
+		//
+		inline void Prepend (String a)
+		{
+			mString = (a + mString).STDString();
+		}
+
+		// =============================================================================
+		//
+		inline String& operator+= (const char data)
+		{
+			Append (data);
+			return *this;
+		}
+
+		// =============================================================================
+		//
+		inline String operator- (int n) const
+		{
+			String newString = mString;
+			newString -= n;
+			return newString;
+		}
+
+		// =============================================================================
+		//
+		inline String& operator-= (int n)
+		{
+			Trim (n);
+			return *this;
+		}
+
+		// =============================================================================
+		//
+		inline String operator+() const
+		{
+			return ToUppercase();
+		}
+
+		// =============================================================================
+		//
+		inline String operator-() const
+		{
+			return ToLowercase();
+		}
+
+		// =============================================================================
+		//
+		inline bool operator== (const String& other) const
+		{
+			return STDString() == other.STDString();
+		}
+
+		// =============================================================================
+		//
+		inline bool operator== (const char* other) const
+		{
+			return operator== (String (other));
+		}
+
+		// =============================================================================
+		//
+		inline bool operator!= (const String& other) const
+		{
+			return STDString() != other.STDString();
+		}
+
+		// =============================================================================
+		//
+		inline bool operator!= (const char* other) const
+		{
+			return operator!= (String (other));
+		}
+
+		// =============================================================================
+		//
+		inline bool operator> (const String& other) const
+		{
+			return STDString() > other.STDString();
+		}
+
+		// =============================================================================
+		//
+		inline bool operator< (const String& other) const
+		{
+			return STDString() < other.STDString();
+		}
+
+		// =============================================================================
+		//
+		inline operator const char*() const
+		{
+			return CString();
+		}
+
+		// =============================================================================
+		//
+		inline operator const StringType&() const
+		{
+			return STDString();
+		}
+
+		// =============================================================================
+		//
+		// Difference between indices @a and @b. @b can be -1, in which
+		// case it will be length() - 1.
+		//
+		inline int IndexDifference (int a, int b)
+		{
+			assert (b == -1 || b >= a);
+			return (b != -1 ? b - a : Length() - 1 - a);
+		}
+
+	private:
+		StringType mString;
+};
+
+// =============================================================================
+//
+class StringList : public List<String>
+{
+	public:
+		StringList() {}
+		StringList (std::initializer_list<String> vals) :
+			List<String> (vals) {}
+		StringList (const List<String>& a) : List<String> (a.GetDeque()) {}
+		StringList (const ListType& a) : List<String> (a) {}
+
+		String Join (const String& delim);
+};
+
+
+// =============================================================================
+//
+inline bool operator== (const char* a, const String& b)
+{
+	return b == a;
+}
+
+
+// =============================================================================
+//
+inline String operator+ (const char* a, const String& b)
+{
+	return String (a) + b;
+}
+
+#endif // BOTC_STRING_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/StringTable.cc	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,80 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "StringTable.h"
+
+static StringList gStringTable;
+
+// ============================================================================
+//
+const StringList& GetStringTable()
+{
+	return gStringTable;
+}
+
+// ============================================================================
+//
+// Potentially adds a string to the table and returns the index of it.
+//
+int GetStringTableIndex (const String& a)
+{
+	// Find a free slot in the table.
+	int idx;
+
+	for (idx = 0; idx < gStringTable.Size(); idx++)
+	{
+		// String is already in the table, thus return it.
+		if (gStringTable[idx] == a)
+			return idx;
+	}
+
+	// Must not be too long.
+	if (a.Length() >= gMaxStringLength)
+		Error ("string `%1` too long (%2 characters, max is %3)\n",
+			   a, a.Length(), gMaxStringLength);
+
+	// Check if the table is already full
+	if (gStringTable.Size() == gMaxStringlistSize - 1)
+		Error ("too many strings!\n");
+
+	// Now, dump the string into the slot
+	gStringTable.Append (a);
+	return (gStringTable.Size() - 1);
+}
+
+// ============================================================================
+//
+// Counts the amount of strings in the table.
+//
+int CountStringsInTable()
+{
+	return gStringTable.Size();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/StringTable.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,38 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_STRINGTABLE_H
+#define BOTC_STRINGTABLE_H
+
+#include "Main.h"
+
+int GetStringTableIndex (const String& a);
+const StringList& GetStringTable();
+int CountStringsInTable();
+
+#endif // BOTC_STRINGTABLE_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Tokens.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,112 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef TOKENS_H
+#define TOKENS_H
+
+#include <climits>
+
+// =======================================================
+enum EToken
+{
+	// Non-word tokens
+	tkEquals,				// ----- 0
+	tkBrackets,				// - 1
+	tkAddAssign,			// - 2
+	tkSubAssign,			// - 3
+	tkMultiplyAssign,		// - 4
+	tkDivideAssign,			// ----- 5
+	tkModulusAssign,		// - 6
+	tkSingleQuote,			// - 7
+	tkDollarSign,			// - 8
+	tkParenStart,			// - 9
+	tkParenEnd,				// ----- 10
+	tkBracketStart,			// - 11
+	tkBracketEnd,			// - 12
+	tkBraceStart,			// - 13
+	tkBraceEnd,				// - 14
+	tkAssign,				// ----- 15
+	tkPlus,					// - 16
+	tkMinus,				// - 17
+	tkMultiply,				// - 18
+	tkDivide,				// - 19
+	tkModulus,				// ----- 20
+	tkComma,				// - 21
+	tkLesser,				// - 22
+	tkGreater,				// - 23
+	tkDot,					// - 24
+	tkColon,				// ----- 25
+	tkSemicolon,			// - 26
+	tkHash,					// - 27
+	tkExclamationMark,		// - 28
+	tkArrow,				// - 29
+
+	// --------------
+	// Named tokens
+	tkBool,					// ----- 30
+	tkBreak,				// - 31
+	tkCase,					// - 32
+	tkContinue,				// - 33
+	tkConst,				// - 34
+	tkDefault,				// ----- 35
+	tkDo,					// - 36
+	tkElse,					// - 37
+	tkEvent,				// - 38
+	tkEventdef,				// - 39
+	tkFor,					// ----- 40
+	tkFuncdef,				// - 41
+	tkGoto,					// - 42
+	tkIf,					// - 43
+	tkInt,					// - 44
+	tkMainloop,				// ----- 45
+	tkOnenter,				// - 46
+	tkOnexit,				// - 47
+	tkState,				// - 48
+	tkSwitch,				// - 49
+	tkStr,					// ----- 50
+	tkVoid,					// - 51
+	tkWhile,				// - 52
+
+	// 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
+	tkEnum,					// - 53
+	tkFunc,					// - 54
+	tkReturn,				// ----- 55
+
+	// --------------
+	// Generic tokens
+	tkSymbol,				// - 56
+	tkNumber,				// - 57
+	tkString,				// - 58
+
+	tkFirstNamedToken		= tkBool,
+	tkLastNamedToken		= (int) tkSymbol - 1,
+	tkAny					= INT_MAX
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Types.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,115 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_TYPES_H
+#define BOTC_TYPES_H
+
+#include <cstdlib>
+#include <stdexcept>
+#include "String.h"
+
+static const std::nullptr_t null = nullptr;
+
+// =============================================================================
+//
+// Byte datatype
+//
+typedef int32_t word;
+typedef unsigned char byte;
+
+// =============================================================================
+//
+// Parser mode: where is the parser at?
+//
+enum EParserMode
+{
+	ETopLevelMode,		// at top level
+	EEventMode,			// inside event definition
+	EMainLoopMode,		// inside mainloop
+	EOnenterMode,		// inside onenter
+	EOnexitMode,		// inside onexit
+};
+
+// =============================================================================
+//
+enum EType
+{
+	EUnknownType = 0,
+	EVoidType,
+	EIntType,
+	EStringType,
+	EBoolType,
+};
+
+// =============================================================================
+//
+struct ByteMark
+{
+	String		name;
+	int			pos;
+};
+
+// =============================================================================
+//
+struct MarkReference
+{
+	ByteMark*	target;
+	int			pos;
+};
+
+// =============================================================================
+//
+class ScriptError : public std::exception
+{
+	public:
+		ScriptError (const String& msg) :
+			mMsg (msg) {}
+
+		inline const char* what() const throw()
+		{
+			return mMsg;
+		}
+
+	private:
+		String mMsg;
+};
+
+// =============================================================================
+//
+// Get absolute value of @a
+//
+template<class T> inline T abs (T a)
+{
+	return (a >= 0) ? a : -a;
+}
+
+#ifdef IN_IDE_PARSER
+using FILE = void;
+#endif
+
+#endif // BOTC_TYPES_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Variables.cc	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,79 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "StringTable.h"
+#include "Variables.h"
+#include "Parser.h"
+
+List<ScriptVariable> g_GlobalVariables;
+List<ScriptVariable> g_LocalVariables;
+
+// ============================================================================
+// Tries to declare a new global-scope variable. Returns pointer
+// to new global variable, null if declaration failed.
+ScriptVariable* DeclareGlobalVariable (EType type, String name)
+{
+	// Unfortunately the VM does not support string variables so yeah.
+	if (type == EStringType)
+		Error ("variables cannot be string\n");
+
+	// Check that the variable is valid
+	if (FindCommandByName (name))
+		Error ("name of variable-to-be `%s` conflicts with that of a command", name.CString());
+
+	if (g_GlobalVariables.Size() >= gMaxGlobalVars)
+		Error ("too many global variables!");
+
+	for (int i = 0; i < g_GlobalVariables.Size(); i++)
+		if (g_GlobalVariables[i].name == name)
+			Error ("attempted redeclaration of global variable `%s`", name.CString());
+
+	ScriptVariable g;
+	g.index = g_GlobalVariables.Size();
+	g.name = name;
+	g.statename = "";
+	g.value = 0;
+	g.type = type;
+
+	g_GlobalVariables << g;
+	return &g_GlobalVariables[g.index];
+}
+
+// ============================================================================
+// Find a global variable by name
+ScriptVariable* FindGlobalVariable (String name)
+{
+	for (ScriptVariable& var : g_GlobalVariables)
+		if (var.name == name)
+			return &var;
+
+	return null;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Variables.h	Sun Feb 02 17:06:39 2014 +0200
@@ -0,0 +1,56 @@
+/*
+	Copyright 2012-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:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. 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.
+	3. The name of the author may not be used to endorse or promote products
+	   derived from this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+*/
+
+#ifndef BOTC_VARIABLES_H
+#define BOTC_VARIABLES_H
+
+#include "Main.h"
+
+class BotscriptParser;
+
+struct ScriptVariable
+{
+	String	name;
+	String	statename;
+	EType	type;
+	int		value;
+	int		index;
+
+	inline bool IsGlobal() const
+	{
+		return statename.IsEmpty();
+	}
+};
+
+extern List<ScriptVariable> g_GlobalVariables;
+extern List<ScriptVariable> g_LocalVariables;
+
+ScriptVariable* DeclareGlobalVariable (EType type, String name);
+ScriptVariable* FindGlobalVariable (String name);
+
+#endif // BOTC_VARIABLES_H
--- a/src/botstuff.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,196 +0,0 @@
-/*
-	Copyright 2000-2010 Brad Carney
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-// Numeric values and stuff from zandronum bots.h
-
-#ifndef BOTC_BOTSTUFF_H
-#define BOTC_BOTSTUFF_H
-
-static const int g_max_states			= 256;
-static const int g_max_events			= 32;
-static const int g_max_global_events	= 32;
-static const int g_max_global_vars		= 128;
-static const int g_max_global_arrays	= 16;
-static const int g_max_array_size		= 65536;
-static const int g_max_state_vars		= 16;
-static const int g_max_stringlist_size	= 128;
-static const int g_max_string_length	= 256;
-static const int g_max_reaction_time	= 52;
-static const int g_max_stored_events	= 64;
-
-enum e_data_header
-{
-	dh_command,
-	dh_state_index,
-	dh_state_name,
-	dh_on_enter,
-	dh_main_loop,
-	dh_on_exit,
-	dh_event,
-	dh_end_on_enter,
-	dh_end_main_loop,
-	dh_end_on_exit,
-	dh_end_event,
-	dh_if_goto,
-	dh_if_not_goto,
-	dh_goto,
-	dh_or_logical,
-	dh_and_logical,
-	dh_or_bitwise,
-	dh_eor_bitwise,
-	dh_and_bitwise,
-	dh_equals,
-	dh_not_equals,
-	dh_less_than,
-	dh_at_most,
-	dh_greater_than,
-	dh_at_least,
-	dh_negate_logical,
-	dh_left_shift,
-	dh_right_shift,
-	dh_add,
-	dh_subtract,
-	dh_unary_minus,
-	dh_multiply,
-	dh_divide,
-	dh_modulus,
-	dh_push_number,
-	dh_push_string_index,
-	dh_push_global_var,
-	dh_push_local_var,
-	dh_drop_stack_position,
-	dh_script_var_list,
-	dh_string_list,
-	dh_increase_global_var,
-	dh_decrease_global_var,
-	dh_assign_global_var,
-	dh_add_global_var,
-	dh_subtract_global_var,
-	dh_multiply_global_var,
-	dh_divide_global_var,
-	dh_mod_global_var,
-	dh_increase_local_var,
-	dh_decrease_local_var,
-	dh_assign_local_var,
-	dh_add_local_var,
-	dh_subtract_local_var,
-	dh_multiply_local_var,
-	dh_divide_local_var,
-	dh_mod_local_var,
-	dh_case_goto,
-	dh_drop,
-	dh_increase_global_array,
-	dh_decrease_global_array,
-	dh_assign_global_array,
-	dh_add_global_array,
-	dh_subtract_global_array,
-	dh_multiply_global_array,
-	dh_divide_global_array,
-	dh_mod_global_array,
-	dh_push_global_array,
-	dh_swap,
-	dh_dup,
-	dh_array_set,
-	num_data_headers
-};
-
-//*****************************************************************************
-//	These are the different bot events that can be posted to a bot's state.
-enum e_event
-{
-	ev_killed_by_enemy,
-	ev_killed_by_player,
-	ev_killed_by_self,
-	ev_killed_by_environment,
-	ev_reached_goal,
-	ev_goal_removed,
-	ev_damaged_by_player,
-	ev_player_say,
-	ev_enemy_killed,
-	ev_respawned,
-	ev_intermission,
-	ev_new_maps,
-	ev_enemy_used_fist,
-	ev_enemy_used_chainsaw,
-	ev_enemy_fired_pistol,
-	ev_enemy_fired_shotgun,
-	ev_enemy_fired_ssg,
-	ev_enemy_fired_chaingun,
-	ev_enemy_fired_minigun,
-	ev_enemy_fired_rocket,
-	ev_enemy_fired_grenade,
-	ev_enemy_fired_railgun,
-	ev_enemy_fired_plasma,
-	ev_enemy_fired_bfg,
-	ev_enemy_fired_bfg10k,
-	ev_player_used_fist,
-	ev_player_used_chainsaw,
-	ev_player_fired_pistol,
-	ev_player_fired_shotgun,
-	ev_player_fired_ssg,
-	ev_player_fired_chaingun,
-	ev_player_fired_minigun,
-	ev_player_fired_rocket,
-	ev_player_fired_grenade,
-	ev_player_fired_railgun,
-	ev_player_fired_plasma,
-	ev_player_fired_bfg,
-	ev_player_fired_bfg10k,
-	ev_used_fist,
-	ev_used_chainsaw,
-	ev_fired_pistol,
-	ev_fired_shotgun,
-	ev_fired_ssg,
-	ev_fired_chaingun,
-	ev_fired_minigun,
-	ev_fired_rocket,
-	ev_fired_grenade,
-	ev_fired_railgun,
-	ev_fired_plasma,
-	ev_fired_bfg,
-	ev_fired_bfg10k,
-	ev_player_joined_game,
-	ev_joined_game,
-	ev_duel_starting_countdown,
-	ev_duel_fight,
-	ev_duel_win_sequence,
-	ev_spectating,
-	ev_lms_starting_countdown,
-	ev_lms_fight,
-	ev_lms_win_sequence,
-	ev_weapon_change,
-	ev_enemy_bfg_explode,
-	ev_player_bfg_explode,
-	ev_bfg_explode,
-	ev_recieved_medal,
-
-	num_bot_events
-};
-
-#endif	// BOTC_BOTSTUFF_H
--- a/src/commands.cc	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "main.h"
-#include "str.h"
-#include "commands.h"
-#include "lexer.h"
-
-static list<command_info*> g_commands;
-
-// ============================================================================
-//
-void add_command_definition (command_info* comm)
-{
-	// Ensure that there is no conflicts
-	for (command_info* it : g_commands)
-		if (it->number == comm->number)
-			error ("Attempted to redefine command #%1 (%2) as %3",
-				g_commands[comm->number]->name, comm->name);
-
-	g_commands << comm;
-}
-
-// ============================================================================
-// Finds a command by name
-command_info* find_command_by_name (string fname)
-{
-	for (command_info* comm : g_commands)
-	{
-		if (fname.to_uppercase() == comm->name.to_uppercase())
-			return comm;
-	}
-
-	return null;
-}
-
-// ============================================================================
-// Returns the prototype of the command
-string get_command_signature (command_info* comm)
-{
-	string text;
-	text += get_type_name (comm->returnvalue);
-	text += ' ';
-	text += comm->name;
-
-	if (comm->maxargs != 0)
-		text += ' ';
-
-	text += '(';
-
-	bool hasoptionals = false;
-
-	for (int i = 0; i < comm->maxargs; i++)
-	{
-		if (i == comm->numargs)
-		{
-			hasoptionals = true;
-			text += '[';
-		}
-
-		if (i)
-			text += ", ";
-
-		text += get_type_name (comm->args[i].type);
-		text += ' ';
-		text += comm->args[i].name;
-
-		if (i >= comm->numargs)
-		{
-			text += " = ";
-
-			bool is_string = comm->args[i].type == e_string_type;
-
-			if (is_string)
-				text += '"';
-
-			text += string::from_number (comm->args[i].defvalue);
-
-			if (is_string)
-				text += '"';
-		}
-	}
-
-	if (hasoptionals)
-		text += ']';
-
-	text += ')';
-	return text;
-}
-
-const list<command_info*> get_commands()
-{
-	return g_commands;
-}
--- a/src/commands.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_COMMANDS_H
-#define BOTC_COMMANDS_H
-
-#include "main.h"
-#include "str.h"
-
-struct command_argument
-{
-	type_e					type;
-	string					name;
-	int						defvalue;
-};
-
-struct command_info
-{
-	string					name;
-	int						number;
-	int						numargs;
-	int						maxargs;
-	type_e					returnvalue;
-	list<command_argument>	args;
-};
-
-void						add_command_definition (command_info* comm);
-command_info*				find_command_by_name (string a);
-string						get_command_signature (command_info* comm);
-const list<command_info*>	get_commands();
-
-#endif // BOTC_COMMANDS_H
--- a/src/containers.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,346 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_CONTAINERS_H
-#define BOTC_CONTAINERS_H
-
-#include <cassert>
-#include <algorithm>
-#include <deque>
-#include <initializer_list>
-
-template<class T> class list
-{
-	public:
-		using list_type					= typename ::std::deque<T>;
-		using iterator					= typename list_type::iterator;
-		using const_iterator			= typename list_type::const_iterator;
-		using reverse_iterator			= typename list_type::reverse_iterator;
-		using const_reverse_iterator	= typename list_type::const_reverse_iterator;
-		using element_type				= T;
-		using self_type					= list<T>;
-
-		// =====================================================================
-		//
-		list() {}
-
-		// =====================================================================
-		//
-		list (std::initializer_list<element_type> vals)
-		{
-			m_data = vals;
-		}
-
-		// =====================================================================
-		//
-		list (const list_type& a) :
-			m_data (a) {}
-
-		// =====================================================================
-		//
-		iterator begin()
-		{
-			return m_data.begin();
-		}
-
-		// =====================================================================
-		//
-		const_iterator begin() const
-		{
-			return m_data.cbegin();
-		}
-
-		// =====================================================================
-		//
-		iterator end()
-		{
-			return m_data.end();
-		}
-
-		// =====================================================================
-		//
-		const_iterator end() const
-		{
-			return m_data.cend();
-		}
-
-		// =====================================================================
-		//
-		reverse_iterator rbegin()
-		{
-			return m_data.rbegin();
-		}
-
-		// =====================================================================
-		//
-		const_reverse_iterator crbegin() const
-		{
-			return m_data.crbegin();
-		}
-
-		// =====================================================================
-		//
-		reverse_iterator rend()
-		{
-			return m_data.rend();
-		}
-
-		// =====================================================================
-		//
-		const_reverse_iterator crend() const
-		{
-			return m_data.crend();
-		}
-
-		// =====================================================================
-		//
-		inline void erase (int pos)
-		{
-			assert (pos < size());
-			m_data.erase (m_data.begin() + pos);
-		}
-
-		// =====================================================================
-		//
-		element_type& push_front (const element_type& value)
-		{
-			m_data.push_front (value);
-			return m_data[0];
-		}
-
-		// =====================================================================
-		//
-		element_type& push_back (const element_type& value)
-		{
-			m_data.push_back (value);
-			return m_data[m_data.size() - 1];
-		}
-
-		// =====================================================================
-		//
-		void push_back (const self_type& vals)
-		{
-			for (const T & val : vals)
-				push_back (val);
-		}
-
-		// =====================================================================
-		//
-		bool pop (T& val)
-		{
-			if (is_empty())
-				return false;
-
-			val = m_data[size() - 1];
-			m_data.erase (m_data.end() - 1);
-			return true;
-		}
-
-		// =====================================================================
-		//
-		T& operator<< (const T& value)
-		{
-			return push_back (value);
-		}
-
-		// =====================================================================
-		//
-		void operator<< (const self_type& vals)
-		{
-			push_back (vals);
-		}
-
-		// =====================================================================
-		//
-		bool operator>> (T& value)
-		{
-			return pop (value);
-		}
-
-		// =====================================================================
-		//
-		self_type reverse() const
-		{
-			self_type rev;
-
-			for (const T & val : *this)
-				val >> rev;
-
-			return rev;
-		}
-
-		// =====================================================================
-		//
-		void clear()
-		{
-			m_data.clear();
-		}
-
-		// =====================================================================
-		//
-		void insert (int pos, const element_type& value)
-		{
-			m_data.insert (m_data.begin() + pos, value);
-		}
-
-		// =====================================================================
-		//
-		void makeUnique()
-		{
-			// Remove duplicate entries. For this to be effective, the vector must be
-			// sorted first.
-			sort();
-			iterator pos = std::unique (begin(), end());
-			resize (std::distance (begin(), pos));
-		}
-
-		// =====================================================================
-		//
-		int size() const
-		{
-			return m_data.size();
-		}
-
-		// =====================================================================
-		//
-		element_type& operator[] (int n)
-		{
-			assert (n < size());
-			return m_data[n];
-		}
-
-		// =====================================================================
-		//
-		const element_type& operator[] (int n) const
-		{
-			assert (n < size());
-			return m_data[n];
-		}
-
-		// =====================================================================
-		//
-		void resize (int size)
-		{
-			m_data.resize (size);
-		}
-
-		// =====================================================================
-		//
-		void sort()
-		{
-			std::sort (begin(), end());
-		}
-
-		// =====================================================================
-		//
-		int find (const element_type& needle) const
-		{
-			int i = 0;
-
-			for (const element_type& hay : *this)
-			{
-				if (&hay == &needle)
-					return i;
-
-				i++;
-			}
-
-			return -1;
-		}
-
-		// =====================================================================
-		//
-		void remove (const element_type& it)
-		{
-			int idx;
-
-			if ((idx = find (it)) != -1)
-				erase (idx);
-		}
-
-		// =====================================================================
-		//
-		inline bool is_empty() const
-		{
-			return size() == 0;
-		}
-
-		// =====================================================================
-		//
-		self_type mid (int a, int b) const
-		{
-			assert (a >= 0 && b >= 0 && a < size() && b < size() && a <= b);
-			self_type result;
-
-			for (int i = a; i <= b; ++i)
-				result << operator[] (i);
-
-			return result;
-		}
-
-		// =====================================================================
-		//
-		inline const list_type& std_deque() const
-		{
-			return m_data;
-		}
-
-		// =====================================================================
-		//
-		const element_type& first() const
-		{
-			return *(m_data.begin());
-		}
-
-		// =====================================================================
-		//
-		const element_type& last() const
-		{
-			return *(m_data.end() - 1);
-		}
-
-		// =====================================================================
-		//
-		bool contains (const element_type& a) const
-		{
-			return find (a) != -1;
-		}
-
-	private:
-		list_type m_data;
-};
-
-// =============================================================================
-//
-template<class T> list<T>& operator>> (const T& value, list<T>& haystack)
-{
-	haystack.push_front (value);
-	return haystack;
-}
-
-#endif // BOTC_CONTAINERS_H
--- a/src/data_buffer.cc	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,243 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 "data_buffer.h"
-
-// ============================================================================
-//
-data_buffer::data_buffer (int size)
-{
-	set_writepos (get_buffer());
-	set_buffer (new byte[size]);
-	set_allocated_size (size);
-}
-
-// ============================================================================
-//
-data_buffer::~data_buffer()
-{
-	assert (count_marks() == 0 && count_refs() == 0);
-	delete get_buffer();
-}
-
-// ============================================================================
-//
-void data_buffer::merge_and_destroy (data_buffer* other)
-{
-	if (!other)
-		return;
-
-	int oldsize = get_write_size();
-	copy_buffer (other);
-
-	// Assimilate in its marks and references
-	for (byte_mark* mark : other->get_marks())
-	{
-		mark->pos += oldsize;
-		push_to_marks (mark);
-	}
-
-	for (mark_reference* ref : other->get_refs())
-	{
-		ref->pos += oldsize;
-		push_to_refs (ref);
-	}
-
-	clear_marks();
-	clear_refs();
-	delete other;
-}
-
-// ============================================================================
-//
-data_buffer* data_buffer::clone()
-{
-	data_buffer* other = new data_buffer;
-	other->copy_buffer (this);
-	return other;
-}
-
-// ============================================================================
-//
-void data_buffer::copy_buffer (const data_buffer* buf)
-{
-	check_space (buf->get_write_size());
-	memcpy (m_writepos, buf->get_buffer(), buf->get_write_size());
-	m_writepos += buf->get_write_size();
-}
-
-// ============================================================================
-//
-byte_mark* data_buffer::add_mark (string name)
-{
-	byte_mark* mark = new byte_mark;
-	mark->name = name;
-	mark->pos = get_write_size();
-	push_to_marks (mark);
-	return mark;
-}
-
-// ============================================================================
-//
-mark_reference* data_buffer::add_reference (byte_mark* mark, bool write_placeholder)
-{
-	mark_reference* ref = new mark_reference;
-	ref->target = mark;
-	ref->pos = get_write_size();
-	push_to_refs (ref);
-
-	// Write a dummy placeholder for the reference
-	if (write_placeholder)
-		write_dword (0xBEEFCAFE);
-
-	return ref;
-}
-
-// ============================================================================
-//
-void data_buffer::adjust_mark (byte_mark* mark)
-{
-	mark->pos = get_write_size();
-}
-
-// ============================================================================
-//
-void data_buffer::offset_mark (byte_mark* mark, int offset)
-{
-	mark->pos += offset;
-}
-
-// ============================================================================
-//
-void data_buffer::write_float (float a)
-{
-	// TODO: Find a way to store the number without decimal loss.
-	write_dword (dh_push_number);
-	write_dword (abs (a));
-
-	if (a < 0)
-		write_dword (dh_unary_minus);
-}
-
-// ============================================================================
-//
-void data_buffer::write_string_index (const string& a)
-{
-	write_dword (dh_push_string_index);
-	write_dword (get_string_table_index (a));
-}
-
-// ============================================================================
-//
-void data_buffer::dump()
-{
-	for (int i = 0; i < get_write_size(); ++i)
-		printf ("%d. [%d]\n", i, get_buffer()[i]);
-}
-
-// ============================================================================
-//
-void data_buffer::check_space (int bytes)
-{
-	int writesize = get_write_size();
-
-	if (writesize + bytes < get_allocated_size())
-		return;
-
-	// We don't have enough space in the buffer to write
-	// the stuff - thus resize. First, store the old
-	// buffer temporarily:
-	char* copy = new char[get_allocated_size()];
-	memcpy (copy, get_buffer(), get_allocated_size());
-
-	// Remake the buffer with the new size. Have enough space
-	// for the stuff we're going to write, as well as a bit
-	// of leeway so we don't have to resize immediately again.
-	size_t newsize = get_allocated_size() + bytes + 512;
-
-	delete get_buffer();
-	set_buffer (new byte[newsize]);
-	set_allocated_size (newsize);
-
-	// Now, copy the stuff back.
-	memcpy (m_buffer, copy, get_allocated_size());
-	set_writepos (get_buffer() + writesize);
-	delete copy;
-}
-
-// =============================================================================
-//
-void data_buffer::write_byte (int8_t data)
-{
-	check_space (1);
-	*m_writepos++ = data;
-}
-
-// =============================================================================
-//
-void data_buffer::write_word (int16_t data)
-{
-	check_space (2);
-
-	for (int i = 0; i < 2; ++i)
-		*m_writepos++ = (data >> (i * 8)) & 0xFF;
-}
-
-// =============================================================================
-//
-void data_buffer::write_dword (int32_t data)
-{
-	check_space (4);
-
-	for (int i = 0; i < 4; ++i)
-		*m_writepos++ = (data >> (i * 8)) & 0xFF;
-}
-
-// =============================================================================
-//
-void data_buffer::write_string (const string& a)
-{
-	check_space (a.length() + 1);
-
-	for (char c : a)
-		write_byte (c);
-
-	write_byte ('\0');
-}
-
-
-// =============================================================================
-//
-byte_mark* data_buffer::find_mark_by_name (const string& target)
-{
-	for (byte_mark* mark : get_marks())
-		if (mark->name == target)
-			return mark;
-
-	return null;
-}
\ No newline at end of file
--- a/src/data_buffer.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_DATABUFFER_H
-#define BOTC_DATABUFFER_H
-
-#include <stdio.h>
-#include <string.h>
-#include "main.h"
-#include "stringtable.h"
-
-#define MAX_MARKS 512
-
-// ============================================================================
-// data_buffer: A dynamic data buffer.
-//
-// Notes:
-//
-// - A mark is a "pointer" to a particular position in the bytecode. The actual
-//   permanent position cannot be predicted in any way or form, thus these things
-//   are used to "mark" a position like that for future use.
-//
-// - A reference is another "mark" that references a mark. When the bytecode
-//   is written to file, they are changed into their marks' current
-//   positions. Marks themselves are never written to files, only refs are
-//
-class data_buffer
-{
-	PROPERTY (private, byte*,					buffer,			NO_OPS,		STOCK_WRITE)
-	PROPERTY (private, int,						allocated_size,	NUM_OPS,	STOCK_WRITE)
-	PROPERTY (private, byte*,					writepos,		NO_OPS,		STOCK_WRITE)
-	PROPERTY (private, list<byte_mark*>,		marks,			LIST_OPS,	STOCK_WRITE)
-	PROPERTY (private, list<mark_reference*>,	refs,			LIST_OPS,	STOCK_WRITE)
-
-	public:
-		data_buffer (int size = 128);
-		~data_buffer();
-
-		// ====================================================================
-		// Merge another data buffer into this one.
-		// Note: @other is destroyed in the process!
-		void merge_and_destroy (data_buffer* other);
-
-		// Clones this databuffer to a new one and returns it.
-		data_buffer* clone ();
-
-		byte_mark* add_mark (string name);
-
-		mark_reference*	add_reference (byte_mark* mark, bool write_placeholder = true);
-		void			check_space (int bytes);
-		void			delete_mark (int marknum);
-		void			adjust_mark(byte_mark* mark);
-		void			offset_mark (byte_mark* mark, int offset);
-		byte_mark*		find_mark_by_name (const string& target);
-		void			dump();
-		void			write_float (float a);
-		void			write_string_index (const string& a);
-		void			write_string (const string& a);
-		void			write_byte (int8_t data);
-		void			write_word (int16_t data);
-		void			write_dword (int32_t data);
-		void			copy_buffer (const data_buffer* buf);
-
-		inline int get_write_size() const
-		{
-			return m_writepos - get_buffer();
-		}
-};
-
-#endif // BOTC_DATABUFFER_H
--- a/src/events.cc	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <stdlib.h>
-#include <stdio.h>
-#include "main.h"
-#include "str.h"
-#include "events.h"
-#include "lexer.h"
-
-static void unlink_events();
-static list<event_info*> g_events;
-
-// ============================================================================
-//
-void add_event (event_info* e)
-{
-	g_events << e;
-}
-
-// ============================================================================
-//
-// Delete event definitions recursively
-//
-static void unlink_events()
-{
-	for (event_info* e : g_events)
-		delete e;
-
-	g_events.clear();
-}
-
-// ============================================================================
-//
-// Finds an event definition by index
-//
-event_info* find_event_by_index (int idx)
-{
-	return g_events[idx];
-}
-
-// ============================================================================
-//
-// Finds an event definition by name
-//
-event_info* find_event_by_name (string a)
-{
-	for (event_info* e : g_events)
-		if (a.to_uppercase() == e->name.to_uppercase())
-			return e;
-
-	return null;
-}
--- a/src/events.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_EVENTS_H
-#define BOTC_EVENTS_H
-
-#include "str.h"
-
-struct event_info
-{
-	string name;
-	int number;
-};
-
-void add_event (event_info* e);
-event_info* find_event_by_index (int idx);
-event_info* find_event_by_name (string a);
-
-#endif // BOTC_EVENTS_H
--- a/src/format.cc	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <cstdio>
-#include "main.h"
-#include "format.h"
-#include "lexer.h"
-
-// =============================================================================
-//
-static void draw_position (const string& fmt, int pos)
-{
-	string rep (fmt);
-	rep.replace ("\n", "↵");
-	rep.replace ("\t", "⇥");
-
-	fprintf (stderr, "%s\n", rep.chars());
-
-	for (int x = 0; x < pos; ++x)
-		fprintf (stderr, "-");
-
-	fprintf (stderr, "^\n");
-}
-
-// =============================================================================
-//
-string format_args (const list<format_arg>& args)
-{
-	const string& fmtstr = args[0].as_string();
-	assert (args.size() >= 1);
-
-	if (args.size() == 1)
-		return args[0].as_string();
-
-	string fmt = fmtstr;
-	string out;
-	int pos = 0;
-
-	while ((pos = fmt.first ("%", pos)) != -1)
-	{
-		if (fmt[pos + 1] == '%')
-		{
-			fmt.replace (pos, 2, "%");
-			pos++;
-			continue;
-		}
-
-		int ofs = 1;
-		char mod = '\0';
-
-		// handle modifiers
-		if (fmt[pos + ofs] == 's' || fmt[pos + ofs] == 'x')
-		{
-			mod = fmt[pos + ofs];
-			ofs++;
-		}
-
-		if (!isdigit (fmt[pos + ofs]))
-		{
-			fprintf (stderr, "bad format string, expected digit with optional "
-				"modifier after '%%':\n");
-			draw_position (fmt, pos);
-			return fmt;
-		}
-
-		int i = fmt[pos + ofs]  - '0';
-
-		if (i >= args.size())
-		{
-			fprintf (stderr, "format arg #%d used but not defined: %s\n", i, fmtstr.chars());
-			return fmt;
-		}
-
-		string repl = args[i].as_string();
-
-		switch (mod)
-		{
-			case 's': repl = (repl == "1") ? "" : "s";			break;
-			case 'd': repl.sprintf ("%d", repl[0]);				break;
-			case 'x': repl.sprintf ("0x%X", repl.to_long());	break;
-			default: break;
-		}
-
-		fmt.replace (pos, 1 + ofs, repl);
-		pos += repl.length();
-	}
-
-	return fmt;
-}
-
-// =============================================================================
-//
-void print_args (FILE* fp, const list<format_arg>& args)
-{
-	string out = format_args (args);
-	fprintf (fp, "%s", out.chars());
-}
-
-// =============================================================================
-//
-void do_error (string msg)
-{
-	lexer* lx = lexer::get_current_lexer();
-	string fileinfo;
-
-	if (lx != null && lx->has_valid_token())
-	{
-		lexer::token* tk = lx->get_token();
-		fileinfo = format ("%1:%2:%3: ", tk->file, tk->line, tk->column);
-	}
-
-	throw script_error (fileinfo + msg);
-}
\ No newline at end of file
--- a/src/format.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_FORMAT_H
-#define BOTC_FORMAT_H
-
-#include "str.h"
-#include "containers.h"
-
-class format_arg
-{
-	public:
-		format_arg (const string& a) :
-			m_string (a) {}
-
-		format_arg (char a) :
-			m_string (a) {}
-
-		format_arg (int a) :
-			m_string (string::from_number (a)) {}
-
-		format_arg (long a) :
-			m_string (string::from_number (a)) {}
-
-		format_arg (const char* a) :
-			m_string (a) {}
-
-		format_arg (void* a)
-		{
-			m_string.sprintf ("%p", a);
-		}
-
-		format_arg (const void* a)
-		{
-			m_string.sprintf ("%p", a);
-		}
-
-		template<class T> format_arg (const list<T>& list)
-		{
-			if (list.is_empty())
-			{
-				m_string = "{}";
-				return;
-			}
-
-			m_string = "{ ";
-
-			for (const T & a : list)
-			{
-				if (&a != &list[0])
-					m_string += ", ";
-
-				m_string += format_arg (a).as_string();
-			}
-
-			m_string += " }";
-		}
-
-		inline const string& as_string() const
-		{
-			return m_string;
-		}
-
-	private:
-		string m_string;
-};
-
-template<class T> string custom_format (T a, const char* fmtstr)
-{
-	string out;
-	out.sprintf (fmtstr, a);
-	return out;
-}
-
-string format_args (const list<format_arg>& args);
-void print_args (FILE* fp, const list<format_arg>& args);
-void do_fatal (const list<format_arg>& args);
-void do_error (string msg);
-
-#ifndef IN_IDE_PARSER
-# define format(...) format_args({ __VA_ARGS__ })
-# define fprint(A, ...) print_args( A, { __VA_ARGS__ })
-# define print(...) print_args( stdout, { __VA_ARGS__ })
-# define error(...) do_error (format (__VA_ARGS__))
-#else
-string format (void, ...);
-void fprint (FILE* fp, ...);
-void print (void, ...);
-void error (void, ...);
-#endif
-
-#ifndef IN_IDE_PARSER
-# ifdef DEBUG
-#  define devf(...) fprint (stderr, __VA_ARGS__)
-#  define dvalof( A ) fprint (stderr, "value of '%1' = %2\n", #A, A)
-# else
-#  define devf(...)
-#  define dvalof( A )
-# endif // DEBUG
-#else
-// print something in debug builds
-void devf (void, ...);
-
-// print the value of @a
-void dvalof (void a);
-#endif // IN_IDE_PARSER
-
-#endif // BOTC_FORMAT_H
--- a/src/lexer.cc	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,305 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <cstring>
-#include "lexer.h"
-
-static string_list	g_file_name_stack;
-static lexer*		g_main_lexer = null;
-
-// =============================================================================
-//
-lexer::lexer()
-{
-	assert (g_main_lexer == null);
-	g_main_lexer = this;
-}
-
-// =============================================================================
-//
-lexer::~lexer()
-{
-	g_main_lexer = null;
-}
-
-// =============================================================================
-//
-void lexer::process_file (string file_name)
-{
-	g_file_name_stack << file_name;
-	FILE* fp = fopen (file_name, "r");
-
-	if (fp == null)
-		error ("couldn't open %1 for reading: %2", file_name, strerror (errno));
-
-	lexer_scanner sc (fp);
-	check_file_header (sc);
-
-	while (sc.get_next_token())
-	{
-		// Preprocessor commands:
-		if (sc.get_token_type() == tk_hash)
-		{
-			must_get_next_from_scanner (sc, tk_symbol);
-
-			if (sc.get_token_text() == "include")
-			{
-				must_get_next_from_scanner (sc, tk_string);
-				string file_name = sc.get_token_text();
-
-				if (g_file_name_stack.contains (file_name))
-					error ("attempted to #include %1 recursively", sc.get_token_text());
-
-				process_file (file_name);
-			}
-			else
-				error ("unknown preprocessor directive \"#%1\"", sc.get_token_text());
-		}
-		else
-		{
-			token tok;
-			tok.file = file_name;
-			tok.line = sc.get_line();
-			tok.column = sc.get_column();
-			tok.type = sc.get_token_type();
-			tok.text = sc.get_token_text();
-
-			// devf ("Token #%1: %2:%3:%4: %5 (%6)\n", m_tokens.size(),
-			//	tok.file, tok.line, tok.column, describe_token (&tok), describe_token_type (tok.type));
-
-			m_tokens << tok;
-		}
-	}
-
-	m_token_position = m_tokens.begin() - 1;
-	g_file_name_stack.remove (file_name);
-}
-
-// ============================================================================
-//
-static bool is_valid_header (string header)
-{
-	if (header.ends_with ("\n"))
-		header.remove_from_end (1);
-
-	string_list tokens = header.split (" ");
-
-	if (tokens.size() != 2 || tokens[0] != "#!botc" || tokens[1].empty())
-		return false;
-
-	string_list nums = tokens[1].split (".");
-
-	if (nums.size() == 2)
-		nums << "0";
-	elif (nums.size() != 3)
-		return false;
-
-	bool ok_a, ok_b, ok_c;
-	long major = nums[0].to_long (&ok_a);
-	long minor = nums[1].to_long (&ok_b);
-	long patch = nums[2].to_long (&ok_c);
-
-	if (!ok_a || !ok_b || !ok_c)
-		return false;
-
-	if (VERSION_NUMBER < MAKE_VERSION_NUMBER (major, minor, patch))
-		error ("The script file requires " APPNAME " v%1, this is v%2",
-			make_version_string (major, minor, patch), get_version_string (e_short_form));
-
-	return true;
-}
-
-// ============================================================================
-//
-void lexer::check_file_header (lexer_scanner& sc)
-{
-	if (!is_valid_header (sc.read_line()))
-		error ("Not a valid botscript file! File must start with '#!botc <version>'");
-}
-
-// =============================================================================
-//
-bool lexer::get_next (e_token req)
-{
-	iterator pos = m_token_position;
-
-	if (m_tokens.is_empty())
-		return false;
-
-	m_token_position++;
-
-	if (is_at_end() || (req != tk_any && get_token_type() != req))
-	{
-		m_token_position = pos;
-		return false;
-	}
-
-	return true;
-}
-
-// =============================================================================
-//
-void lexer::must_get_next (e_token tt)
-{
-	if (!get_next())
-		error ("unexpected EOF");
-
-	if (tt != tk_any)
-		must_be (tt);
-}
-
-// =============================================================================
-// eugh..
-//
-void lexer::must_get_next_from_scanner (lexer_scanner& sc, e_token tt)
-{
-	if (!sc.get_next_token())
-		error ("unexpected EOF");
-
-	if (tt != tk_any && sc.get_token_type() != tt)
-	{	// TODO
-		token tok;
-		tok.type = sc.get_token_type();
-		tok.text = sc.get_token_text();
-
-		error ("at %1:%2: expected %3, got %4",
-			g_file_name_stack.last(),
-			sc.get_line(),
-			describe_token_type (tt),
-			describe_token (&tok));
-	}
-}
-
-// =============================================================================
-//
-void lexer::must_get_any_of (const list<e_token>& toks)
-{
-	if (!get_next())
-		error ("unexpected EOF");
-
-	for (e_token tok : toks)
-		if (get_token_type() == tok)
-			return;
-
-	string toknames;
-
-	for (const e_token& tok_type : toks)
-	{
-		if (&tok_type == &toks.last())
-			toknames += " or ";
-		elif (toknames.is_empty() == false)
-			toknames += ", ";
-
-		toknames += describe_token_type (tok_type);
-	}
-
-	error ("expected %1, got %2", toknames, describe_token (get_token()));
-}
-
-// =============================================================================
-//
-int lexer::get_one_symbol (const string_list& syms)
-{
-	if (!get_next())
-		error ("unexpected EOF");
-
-	if (get_token_type() == tk_symbol)
-	{
-		for (int i = 0; i < syms.size(); ++i)
-		{
-			if (syms[i] == get_token()->text)
-				return i;
-		}
-	}
-
-	error ("expected one of %1, got %2", syms, describe_token (get_token()));
-	return -1;
-}
-
-// =============================================================================
-//
-void lexer::must_be (e_token tok)
-{
-	if (get_token_type() != tok)
-		error ("expected %1, got %2", describe_token_type (tok),
-			describe_token (get_token()));
-}
-
-// =============================================================================
-//
-string lexer::describe_token_private (e_token tok_type, lexer::token* tok)
-{
-	if (tok_type < tk_last_named_token)
-		return "\"" + lexer_scanner::get_token_string (tok_type) + "\"";
-
-	switch (tok_type)
-	{
-		case tk_symbol:	return tok ? tok->text : "a symbol";
-		case tk_number:	return tok ? tok->text : "a number";
-		case tk_string:	return tok ? ("\"" + tok->text + "\"") : "a string";
-		case tk_any:	return tok ? tok->text : "any token";
-		default: break;
-	}
-
-	return "";
-}
-
-// =============================================================================
-//
-bool lexer::peek_next (lexer::token* tk)
-{
-	iterator pos = m_token_position;
-	bool r = get_next();
-
-	if (r && tk != null)
-		*tk = *m_token_position;
-
-	m_token_position = pos;
-	return r;
-}
-
-// =============================================================================
-//
-lexer* lexer::get_current_lexer()
-{
-	return g_main_lexer;
-}
-
-// =============================================================================
-//
-string lexer::peek_next_string (int a)
-{
-	if (m_token_position + a >= m_tokens.end())
-		return "";
-
-	iterator oldpos = m_token_position;
-	m_token_position += a;
-	string result = get_token()->text;
-	m_token_position = oldpos;
-	return result;
-}
--- a/src/lexer.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_LEXER_H
-#define BOTC_LEXER_H
-
-#include "main.h"
-#include "lexer_scanner.h"
-
-class lexer
-{
-types:
-	struct token
-	{
-		e_token		type;
-		string		text;
-		string		file;
-		int			line;
-		int			column;
-	};
-
-	using token_list	= list<token>;
-	using iterator		= token_list::iterator;
-
-public:
-	lexer();
-	~lexer();
-
-	void	process_file (string file_name);
-	bool	get_next (e_token req = tk_any);
-	void	must_get_next (e_token tok = tk_any);
-	void	must_get_any_of (const list<e_token>& toks);
-	int		get_one_symbol (const string_list& syms);
-	void	must_be (e_token tok);
-	bool	peek_next (token* tk = null);
-
-	inline bool has_valid_token() const
-	{
-		return (m_token_position < m_tokens.end() && m_token_position >= m_tokens.begin());
-	}
-
-	inline token* get_token() const
-	{
-		assert (has_valid_token() == true);
-		return &(*m_token_position);
-	}
-
-	inline bool is_at_end() const
-	{
-		return m_token_position == m_tokens.end();
-	}
-
-	inline e_token get_token_type() const
-	{
-		return get_token()->type;
-	}
-
-	// If @tok is given, describes the token. If not, describes @tok_type.
-	static inline string describe_token_type (e_token tok_type)
-	{
-		return describe_token_private (tok_type, null);
-	}
-
-	static inline string describe_token (token* tok)
-	{
-		return describe_token_private (tok->type, tok);
-	}
-
-	static lexer* get_current_lexer();
-
-	inline void skip (int a = 1)
-	{
-		m_token_position += a;
-	}
-
-	string peek_next_string (int a = 1);
-
-private:
-	token_list		m_tokens;
-	iterator		m_token_position;
-
-	// read a mandatory token from scanner
-	void must_get_next_from_scanner (lexer_scanner& sc, e_token tt = tk_any);
-	void check_file_header (lexer_scanner& sc);
-
-	static string describe_token_private (e_token tok_type, token* tok);
-};
-
-#endif // BOTC_LEXER_H
--- a/src/lexer_scanner.cc	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,290 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <cstdio>
-#include <cstdlib>
-#include <cassert>
-#include <cstring>
-#include <string>
-#include "lexer_scanner.h"
-#include "lexer.h"
-
-static const string g_token_strings[] =
-{
-	"==",
-	"[]",
-	"+=",
-	"-=",
-	"*=",
-	"/=",
-	"%=",
-	"'",
-	"$",
-	"(",
-	")",
-	"[",
-	"]",
-	"{",
-	"}",
-	"=",
-	"+",
-	"-",
-	"*",
-	"/",
-	"%",
-	",",
-	"<",
-	">",
-	".",
-	":",
-	";",
-	"#",
-	"!",
-	"->",
-	"bool",
-	"break",
-	"case",
-	"continue",
-	"const",
-	"default",
-	"do",
-	"else",
-	"event",
-	"eventdef",
-	"for",
-	"funcdef",
-	"goto",
-	"if",
-	"int",
-	"mainloop",
-	"onenter",
-	"onexit",
-	"state",
-	"switch",
-	"str",
-	"void",
-	"while",
-	"enum",
-	"func",
-	"return",
-};
-
-static_assert (countof (g_token_strings) == (int) tk_last_named_token + 1,
-	"Count of g_token_strings is not the same as the amount of named token identifiers.");
-
-// =============================================================================
-//
-lexer_scanner::lexer_scanner (FILE* fp) :
-	m_line (1)
-{
-	long fsize, bytes;
-
-	fseek (fp, 0l, SEEK_END);
-	fsize = ftell (fp);
-	rewind (fp);
-	m_data = new char[fsize];
-	m_ptr = m_line_break_pos = &m_data[0];
-	bytes = fread (m_data, 1, fsize, fp);
-	assert (bytes >= fsize);
-}
-
-// =============================================================================
-//
-lexer_scanner::~lexer_scanner()
-{
-	delete m_data;
-}
-
-// =============================================================================
-//
-bool lexer_scanner::check_string (const char* c, int flags)
-{
-	bool r = strncmp (m_ptr, c, strlen (c)) == 0;
-
-	// There is to be a non-symbol character after words
-	if (r && (flags & f_check_word) && is_symbol_char (m_ptr[strlen (c)], true))
-		r = false;
-
-	// Advance the cursor unless we want to just peek
-	if (r && !(flags & f_check_peek))
-		m_ptr += strlen (c);
-
-	return r;
-}
-
-// =============================================================================
-//
-bool lexer_scanner::get_next_token()
-{
-	m_token_text = "";
-
-	while (isspace (*m_ptr))
-		skip();
-
-	// Check for comments
-	if (strncmp (m_ptr, "//", 2) == 0)
-	{
-		m_ptr += 2;
-
-		while (*m_ptr != '\n')
-			skip();
-
-		return get_next_token();
-	}
-	elif (strncmp (m_ptr, "/*", 2) == 0)
-	{
-		skip (2); // skip the start symbols
-
-		while (strncmp (m_ptr, "*/", 2) != 0)
-			skip();
-
-		skip (2); // skip the end symbols
-		return get_next_token();
-	}
-
-	if (*m_ptr == '\0')
-		return false;
-
-	// Check tokens
-	for (int i = 0; i < countof (g_token_strings); ++i)
-	{
-		int flags = 0;
-
-		if (i >= tk_first_named_token)
-			flags |= f_check_word;
-
-		if (check_string (g_token_strings[i], flags))
-		{
-			m_token_text = g_token_strings[i];
-			m_token_type = (e_token) i;
-			return true;
-		}
-	}
-
-	// Check and parse string
-	if (*m_ptr == '\"')
-	{
-		m_ptr++;
-
-		while (*m_ptr != '\"')
-		{
-			if (!*m_ptr)
-				error ("unterminated string");
-
-			if (check_string ("\\n"))
-			{
-				m_token_text += '\n';
-				continue;
-			}
-			elif (check_string ("\\t"))
-			{
-				m_token_text += '\t';
-				continue;
-			}
-			elif (check_string ("\\\""))
-			{
-				m_token_text += '"';
-				continue;
-			}
-
-			m_token_text += *m_ptr++;
-		}
-
-		m_token_type = tk_string;
-		skip(); // skip the final quote
-		return true;
-	}
-
-	if (isdigit (*m_ptr))
-	{
-		while (isdigit (*m_ptr))
-			m_token_text += *m_ptr++;
-
-		m_token_type = tk_number;
-		return true;
-	}
-
-	if (is_symbol_char (*m_ptr, false))
-	{
-		m_token_type = tk_symbol;
-
-		do
-		{
-			if (!is_symbol_char (*m_ptr, true))
-				break;
-
-			m_token_text += *m_ptr++;
-		} while (*m_ptr != '\0');
-
-		return true;
-	}
-
-	error ("unknown character \"%1\"", *m_ptr);
-	return false;
-}
-
-// =============================================================================
-//
-void lexer_scanner::skip()
-{
-	if (*m_ptr == '\n')
-	{
-		m_line++;
-		m_line_break_pos = m_ptr;
-	}
-
-	m_ptr++;
-}
-
-// =============================================================================
-//
-void lexer_scanner::skip (int chars)
-{
-	for (int i = 0; i < chars; ++i)
-		skip();
-}
-
-// =============================================================================
-//
-string lexer_scanner::get_token_string (e_token a)
-{
-	assert ((int) a <= tk_last_named_token);
-	return g_token_strings[a];
-}
-
-// =============================================================================
-//
-string lexer_scanner::read_line()
-{
-	string line;
-
-	while (*m_ptr != '\n')
-		line += *(m_ptr++);
-
-	return line;
-}
\ No newline at end of file
--- a/src/lexer_scanner.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_LEXER_SCANNER_H
-#define BOTC_LEXER_SCANNER_H
-
-#include <climits>
-#include "main.h"
-
-class lexer_scanner
-{
-	types:
-		struct position_info
-		{
-			char*	pos;
-			char*	line_break_pos;
-			int		line;
-		};
-
-		// Flags for check_string()
-		enum
-		{
-			f_check_word = (1 << 0),   // must be followed by whitespace
-			f_check_peek = (1 << 1),   // don't advance cursor
-		};
-
-	public:
-		static inline bool is_symbol_char (char c, bool allow_numbers)
-		{
-			if (allow_numbers && (c >= '0' && c <= '9'))
-				return true;
-
-			return (c >= 'a' && c <= 'z') ||
-				   (c >= 'A' && c <= 'Z') ||
-				   (c == '_');
-		}
-
-		lexer_scanner (FILE* fp);
-		~lexer_scanner();
-		bool get_next_token();
-		string read_line();
-
-		inline const string& get_token_text() const
-		{
-			return m_token_text;
-		}
-
-		inline int get_line() const
-		{
-			return m_line;
-		}
-
-		inline int get_column() const
-		{
-			return m_ptr - m_line_break_pos;
-		}
-
-		inline e_token get_token_type() const
-		{
-			return m_token_type;
-		}
-
-		static string get_token_string (e_token a);
-
-	private:
-		char*			m_data;
-		char*			m_ptr;
-		char*			m_line_break_pos;
-		string			m_token_text,
-						m_last_token;
-		e_token			m_token_type;
-		int				m_line;
-
-		bool			check_string (const char* c, int flags = 0);
-
-		// Yields a copy of the current position information.
-		position_info	get_position() const;
-
-		// Sets the current position based on given data.
-		void			set_position (const position_info& a);
-
-		// Skips one character
-		void			skip();
-
-		// Skips many characters
-		void			skip (int chars);
-};
-
-#endif // BOTC_LEXER_SCANNER_H
--- a/src/main.cc	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,191 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 "main.h"
-#include "events.h"
-#include "commands.h"
-#include "stringtable.h"
-#include "variables.h"
-#include "data_buffer.h"
-#include "parser.h"
-#include "lexer.h"
-#include "gitinfo.h"
-
-int main (int argc, char** argv)
-{
-	try
-	{
-		// Intepret command-line parameters:
-		// -l: list commands
-		// I guess there should be a better way to do this.
-		if (argc == 2 && !strcmp (argv[1], "-l"))
-		{
-			printf ("Begin list of commands:\n");
-			printf ("------------------------------------------------------\n");
-
-			for (command_info* comm : get_commands())
-				print ("%1\n", get_command_signature (comm));
-
-			printf ("------------------------------------------------------\n");
-			printf ("End of command list\n");
-			exit (0);
-		}
-
-		// Print header
-		string header;
-		string headerline;
-		header = format (APPNAME " version %1", get_version_string (e_long_form));
-
-#ifdef DEBUG
-		header += " (debug build)";
-#endif
-
-		for (int i = 0; i < header.length() / 2; ++i)
-			headerline += "-=";
-
-		headerline += '-';
-		print ("%2\n\n%1\n\n%2\n\n", header, headerline);
-
-		if (argc < 2)
-		{
-			fprintf (stderr, "usage: %s <infile> [outfile] # compiles botscript\n", argv[0]);
-			fprintf (stderr, "       %s -l                 # lists commands\n", argv[0]);
-			exit (1);
-		}
-
-		string outfile;
-
-		if (argc < 3)
-			outfile = make_object_file_name (argv[1]);
-		else
-			outfile = argv[2];
-
-		// Prepare reader and writer
-		botscript_parser* parser = new botscript_parser;
-
-		// We're set, begin parsing :)
-		print ("Parsing script...\n");
-		parser->parse_botscript (argv[1]);
-		print ("Script parsed successfully.\n");
-
-		// Parse done, print statistics and write to file
-		int globalcount = g_GlobalVariables.size();
-		int stringcount = num_strings_in_table();
-		print ("%1 / %2 strings written\n", stringcount, g_max_stringlist_size);
-		print ("%1 / %2 global variables\n", globalcount, g_max_global_vars);
-		print ("%1 / %2 events\n", parser->get_num_events(), g_max_events);
-		print ("%1 state%s1\n", parser->get_num_states());
-
-		parser->write_to_file (outfile);
-
-		// Clear out the junk
-		delete parser;
-
-		// Done!
-		exit (0);
-	}
-	catch (script_error& e)
-	{
-		fprint (stderr, "error: %1\n", e.what());
-	}
-}
-
-// ============================================================================
-//
-// Mutates given filename to an object filename
-//
-string make_object_file_name (string s)
-{
-	// Locate the extension and chop it out
-	int extdot = s.last (".");
-
-	if (extdot >= s.length() - 4)
-		s -= (s.length() - extdot);
-
-	s += ".o";
-	return s;
-}
-
-// ============================================================================
-//
-type_e get_type_by_name (string t)
-{
-	t = t.to_lowercase();
-	return	(t == "int") ? e_int_type :
-			(t == "str") ? e_string_type :
-			(t == "void") ? e_void_type :
-			(t == "bool") ? e_bool_type :
-			e_unknown_type;
-}
-
-
-// ============================================================================
-//
-// Inverse operation - type name by value
-//
-string get_type_name (type_e type)
-{
-	switch (type)
-	{
-		case e_int_type: return "int"; break;
-		case e_string_type: return "str"; break;
-		case e_void_type: return "void"; break;
-		case e_bool_type: return "bool"; break;
-		case e_unknown_type: return "???"; break;
-	}
-
-	return "";
-}
-
-// =============================================================================
-//
-string make_version_string (int major, int minor, int patch)
-{
-	string ver = format ("%1.%2", major, minor);
-
-	if (patch != 0)
-	{
-		ver += ".";
-		ver += patch;
-	}
-
-	return ver;
-}
-
-// =============================================================================
-//
-string get_version_string (form_length_e len)
-{
-	string tag (GIT_DESCRIPTION);
-	string version = tag;
-
-	if (tag.ends_with ("-pre") && len == e_long_form)
-		version += "-" + string (GIT_HASH).mid (0, 8);
-
-	return version;
-}
\ No newline at end of file
--- a/src/main.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_MAIN_H
-#define BOTC_MAIN_H
-
-#if !defined (__cplusplus) || __cplusplus < 201103L
-# error botc requires a C++11-compliant compiler to be built
-#endif
-
-#include <cstdio>
-#include <cstdarg>
-#include <cstdint>
-#include "property.h"
-#include "types.h"
-#include "containers.h"
-#include "str.h"
-#include "format.h"
-#include "botstuff.h"
-#include "tokens.h"
-
-// Application name and version
-#define APPNAME "botc"
-#define VERSION_MAJOR	1
-#define VERSION_MINOR	0
-#define VERSION_PATCH 	0
-
-#define MAKE_VERSION_NUMBER(MAJ, MIN, PAT) ((MAJ * 10000) + (MIN * 100) + PAT)
-#define VERSION_NUMBER MAKE_VERSION_NUMBER (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)
-
-// On Windows, files are case-insensitive
-#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)
-#define FILE_CASEINSENSITIVE
-#endif
-
-#define elif else if
-
-#define types public
-#define countof(A) ((int) (sizeof A / sizeof *A))
-
-// Shortcut for zeroing something
-#define ZERO(obj) memset (&obj, 0, sizeof (obj));
-
-enum form_length_e { e_long_form, e_short_form };
-
-string make_object_file_name (string s);
-bool fexists (string path);
-type_e get_type_by_name (string token);
-string get_type_name (type_e type);
-string get_version_string (form_length_e len);
-string make_version_string (int major, int minor, int patch);
-
-#ifndef __GNUC__
-#define __attribute__(X)
-#endif
-#define deprecated __attribute__ ((deprecated))
-
-#endif // BOTC_MAIN_H
--- a/src/parser.cc	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1649 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 "parser.h"
-#include "events.h"
-#include "commands.h"
-#include "stringtable.h"
-#include "variables.h"
-#include "containers.h"
-#include "lexer.h"
-#include "data_buffer.h"
-
-#define SCOPE(n) (m_scope_stack[m_scope_cursor - n])
-
-// ============================================================================
-//
-botscript_parser::botscript_parser() :
-	m_lx (new lexer) {}
-
-// ============================================================================
-//
-botscript_parser::~botscript_parser()
-{
-	delete m_lx;
-}
-
-// ============================================================================
-//
-void botscript_parser::check_toplevel()
-{
-	if (m_current_mode != e_top_level_mode)
-		error ("%1-statements may only be defined at top level!", token_string());
-}
-
-// ============================================================================
-//
-void botscript_parser::check_not_toplevel()
-{
-	if (m_current_mode == e_top_level_mode)
-		error ("%1-statements must not be defined at top level!", token_string());
-}
-
-// ============================================================================
-// 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 botscript_parser::parse_botscript (string file_name)
-{
-	// Lex and preprocess the file
-	m_lx->process_file (file_name);
-
-	m_current_mode = e_top_level_mode;
-	m_num_states = 0;
-	m_num_events = 0;
-	m_scope_cursor = 0;
-	m_state_spawn_defined = false;
-	m_got_main_loop = false;
-	m_if_expression = null;
-	m_can_else = false;
-
-	// Zero the entire block stack first
-	// TODO: this shouldn't be necessary
-	for (int i = 0; i < MAX_SCOPE; i++)
-		ZERO (m_scope_stack[i]);
-
-	while (m_lx->get_next())
-	{
-		// Check if else is potentically valid
-		if (token_is (tk_else) && !m_can_else)
-			error ("else without preceding if");
-
-		if (!token_is (tk_else))
-			m_can_else = false;
-
-		switch (m_lx->get_token()->type)
-		{
-			case tk_state:
-				parse_state_block();
-				break;
-
-			case tk_event:
-				parse_event_block();
-				break;
-
-			case tk_mainloop:
-				parse_mainloop();
-				break;
-
-			case tk_onenter:
-			case tk_onexit:
-				parse_on_enter_exit();
-				break;
-
-			case tk_int:
-			case tk_str:
-			case tk_void:
-				parse_variable_declaration();
-				break;
-
-			case tk_goto:
-				parse_goto();
-				break;
-
-			case tk_if:
-				parse_if();
-				break;
-
-			case tk_else:
-				parse_else();
-				break;
-
-			case tk_while:
-				parse_while_block();
-				break;
-
-			case tk_for:
-				parse_for_block();
-				break;
-
-			case tk_do:
-				parse_do_block();
-				break;
-
-			case tk_switch:
-				parse_switch_block();
-				break;
-
-			case tk_case:
-				parse_switch_case();
-				break;
-
-			case tk_default:
-				parse_switch_default();
-				break;
-
-			case tk_break:
-				parse_break();
-				break;
-
-			case tk_continue:
-				parse_continue();
-				break;
-
-			case tk_brace_end:
-				parse_block_end();
-				break;
-
-			case tk_const:
-				parse_const();
-				break;
-
-			case tk_eventdef:
-				parse_eventdef();
-				break;
-
-			case tk_funcdef:
-				parse_funcdef();
-				break;
-
-			default:
-			{
-				// Check for labels
-				lexer::token next;
-
-				if (token_is (tk_symbol) &&
-					m_lx->peek_next (&next) &&
-					next.type == tk_colon)
-				{
-					parse_label();
-					break;
-				}
-
-				// Check if it's a command
-				command_info* comm = find_command_by_name (token_string());
-
-				if (comm)
-				{
-					buffer()->merge_and_destroy (parse_command (comm));
-					m_lx->must_get_next (tk_semicolon);
-					continue;
-				}
-
-				// If nothing else, parse it as a statement
-				data_buffer* b = parse_statement();
-
-				if (!b)
-					error ("unknown token `%1`", token_string());
-
-				buffer()->merge_and_destroy (b);
-				m_lx->must_get_next (tk_semicolon);
-			}
-			break;
-		}
-	}
-
-	// ===============================================================================
-	// Script file ended. Do some last checks and write the last things to main buffer
-	if (m_current_mode != e_top_level_mode)
-		error ("script did not end at top level; a `}` is missing somewhere");
-
-	// stateSpawn must be defined!
-	if (!m_state_spawn_defined)
-		error ("script must have a state named `stateSpawn`!");
-
-	// Ensure no goto target is left undefined
-	if (m_undefined_labels.is_empty() == false)
-	{
-		string_list names;
-
-		for (undefined_label& undf : m_undefined_labels)
-			names << undf.name;
-
-		error ("labels `%1` are referenced via `goto` but are not defined\n", names);
-	}
-
-	// Dump the last state's onenter and mainloop
-	write_member_buffers();
-
-	// String table
-	write_string_table();
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_state_block()
-{
-	check_toplevel();
-	m_lx->must_get_next (tk_string);
-	string statename = token_string();
-
-	// State name must be a word.
-	if (statename.first (" ") != -1)
-		error ("state name must be a single word, got `%1`", statename);
-
-	// stateSpawn is special - it *must* be defined. If we
-	// encountered it, then mark down that we have it.
-	if (-statename == "statespawn")
-		m_state_spawn_defined = 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 (m_current_state.is_empty() == false)
-		write_member_buffers();
-
-	buffer()->write_dword (dh_state_name);
-	buffer()->write_string (statename);
-	buffer()->write_dword (dh_state_index);
-	buffer()->write_dword (m_num_states);
-
-	m_num_states++;
-	m_current_state = statename;
-	m_got_main_loop = false;
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_event_block()
-{
-	check_toplevel();
-	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);
-	m_current_mode = e_event_mode;
-	buffer()->write_dword (dh_event);
-	buffer()->write_dword (e->number);
-	m_num_events++;
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_mainloop()
-{
-	check_toplevel();
-	m_lx->must_get_next (tk_brace_start);
-
-	m_current_mode = e_main_loop_mode;
-	m_main_loop_buffer->write_dword (dh_main_loop);
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_on_enter_exit()
-{
-	check_toplevel();
-	bool onenter = (token_is (tk_onenter));
-	m_lx->must_get_next (tk_brace_start);
-
-	m_current_mode = onenter ? e_onenter_mode : e_onexit_mode;
-	buffer()->write_dword (onenter ? dh_on_enter : dh_on_exit);
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_variable_declaration()
-{
-	// For now, only globals are supported
-	if (m_current_mode != e_top_level_mode || m_current_state.is_empty() == false)
-		error ("variables must only be global for now");
-
-	type_e type =	(token_is (tk_int)) ? e_int_type :
-					(token_is (tk_str)) ? e_string_type :
-					e_bool_type;
-
-	m_lx->must_get_next();
-	string varname = token_string();
-
-	// Var name must not be a number
-	if (varname.is_numeric())
-		error ("variable name must not be a number");
-
-	script_variable* var = declare_global_variable (type, varname);
-	(void) var;
-	m_lx->must_get_next (tk_semicolon);
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_goto()
-{
-	check_not_toplevel();
-
-	// Get the name of the label
-	m_lx->must_get_next();
-
-	// Find the mark this goto statement points to
-	string target = token_string();
-	byte_mark* mark = buffer()->find_mark_by_name (target);
-
-	// If not set, define it
-	if (!mark)
-	{
-		undefined_label undf;
-		undf.name = target;
-		undf.target = buffer()->add_mark (target);
-		m_undefined_labels << undf;
-	}
-
-	// Add a reference to the mark.
-	buffer()->write_dword (dh_goto);
-	buffer()->add_reference (mark);
-	m_lx->must_get_next (tk_semicolon);
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_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 (e_int_type);
-	buffer()->merge_and_destroy (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.
-	byte_mark* mark = buffer()->add_mark ("");
-
-	// Use dh_if_not_goto - 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.
-	buffer()->write_dword (dh_if_not_goto);
-	buffer()->add_reference (mark);
-
-	// Store it
-	SCOPE (0).mark1 = mark;
-	SCOPE (0).type = e_if_scope;
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_else()
-{
-	check_not_toplevel();
-	m_lx->must_get_next (tk_brace_start);
-
-	// Don't use PushScope as it resets the scope
-	m_scope_cursor++;
-
-	if (m_scope_cursor >= MAX_SCOPE)
-		error ("too deep scope");
-
-	if (SCOPE (0).type != e_if_scope)
-		error ("else without preceding if");
-
-	// write down to jump to the end of the else statement
-	// Otherwise we have fall-throughs
-	SCOPE (0).mark2 = buffer()->add_mark ("");
-
-	// Instruction to jump to the end after if block is complete
-	buffer()->write_dword (dh_goto);
-	buffer()->add_reference (SCOPE (0).mark2);
-
-	// Move the ifnot mark here and set type to else
-	buffer()->adjust_mark (SCOPE (0).mark1);
-	SCOPE (0).type = e_else_scope;
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_while_block()
-{
-	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.
-	byte_mark* mark1 = buffer()->add_mark (""); // start
-	byte_mark* mark2 = buffer()->add_mark (""); // end
-
-	// Condition
-	m_lx->must_get_next (tk_paren_start);
-	m_lx->must_get_next();
-	data_buffer* expr = parse_expression (e_int_type);
-	m_lx->must_get_next (tk_paren_end);
-	m_lx->must_get_next (tk_brace_start);
-
-	// write condition
-	buffer()->merge_and_destroy (expr);
-
-	// Instruction to go to the end if it fails
-	buffer()->write_dword (dh_if_not_goto);
-	buffer()->add_reference (mark2);
-
-	// Store the needed stuff
-	SCOPE (0).mark1 = mark1;
-	SCOPE (0).mark2 = mark2;
-	SCOPE (0).type = e_while_scope;
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_for_block()
-{
-	check_not_toplevel();
-	push_scope();
-
-	// Initializer
-	m_lx->must_get_next (tk_paren_start);
-	m_lx->must_get_next();
-	data_buffer* init = parse_statement();
-
-	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 (e_int_type);
-
-	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();
-
-	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
-	buffer()->merge_and_destroy (init);
-
-	// Init two marks
-	byte_mark* mark1 = buffer()->add_mark ("");
-	byte_mark* mark2 = buffer()->add_mark ("");
-
-	// Add the condition
-	buffer()->merge_and_destroy (cond);
-	buffer()->write_dword (dh_if_not_goto);
-	buffer()->add_reference (mark2);
-
-	// Store the marks and incrementor
-	SCOPE (0).mark1 = mark1;
-	SCOPE (0).mark2 = mark2;
-	SCOPE (0).buffer1 = incr;
-	SCOPE (0).type = e_for_scope;
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_do_block()
-{
-	check_not_toplevel();
-	push_scope();
-	m_lx->must_get_next (tk_brace_start);
-	SCOPE (0).mark1 = buffer()->add_mark ("");
-	SCOPE (0).type = e_do_scope;
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_switch_block()
-{
-	// 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();
-	buffer()->merge_and_destroy (parse_expression (e_int_type));
-	m_lx->must_get_next (tk_paren_end);
-	m_lx->must_get_next (tk_brace_start);
-	SCOPE (0).type = e_switch_scope;
-	SCOPE (0).mark1 = buffer()->add_mark (""); // end mark
-	SCOPE (0).buffer1 = null; // default header
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_switch_case()
-{
-	// case is only allowed inside switch
-	if (SCOPE (0).type != e_switch_scope)
-		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.
-	//
-	// We null the switch buffer for the case-go-to statement as
-	// we want it all under the switch, not into the case-buffers.
-	m_switch_buffer = null;
-	buffer()->write_dword (dh_case_goto);
-	buffer()->write_dword (num);
-	add_switch_case (null);
-	SCOPE (0).casenumbers[SCOPE (0).casecursor] = num;
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_switch_default()
-{
-	if (SCOPE (0).type != e_switch_scope)
-		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_dword (dh_drop);
-	b->write_dword (dh_goto);
-	add_switch_case (b);
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_break()
-{
-	if (!m_scope_cursor)
-		error ("unexpected `break`");
-
-	buffer()->write_dword (dh_goto);
-
-	// switch and if use mark1 for the closing point,
-	// for and while use mark2.
-	switch (SCOPE (0).type)
-	{
-		case e_if_scope:
-		case e_switch_scope:
-		{
-			buffer()->add_reference (SCOPE (0).mark1);
-		} break;
-
-		case e_for_scope:
-		case e_while_scope:
-		{
-			buffer()->add_reference (SCOPE (0).mark2);
-		} break;
-
-		default:
-		{
-			error ("unexpected `break`");
-		} break;
-	}
-
-	m_lx->must_get_next (tk_semicolon);
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_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 = m_scope_cursor; curs > 0 && !found; curs--)
-	{
-		switch (m_scope_stack[curs].type)
-		{
-			case e_for_scope:
-			case e_while_scope:
-			case e_do_scope:
-			{
-				buffer()->write_dword (dh_goto);
-				buffer()->add_reference (m_scope_stack[curs].mark1);
-				found = true;
-			} break;
-
-			default:
-				break;
-		}
-	}
-
-	// No loop blocks
-	if (!found)
-		error ("`continue`-statement not inside a loop");
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_block_end()
-{
-	// Closing brace
-	// If we're in the block stack, we're descending down from it now
-	if (m_scope_cursor > 0)
-	{
-		switch (SCOPE (0).type)
-		{
-			case e_if_scope:
-				// Adjust the closing mark.
-				buffer()->adjust_mark (SCOPE (0).mark1);
-
-				// We're returning from if, thus else can be next
-				m_can_else = true;
-				break;
-
-			case e_else_scope:
-				// else instead uses mark1 for itself (so if expression
-				// fails, jump to else), mark2 means end of else
-				buffer()->adjust_mark (SCOPE (0).mark2);
-				break;
-
-			case e_for_scope:
-				// write the incrementor at the end of the loop block
-				buffer()->merge_and_destroy (SCOPE (0).buffer1);
-			case e_while_scope:
-				// write down the instruction to go back to the start of the loop
-				buffer()->write_dword (dh_goto);
-				buffer()->add_reference (SCOPE (0).mark1);
-
-				// Move the closing mark here since we're at the end of the while loop
-				buffer()->adjust_mark (SCOPE (0).mark2);
-				break;
-
-			case e_do_scope:
-			{
-				m_lx->must_get_next (tk_while);
-				m_lx->must_get_next (tk_paren_start);
-				m_lx->must_get_next();
-				data_buffer* expr = parse_expression (e_int_type);
-				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.
-				buffer()->merge_and_destroy (expr);
-				buffer()->write_dword (dh_if_goto);
-				buffer()->add_reference (SCOPE (0).mark1);
-				break;
-			}
-
-			case e_switch_scope:
-			{
-				// Switch closes. Move down to the record buffer of
-				// the lower block.
-				if (SCOPE (1).casecursor != -1)
-					m_switch_buffer = SCOPE (1).casebuffers[SCOPE (1).casecursor];
-				else
-					m_switch_buffer = 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)
-					buffer()->merge_and_destroy (SCOPE (0).buffer1);
-				else
-				{
-					buffer()->write_dword (dh_drop);
-					buffer()->write_dword (dh_goto);
-					buffer()->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;
-
-					buffer()->adjust_mark (SCOPE (0).casemarks[u]);
-					buffer()->merge_and_destroy (SCOPE (0).casebuffers[u]);
-				}
-
-				// Move the closing mark here
-				buffer()->adjust_mark (SCOPE (0).mark1);
-				break;
-			}
-
-			case e_unknown_scope:
-				break;
-		}
-
-		// Descend down the stack
-		m_scope_cursor--;
-		return;
-	}
-
-	int dataheader =	(m_current_mode == e_event_mode) ? dh_end_event :
-						(m_current_mode == e_main_loop_mode) ? dh_end_main_loop :
-						(m_current_mode == e_onenter_mode) ? dh_end_on_enter :
-						(m_current_mode == e_onexit_mode) ? dh_end_on_exit : -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.
-	buffer()->write_dword (dataheader);
-	m_current_mode = e_top_level_mode;
-	m_lx->get_next (tk_semicolon);
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_const()
-{
-	constant_info info;
-
-	// Get the type
-	m_lx->must_get_next();
-	string typestring = token_string();
-	info.type = get_type_by_name (typestring);
-
-	if (info.type == e_unknown_type || info.type == e_void_type)
-		error ("unknown type `%1` for constant", typestring);
-
-	m_lx->must_get_next();
-	info.name = token_string();
-
-	m_lx->must_get_next (tk_assign);
-
-	switch (info.type)
-	{
-		case e_bool_type:
-		case e_int_type:
-		{
-			m_lx->must_get_next (tk_number);
-		} break;
-
-		case e_string_type:
-		{
-			m_lx->must_get_next (tk_string);
-		} break;
-
-		case e_unknown_type:
-		case e_void_type:
-			break;
-	}
-
-	info.val = m_lx->get_token()->text;
-	m_constants << info;
-
-	m_lx->must_get_next (tk_semicolon);
-}
-
-// ============================================================================
-//
-void botscript_parser::parse_label()
-{
-	check_not_toplevel();
-	string label_name = token_string();
-	byte_mark* mark = null;
-
-	// want no conflicts..
-	if (find_command_by_name (label_name))
-		error ("label name `%1` conflicts with command name\n", label_name);
-
-	if (find_global_variable (label_name))
-		error ("label name `%1` conflicts with variable\n", label_name);
-
-	// See if a mark already exists for this label
-	for (undefined_label& undf : m_undefined_labels)
-	{
-		if (undf.name != label_name)
-			continue;
-
-		mark = undf.target;
-		buffer()->adjust_mark (mark);
-
-		// No longer undefined
-		m_undefined_labels.remove (undf);
-		break;
-	}
-
-	// Not found in unmarked lists, define it now
-	if (mark == null)
-		buffer()->add_mark (label_name);
-
-	m_lx->must_get_next (tk_colon);
-}
-
-// =============================================================================
-//
-void botscript_parser::parse_eventdef()
-{
-	event_info* e = new event_info;
-
-	m_lx->must_get_next (tk_number);
-	e->number = token_string().to_long();
-	m_lx->must_get_next (tk_colon);
-	m_lx->must_get_next (tk_symbol);
-	e->name = m_lx->get_token()->text;
-	m_lx->must_get_next (tk_paren_start);
-	m_lx->must_get_next (tk_paren_end);
-	m_lx->must_get_next (tk_semicolon);
-	add_event (e);
-}
-
-// =============================================================================
-//
-void botscript_parser::parse_funcdef()
-{
-	command_info* comm = new command_info;
-
-	// Number
-	m_lx->must_get_next (tk_number);
-	comm->number = m_lx->get_token()->text.to_long();
-
-	m_lx->must_get_next (tk_colon);
-
-	// Name
-	m_lx->must_get_next (tk_symbol);
-	comm->name = m_lx->get_token()->text;
-
-	m_lx->must_get_next (tk_colon);
-
-	// Return value
-	m_lx->must_get_any_of ({tk_int, tk_void, tk_bool, tk_str});
-	comm->returnvalue = get_type_by_name (m_lx->get_token()->text); // TODO
-	assert (comm->returnvalue != -1);
-
-	m_lx->must_get_next (tk_colon);
-
-	// Num args
-	m_lx->must_get_next (tk_number);
-	comm->numargs = m_lx->get_token()->text.to_long();
-
-	m_lx->must_get_next (tk_colon);
-
-	// Max args
-	m_lx->must_get_next (tk_number);
-	comm->maxargs = m_lx->get_token()->text.to_long();
-
-	// Argument types
-	int curarg = 0;
-
-	while (curarg < comm->maxargs)
-	{
-		command_argument arg;
-		m_lx->must_get_next (tk_colon);
-		m_lx->must_get_any_of ({tk_int, tk_bool, tk_str});
-		type_e type = get_type_by_name (m_lx->get_token()->text);
-		assert (type != -1 && type != e_void_type);
-		arg.type = type;
-
-		m_lx->must_get_next (tk_paren_start);
-		m_lx->must_get_next (tk_symbol);
-		arg.name = m_lx->get_token()->text;
-
-		// If this is an optional parameter, we need the default value.
-		if (curarg >= comm->numargs)
-		{
-			m_lx->must_get_next (tk_assign);
-
-			switch (type)
-			{
-				case e_int_type:
-				case e_bool_type:
-					m_lx->must_get_next (tk_number);
-					break;
-
-				case e_string_type:
-					m_lx->must_get_next (tk_string);
-					break;
-
-				case e_unknown_type:
-				case e_void_type:
-					break;
-			}
-
-			arg.defvalue = m_lx->get_token()->text.to_long();
-		}
-
-		m_lx->must_get_next (tk_paren_end);
-		comm->args << arg;
-		curarg++;
-	}
-
-	m_lx->must_get_next (tk_semicolon);
-	add_command_definition (comm);
-}
-
-// ============================================================================
-// Parses a command call
-data_buffer* botscript_parser::parse_command (command_info* comm)
-{
-	data_buffer* r = new data_buffer (64);
-
-	if (m_current_mode == e_top_level_mode)
-		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_is (tk_paren_end))
-		{
-			if (curarg < comm->numargs)
-				error ("too few arguments passed to %1\n\tprototype: %2",
-					comm->name, get_command_signature (comm));
-
-			break;
-			curarg++;
-		}
-
-		if (curarg >= comm->maxargs)
-			error ("too many arguments passed to %1\n\tprototype: %2",
-				comm->name, get_command_signature (comm));
-
-		r->merge_and_destroy (parse_expression (comm->args[curarg].type));
-		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_is (tk_paren_end))
-			{
-				curarg++;
-				break;
-			}
-			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_dword (dh_push_number);
-		r->write_dword (comm->args[curarg].defvalue);
-		curarg++;
-	}
-
-	r->write_dword (dh_command);
-	r->write_dword (comm->number);
-	r->write_dword (comm->maxargs);
-
-	return r;
-}
-
-// ============================================================================
-// Is the given operator an assignment operator?
-//
-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 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_add_global_var;
-			case OPER_ASSIGNSUB: return dh_subtract_global_var;
-			case OPER_ASSIGNMUL: return dh_multiply_global_var;
-			case OPER_ASSIGNDIV: return dh_divide_global_var;
-			case OPER_ASSIGNMOD: return dh_mod_global_var;
-			case OPER_ASSIGN: return dh_assign_global_var;
-
-			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;
-		case OPER_EQUALS: return dh_equals;
-		case OPER_NOTEQUALS: return dh_not_equals;
-		case OPER_LESSTHAN: return dh_less_than;
-		case OPER_GREATERTHAN: return dh_greater_than;
-		case OPER_LESSTHANEQUALS: return dh_at_most;
-		case OPER_GREATERTHANEQUALS: return dh_at_least;
-		case OPER_LEFTSHIFT: return dh_left_shift;
-		case OPER_RIGHTSHIFT: return dh_right_shift;
-		case OPER_OR: return dh_or_logical;
-		case OPER_AND: return dh_and_logical;
-		case OPER_BITWISEOR: return dh_or_bitwise;
-		case OPER_BITWISEEOR: return dh_eor_bitwise;
-		case OPER_BITWISEAND: return dh_and_bitwise;
-	}
-
-	error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper);
-	return 0;
-}
-
-// ============================================================================
-// Parses an expression, potentially recursively
-//
-data_buffer* botscript_parser::parse_expression (type_e reqtype)
-{
-	data_buffer* retbuf = new data_buffer (64);
-
-	// Parse first operand
-	retbuf->merge_and_destroy (parse_expr_value (reqtype));
-
-	// Parse any and all operators we get
-	int oper;
-
-	while ( (oper = parse_operator (true)) != -1)
-	{
-		// We peeked the operator, move forward now
-		m_lx->skip();
-
-		// Can't be an assignement operator, those belong in assignments.
-		if (is_assignment_operator (oper))
-			error ("assignment operator inside expression");
-
-		// Parse the right operand.
-		m_lx->must_get_next();
-		data_buffer* rb = parse_expr_value (reqtype);
-
-		if (oper == OPER_TERNARY)
-		{
-			// Ternary operator requires - naturally - a third operand.
-			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
-			byte_mark* mark1 = retbuf->add_mark (""); // start of "else" case
-			byte_mark* mark2 = retbuf->add_mark (""); // end of expression
-			retbuf->write_dword (dh_if_not_goto); // if the first operand (condition)
-			retbuf->add_reference (mark1); // didn't eval true, jump into mark1
-			retbuf->merge_and_destroy (rb); // otherwise, perform second operand (true case)
-			retbuf->write_dword (dh_goto); // afterwards, jump to the end, which is
-			retbuf->add_reference (mark2); // marked by mark2.
-			retbuf->adjust_mark (mark1); // move mark1 at the end of the true case
-			retbuf->merge_and_destroy (tb); // perform third operand (false case)
-			retbuf->adjust_mark (mark2); // move the ending mark2 here
-		}
-		else
-		{
-			// write to buffer
-			retbuf->merge_and_destroy (rb);
-			retbuf->write_dword (get_data_header_by_operator (null, oper));
-		}
-	}
-
-	return retbuf;
-}
-
-// ============================================================================
-// Parses an operator string. Returns the operator number code.
-//
-#define ISNEXT(C) (m_lx->peek_next_string (peek ? 1 : 0) == C)
-
-int botscript_parser::parse_operator (bool peek)
-{
-	string oper;
-
-	if (peek)
-		oper += m_lx->peek_next_string();
-	else
-		oper += token_string();
-
-	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)
-	{
-		return o;
-	}
-
-	// Two-char operators
-	oper += m_lx->peek_next_string (peek ? 1 : 0);
-	equalsnext = m_lx->peek_next_string (peek ? 2 : 1) == ("=");
-
-	o =	(oper == "+=") ? OPER_ASSIGNADD :
-		(oper == "-=") ? OPER_ASSIGNSUB :
-		(oper == "*=") ? OPER_ASSIGNMUL :
-		(oper == "/=") ? OPER_ASSIGNDIV :
-		(oper == "%=") ? OPER_ASSIGNMOD :
-		(oper == "==") ? OPER_EQUALS :
-		(oper == "!=") ? OPER_NOTEQUALS :
-		(oper == ">=") ? OPER_GREATERTHANEQUALS :
-		(oper == "<=") ? OPER_LESSTHANEQUALS :
-		(oper == "&&") ? OPER_AND :
-		(oper == "||") ? OPER_OR :
-		(oper == "<<" && !equalsnext) ? OPER_LEFTSHIFT :
-		(oper == ">>" && !equalsnext) ? OPER_RIGHTSHIFT :
-		-1;
-
-	if (o != -1)
-	{
-		m_lx->must_get_next();
-		return o;
-	}
-
-	// Three-char opers
-	oper += m_lx->peek_next_string (peek ? 2 : 1);
-	o =	oper == "<<=" ? OPER_ASSIGNLEFTSHIFT :
-		oper == ">>=" ? OPER_ASSIGNRIGHTSHIFT :
-		-1;
-
-	if (o != -1)
-	{
-		m_lx->must_get_next();
-		m_lx->must_get_next();
-	}
-
-	return o;
-}
-
-// ============================================================================
-//
-string botscript_parser::parse_float()
-{
-	m_lx->must_be (tk_number);
-	string floatstring = token_string();
-	lexer::token tok;
-
-	// Go after the decimal point
-	if (m_lx->peek_next (&tok) && tok.type == tk_dot)
-	{
-		m_lx->skip();
-		m_lx->must_get_next (tk_number);
-		floatstring += ".";
-		floatstring += token_string();
-	}
-
-	return floatstring;
-}
-
-// ============================================================================
-// 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.
-//
-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_is (tk_exclamation_mark);
-
-	if (negate) // Jump past the "!"
-		m_lx->skip();
-
-	// Handle strlen
-	/* if (token_string() == "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_string());
-
-		if (!constant || constant->type != TYPE_STRING)
-			error ("strlen only works with const str");
-
-		if (reqtype != TYPE_INT)
-			error ("strlen returns int but %1 is expected\n", GetTypeName (reqtype));
-
-		b->write_dword (dh_push_number);
-		b->write_dword (constant->val.len());
-
-		m_lx->must_get_next (tk_paren_end);
-	}
-	else */
-	if (token_is (tk_paren_start))
-	{
-		// Expression
-		m_lx->must_get_next();
-		data_buffer* c = parse_expression (reqtype);
-		b->merge_and_destroy (c);
-		m_lx->must_get_next (tk_paren_end);
-	}
-	else if (command_info* comm = find_command_by_name (token_string()))
-	{
-		delete b;
-
-		// Command
-		if (reqtype && comm->returnvalue != reqtype)
-			error ("%1 returns an incompatible data type", comm->name);
-
-		b = parse_command (comm);
-	}
-	else if (constant_info* constant = find_constant (token_string()))
-	{
-		// Type check
-		if (reqtype != constant->type)
-			error ("constant `%1` is %2, expression requires %3\n",
-				constant->name, get_type_name (constant->type),
-				get_type_name (reqtype));
-
-		switch (constant->type)
-		{
-			case e_bool_type:
-			case e_int_type:
-				b->write_dword (dh_push_number);
-				b->write_dword (atoi (constant->val));
-				break;
-
-			case e_string_type:
-				b->write_string_index (constant->val);
-				break;
-
-			case e_void_type:
-			case e_unknown_type:
-				break;
-		}
-	}
-	else if ((g = find_global_variable (token_string())))
-	{
-		// Global variable
-		b->write_dword (dh_push_global_var);
-		b->write_dword (g->index);
-	}
-	else
-	{
-		// If nothing else, check for literal
-		switch (reqtype)
-		{
-		case e_void_type:
-		case e_unknown_type:
-			error ("unknown identifier `%1` (expected keyword, function or variable)", token_string());
-			break;
-
-		case e_bool_type:
-		case e_int_type:
-		{
-			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_dword (dh_push_number);
-
-			long v = token_string().to_long();
-			b->write_dword (static_cast<word> (abs (v)));
-
-			if (v < 0)
-				b->write_dword (dh_unary_minus);
-
-			break;
-		}
-
-		case e_string_type:
-			// 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.
-			m_lx->must_be (tk_string);
-			b->write_string_index (token_string());
-			break;
-		}
-	}
-
-	// Negate it now if desired
-	if (negate)
-		b->write_dword (dh_negate_logical);
-
-	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.
-//
-data_buffer* botscript_parser::parse_assignment (script_variable* var)
-{
-	// Get an operator
-	m_lx->must_get_next();
-	int oper = parse_operator();
-
-	if (!is_assignment_operator (oper))
-		error ("expected assignment operator");
-
-	if (m_current_mode == e_top_level_mode)
-		error ("can't alter variables at top level");
-
-	// Parse the right operand
-	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_dword (var->is_global() ? dh_push_global_var : dh_push_local_var);
-		retbuf->write_dword (var->index);
-		retbuf->merge_and_destroy (expr);
-		retbuf->write_dword ((oper == OPER_ASSIGNLEFTSHIFT) ? dh_left_shift : dh_right_shift);
-		retbuf->write_dword (var->is_global() ? dh_assign_global_var : dh_assign_local_var);
-		retbuf->write_dword (var->index);
-	}
-	else
-	{
-		retbuf->merge_and_destroy (expr);
-		long dh = get_data_header_by_operator (var, oper);
-		retbuf->write_dword (dh);
-		retbuf->write_dword (var->index);
-	}
-
-	return retbuf;
-}
-
-// ============================================================================
-//
-void botscript_parser::push_scope()
-{
-	m_scope_cursor++;
-
-	if (m_scope_cursor >= MAX_SCOPE)
-		error ("too deep scope");
-
-	ScopeInfo* info = &SCOPE (0);
-	info->type = e_unknown_scope;
-	info->mark1 = null;
-	info->mark2 = null;
-	info->buffer1 = null;
-	info->casecursor = -1;
-
-	for (int i = 0; i < MAX_CASE; i++)
-	{
-		info->casemarks[i] = null;
-		info->casebuffers[i] = null;
-		info->casenumbers[i] = -1;
-	}
-}
-
-// ============================================================================
-//
-data_buffer* botscript_parser::parse_statement()
-{
-	if (find_constant (token_string())) // There should not be constants here.
-		error ("invalid use for constant\n");
-
-	// If it's a variable, expect assignment.
-	if (script_variable* var = find_global_variable (token_string()))
-		return parse_assignment (var);
-
-	return null;
-}
-
-// ============================================================================
-//
-void botscript_parser::add_switch_case (data_buffer* b)
-{
-	ScopeInfo* info = &SCOPE (0);
-
-	info->casecursor++;
-
-	if (info->casecursor >= MAX_CASE)
-		error ("too many cases in one switch");
-
-	// Init a mark for the case buffer
-	byte_mark* casemark = buffer()->add_mark ("");
-	info->casemarks[info->casecursor] = casemark;
-
-	// Add a reference to the mark. "case" and "default" both
-	// add the necessary bytecode before the reference.
-	if (b)
-		b->add_reference (casemark);
-	else
-		buffer()->add_reference (casemark);
-
-	// Init a buffer for the case block and tell the object
-	// writer to record all written data to it.
-	info->casebuffers[info->casecursor] = m_switch_buffer = new data_buffer;
-}
-
-// ============================================================================
-//
-constant_info* botscript_parser::find_constant (const string& tok)
-{
-	for (int i = 0; i < m_constants.size(); i++)
-		if (m_constants[i].name == tok)
-			return &m_constants[i];
-
-	return null;
-}
-
-// ============================================================================
-//
-bool botscript_parser::token_is (e_token a)
-{
-	return (m_lx->get_token_type() == 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);
-}
-
-// ============================================================================
-//
-data_buffer* botscript_parser::buffer()
-{
-	if (m_switch_buffer != null)
-		return m_switch_buffer;
-
-	if (m_current_mode == e_main_loop_mode)
-		return m_main_loop_buffer;
-
-	if (m_current_mode == e_onenter_mode)
-		return m_on_enter_buffer;
-
-	return m_main_buffer;
-}
-
-// ============================================================================
-//
-void botscript_parser::write_member_buffers()
-{
-	// If there was no mainloop defined, write a dummy one now.
-	if (!m_got_main_loop)
-	{
-		m_main_loop_buffer->write_dword (dh_main_loop);
-		m_main_loop_buffer->write_dword (dh_end_main_loop);
-	}
-
-	// Write the onenter and mainloop buffers, in that order in particular.
-	for (data_buffer** bufp : list<data_buffer**> ({&m_on_enter_buffer, &m_main_loop_buffer}))
-	{
-		buffer()->merge_and_destroy (*bufp);
-
-		// Clear the buffer afterwards for potential next state
-		*bufp = new data_buffer;
-	}
-
-	// Next state definitely has no mainloop yet
-	m_got_main_loop = false;
-}
-
-// ============================================================================
-//
-// Write string table
-//
-void botscript_parser::write_string_table()
-{
-	int stringcount = num_strings_in_table();
-
-	if (!stringcount)
-		return;
-
-	// Write header
-	m_main_buffer->write_dword (dh_string_list);
-	m_main_buffer->write_dword (stringcount);
-
-	// Write all strings
-	for (int i = 0; i < stringcount; i++)
-		m_main_buffer->write_string (get_string_table()[i]);
-}
-
-// ============================================================================
-//
-// Write the compiled bytecode to a file
-//
-void botscript_parser::write_to_file (string outfile)
-{
-	FILE* fp = fopen (outfile, "w");
-
-	if (fp == null)
-		error ("couldn't open %1 for writing: %2", outfile, strerror (errno));
-
-	// First, resolve references
-	for (mark_reference* ref : m_main_buffer->get_refs())
-	{
-		// Substitute the placeholder with the mark position
-		for (int v = 0; v < 4; v++)
-			m_main_buffer->get_buffer()[ref->pos + v] = ((ref->target->pos) << (8 * v)) & 0xFF;
-
-		print ("reference at %1 resolved to mark at %2\n", ref->pos, ref->target->pos);
-	}
-
-	// Then, dump the main buffer to the file
-	fwrite (m_main_buffer->get_buffer(), 1, m_main_buffer->get_write_size(), fp);
-	print ("-- %1 byte%s1 written to %2\n", m_main_buffer->get_write_size(), outfile);
-	fclose (fp);
-}
\ No newline at end of file
--- a/src/parser.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,260 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_PARSER_H
-#define BOTC_PARSER_H
-
-#include <stdio.h>
-#include "main.h"
-#include "commands.h"
-#include "lexer_scanner.h"
-#include "tokens.h"
-
-#define MAX_SCOPE 32
-#define MAX_CASE 64
-#define MAX_MARKS 512 // TODO: get rid of this
-
-class data_buffer;
-class lexer;
-class script_variable;
-
-struct undefined_label
-{
-	string		name;
-	byte_mark*	target;
-};
-
-// ============================================================================
-// Operators
-//
-enum operator_e
-{
-	OPER_ADD,
-	OPER_SUBTRACT,
-	OPER_MULTIPLY,
-	OPER_DIVIDE,
-	OPER_MODULUS,
-	OPER_ASSIGN,
-	OPER_ASSIGNADD,
-	OPER_ASSIGNSUB,
-	OPER_ASSIGNMUL,
-	OPER_ASSIGNDIV,
-	OPER_ASSIGNMOD, // -- 10
-	OPER_EQUALS,
-	OPER_NOTEQUALS,
-	OPER_LESSTHAN,
-	OPER_GREATERTHAN,
-	OPER_LESSTHANEQUALS,
-	OPER_GREATERTHANEQUALS,
-	OPER_LEFTSHIFT,
-	OPER_RIGHTSHIFT,
-	OPER_ASSIGNLEFTSHIFT,
-	OPER_ASSIGNRIGHTSHIFT, // -- 20
-	OPER_OR,
-	OPER_AND,
-	OPER_BITWISEOR,
-	OPER_BITWISEAND,
-	OPER_BITWISEEOR,
-	OPER_TERNARY,
-	OPER_STRLEN,
-};
-
-// ============================================================================
-//
-struct operator_info
-{
-	operator_e		opercode;
-	e_data_header	dataheader;
-	e_token			token;
-};
-
-// ============================================================================
-// Mark types
-//
-enum marktype_e
-{
-	e_label_mark,
-	e_if_mark,
-	e_internal_mark, // internal structures
-};
-
-// ============================================================================
-// Scope types
-//
-enum scopetype_e
-{
-	e_unknown_scope,
-	e_if_scope,
-	e_while_scope,
-	e_for_scope,
-	e_do_scope,
-	e_switch_scope,
-	e_else_scope,
-};
-
-// ============================================================================
-// Meta-data about scopes
-//
-struct ScopeInfo
-{
-	byte_mark*		mark1;
-	byte_mark*		mark2;
-	scopetype_e		type;
-	data_buffer*	buffer1;
-
-	// switch-related stuff
-	// Which case are we at?
-	int				casecursor;
-
-	// Marks to case-blocks
-	byte_mark*		casemarks[MAX_CASE];
-
-	// Numbers of the case labels
-	int				casenumbers[MAX_CASE];
-
-	// actual case blocks
-	data_buffer*	casebuffers[MAX_CASE];
-
-	// What is the current buffer of the block?
-	data_buffer*	recordbuffer;
-};
-
-// ============================================================================
-//
-struct constant_info
-{
-	string name;
-	type_e type;
-	string val;
-};
-
-// ============================================================================
-//
-class botscript_parser
-{
-	public:
-		// ====================================================================
-		// METHODS
-		botscript_parser();
-		~botscript_parser();
-		void			parse_botscript (string file_name);
-		data_buffer*	parse_command (command_info* comm);
-		data_buffer*	parse_expression (type_e reqtype);
-		data_buffer*	parse_assignment (script_variable* var);
-		int				parse_operator (bool peek = false);
-		data_buffer*	parse_expr_value (type_e reqtype);
-		string			parse_float();
-		void			push_scope();
-		data_buffer*	parse_statement();
-		void			add_switch_case (data_buffer* b);
-		void			check_toplevel();
-		void			check_not_toplevel();
-		bool			token_is (e_token a);
-		string			token_string();
-		string			describe_position() const;
-		void			write_to_file (string outfile);
-
-		inline int get_num_events() const
-		{
-			return m_num_events;
-		}
-
-		inline int get_num_states() const
-		{
-			return m_num_states;
-		}
-
-	private:
-		// The lexer we're using.
-		lexer*					m_lx;
-
-		// The main buffer - the contents of this is what we
-		// write to file after parsing is complete
-		data_buffer*			m_main_buffer;
-
-		// onenter buffer - the contents of the onenter{} block
-		// is buffered here and is merged further at the end of state
-		data_buffer*			m_on_enter_buffer;
-
-		// Mainloop buffer - the contents of the mainloop{} block
-		// is buffered here and is merged further at the end of state
-		data_buffer*			m_main_loop_buffer;
-
-		// Switch buffer - switch case data is recorded to this
-		// buffer initially, instead of into main buffer.
-		data_buffer*			m_switch_buffer;
-
-		int						m_num_states;
-		int						m_num_events;
-		parsermode_e			m_current_mode;
-		string					m_current_state;
-		bool					m_state_spawn_defined;
-		bool					m_got_main_loop;
-		int						m_scope_cursor;
-		data_buffer*			m_if_expression;
-		bool					m_can_else;
-		list<undefined_label>	m_undefined_labels;
-		list<constant_info>		m_constants;
-
-		// How many bytes have we written to file?
-		int				m_num_written_bytes;
-
-		// Scope data
-		// TODO: make a list
-		ScopeInfo		m_scope_stack[MAX_SCOPE];
-
-		data_buffer*	buffer();
-		constant_info*	find_constant (const string& tok);
-		void			parse_state_block();
-		void			parse_event_block();
-		void			parse_mainloop();
-		void			parse_on_enter_exit();
-		void			parse_variable_declaration();
-		void			parse_goto();
-		void			parse_if();
-		void			parse_else();
-		void			parse_while_block();
-		void			parse_for_block();
-		void			parse_do_block();
-		void			parse_switch_block();
-		void			parse_switch_case();
-		void			parse_switch_default();
-		void			parse_break();
-		void			parse_continue();
-		void			parse_block_end();
-		void			parse_const();
-		void			parse_label();
-		void			parse_eventdef();
-		void			parse_funcdef();
-		void			write_member_buffers();
-		void			write_string_table();
-};
-
-constant_info* find_constant (const string& tok);
-
-#endif // BOTC_PARSER_H
--- a/src/str.cc	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,509 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <cstring>
-#include "main.h"
-#include "str.h"
-
-// =============================================================================
-//
-int string::compare (const string& other) const
-{
-    return m_string.compare (other.std_string());
-}
-
-// =============================================================================
-//
-void string::trim (string::length_type n)
-{
-    if (n > 0)
-        m_string = substring (0, length() - n).std_string();
-    else
-        m_string = substring (n, -1).std_string();
-}
-
-// =============================================================================
-//
-string string::strip (list<char> unwanted)
-{
-    string copy (m_string);
-
-    for (char c : unwanted)
-        for (int i = 0; i < copy.length(); ++i)
-            if (copy[i] == c)
-                copy.erase (i);
-
-    /*
-    while(( pos = copy.first( c )) != -1 )
-    	copy.erase( pos );
-    */
-
-    return copy;
-}
-
-// =============================================================================
-//
-string string::to_uppercase() const
-{
-    string newstr = m_string;
-
-    for (char& c : newstr)
-        if (c >= 'a' && c <= 'z')
-            c -= 'a' - 'A';
-
-    return newstr;
-}
-
-// =============================================================================
-//
-string string::to_lowercase() const
-{
-    string newstr = m_string;
-
-    for (char & c : newstr)
-        if (c >= 'A' && c <= 'Z')
-            c += 'a' - 'A';
-
-    return newstr;
-}
-
-// =============================================================================
-//
-string_list string::split (char del) const
-{
-    string delimstr;
-    delimstr += del;
-    return split (delimstr);
-}
-
-// =============================================================================
-//
-string_list string::split (string del) const
-{
-    string_list res;
-    long a = 0;
-
-    // Find all separators and store the text left to them.
-    for (;;)
-    {
-        long b = first (del, a);
-
-        if (b == -1)
-            break;
-
-        string sub = substring (a, b);
-
-        if (sub.length() > 0)
-            res.push_back (substring (a, b));
-
-        a = b + strlen (del);
-    }
-
-    // Add the string at the right of the last separator
-    if (a < (int) length())
-        res.push_back (substring (a, length()));
-
-    return res;
-}
-
-// =============================================================================
-//
-void string::replace (const char* a, const char* b)
-{
-    long pos;
-
-    while ( (pos = first (a)) != -1)
-        m_string = m_string.replace (pos, strlen (a), b);
-}
-
-// =============================================================================
-//
-int string::count (const char needle) const
-{
-    int needles = 0;
-
-    for (const char & c : m_string)
-        if (c == needle)
-            needles++;
-
-    return needles;
-}
-
-// =============================================================================
-//
-string string::substring (long a, long b) const
-{
-    if (b == -1)
-        b = length();
-
-    if (b == a)
-        return "";
-
-    if (b < a)
-    {
-        // Swap the variables
-        int c = a;
-        a = b;
-        b = c;
-    }
-
-    char* newstr = new char[b - a + 1];
-    strncpy (newstr, m_string.c_str() + a, b - a);
-    newstr[b - a] = '\0';
-
-    string other (newstr);
-    delete[] newstr;
-    return other;
-}
-
-// =============================================================================
-//
-string::length_type string::posof (int n) const
-{
-    int count = 0;
-
-    for (int i = 0; i < length(); ++i)
-    {
-        if (m_string[i] != ' ')
-            continue;
-
-        if (++count < n)
-            continue;
-
-        return i;
-    }
-
-    return -1;
-}
-
-// =============================================================================
-//
-int string::first (const char* c, string::length_type a) const
-{
-    for (; a < length(); a++)
-        if (m_string[a] == c[0] && strncmp (m_string.c_str() + a, c, strlen (c)) == 0)
-            return a;
-
-    return -1;
-}
-
-// =============================================================================
-//
-int string::last (const char* c, string::length_type a) const
-{
-    if (a == -1 || a >= length())
-        a = length() - 1;
-
-    for (; a > 0; a--)
-        if (m_string[a] == c[0] && strncmp (m_string.c_str() + a, c, strlen (c)) == 0)
-            return a;
-
-    return -1;
-}
-
-// =============================================================================
-//
-void string::dump() const
-{
-    print ("`%1`:\n", chars());
-    int i = 0;
-
-    for (char u : m_string)
-        print ("\t%1. [%d2] `%3`\n", i++, u, string (u));
-}
-
-// =============================================================================
-//
-long string::to_long (bool* ok, int base) const
-{
-    errno = 0;
-    char* endptr;
-    long i = strtol (m_string.c_str(), &endptr, base);
-
-	if (ok)
-		*ok = (errno == 0 && *endptr == '\0');
-
-    return i;
-}
-
-// =============================================================================
-//
-float string::to_float (bool* ok) const
-{
-    errno = 0;
-    char* endptr;
-    float i = strtof (m_string.c_str(), &endptr);
-
-	if (ok)
-		*ok = (errno == 0 && *endptr == '\0');
-
-    return i;
-}
-
-// =============================================================================
-//
-double string::to_double (bool* ok) const
-{
-    errno = 0;
-    char* endptr;
-    double i = strtod (m_string.c_str(), &endptr);
-
-	if (ok)
-		*ok = (errno == 0 && *endptr == '\0');
-
-    return i;
-}
-
-// =============================================================================
-//
-bool operator== (const char* a, const string& b)
-{
-    return b == a;
-}
-
-// =============================================================================
-//
-string operator+ (const char* a, const string& b)
-{
-    return string (a) + b;
-}
-
-// =============================================================================
-//
-string string::operator+ (const string& data) const
-{
-    string newString = *this;
-    newString += data;
-    return newString;
-}
-
-// =============================================================================
-//
-string string::operator+ (const char* data) const
-{
-    string newstr = *this;
-    newstr += data;
-    return newstr;
-}
-
-// =============================================================================
-//
-string string::operator+ (int num) const
-{
-	string newstr = *this;
-	string numstr;
-	numstr.sprintf ("%d", num);
-	newstr += numstr;
-	return newstr;
-}
-
-// =============================================================================
-//
-string& string::operator+= (const string data)
-{
-    append (data);
-    return *this;
-}
-
-// =============================================================================
-//
-string& string::operator+= (const char* data)
-{
-    append (data);
-    return *this;
-}
-
-// =============================================================================
-//
-string& string::operator+= (int num)
-{
-	string numstr;
-	numstr.sprintf ("%d", num);
-	return operator+= (numstr);
-}
-
-// =============================================================================
-//
-bool string::is_numeric() const
-{
-    bool gotDot = false;
-
-    for (const char & c : m_string)
-    {
-        // Allow leading hyphen for negatives
-        if (&c == &m_string[0] && c == '-')
-            continue;
-
-        // Check for decimal point
-        if (!gotDot && c == '.')
-        {
-            gotDot = true;
-            continue;
-        }
-
-        if (c >= '0' && c <= '9')
-            continue; // Digit
-
-        // If the above cases didn't catch this character, it was
-        // illegal and this is therefore not a number.
-        return false;
-    }
-
-    return true;
-}
-
-// =============================================================================
-//
-bool string::ends_with (const string& other)
-{
-    if (length() < other.length())
-        return false;
-
-    const int ofs = length() - other.length();
-    return strncmp (chars() + ofs, other.chars(), other.length()) == 0;
-}
-
-// =============================================================================
-//
-bool string::starts_with (const string& other)
-{
-    if (length() < other.length())
-        return false;
-
-    return strncmp (chars(), other.chars(), other.length()) == 0;
-}
-
-// =============================================================================
-//
-void string::sprintf (const char* fmtstr, ...)
-{
-    char* buf;
-    int bufsize = 256;
-    va_list va;
-    va_start (va, fmtstr);
-
-    do
-        buf = new char[bufsize];
-
-    while (vsnprintf (buf, bufsize, fmtstr, va) >= bufsize);
-
-    va_end (va);
-    m_string = buf;
-    delete[] buf;
-}
-
-// =============================================================================
-//
-void string::prepend (string a)
-{
-    m_string = (a + m_string).std_string();
-}
-
-// =============================================================================
-//
-string string_list::join (const string& delim)
-{
-    string result;
-
-    for (const string & it : std_deque())
-    {
-        if (!result.is_empty())
-            result += delim;
-
-        result += it;
-    }
-
-    return result;
-}
-
-// =============================================================================
-//
-bool string::mask (const string& pattern) const
-{
-    // Elevate to uppercase for case-insensitive matching
-    string pattern_upper = pattern.to_uppercase();
-    string this_upper = to_uppercase();
-    const char* maskstring = pattern_upper.chars();
-    const char* mptr = &maskstring[0];
-
-    for (const char* sptr = this_upper.chars(); *sptr != '\0'; sptr++)
-    {
-        if (*mptr == '?')
-        {
-            if (* (sptr + 1) == '\0')
-            {
-                // ? demands that there's a character here and there wasn't.
-                // Therefore, mask matching fails
-                return false;
-            }
-        }
-
-        elif (*mptr == '*')
-        {
-            char end = * (++mptr);
-
-            // If '*' is the final character of the message, all of the remaining
-            // string matches against the '*'. We don't need to bother checking
-            // the string any further.
-            if (end == '\0')
-                return true;
-
-            // Skip to the end character
-            while (*sptr != end && *sptr != '\0')
-                sptr++;
-
-            // String ended while the mask still had stuff
-            if (*sptr == '\0')
-                return false;
-        }
-        elif (*sptr != *mptr)
-        return false;
-
-        mptr++;
-    }
-
-    return true;
-}
-
-// =============================================================================
-//
-string string::from_number (int a)
-{
-	char buf[32];
-	::sprintf (buf, "%d", a);
-	return string (buf);
-}
-
-// =============================================================================
-//
-string string::from_number (long a)
-{
-	char buf[32];
-	::sprintf (buf, "%ld", a);
-	return string (buf);
-}
\ No newline at end of file
--- a/src/str.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,329 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_STRING_H
-#define BOTC_STRING_H
-
-#include <deque>
-#include <string>
-#include <stdarg.h>
-#include "types.h"
-#include "containers.h"
-
-class string;
-class string_list;
-
-// =============================================================================
-//
-class string
-{
-	public:
-		typedef typename ::std::string::iterator       iterator;
-		typedef typename ::std::string::const_iterator const_iterator;
-		using length_type = int;
-
-		string() {}
-
-		explicit string (char a)
-		{
-			m_string = &a;
-		}
-
-		string (const char* data)
-		{
-			m_string = data;
-		}
-
-		string (std::string data)
-		{
-			m_string = data;
-		}
-
-		void               dump() const;
-		int                compare (const string& other) const;
-		bool               ends_with (const string& other);
-		int                count (const char needle) const;
-		int                first (const char* c, length_type a = 0) const;
-		int                last (const char* c, length_type a = -1) const;
-		string             to_lowercase() const;
-		bool               is_numeric() const;
-		bool				mask (const string& pattern) const;
-		length_type        posof (int n) const;
-		void               prepend (string a);
-		void               replace (const char* a, const char* b);
-		string_list        split (string del) const;
-		string_list        split (char del) const;
-		void               sprintf (const char* fmtstr, ...);
-		bool               starts_with (const string& other);
-		string             strip (list< char > unwanted);
-		string             substring (long a, long b = -1) const;
-		double             to_double (bool* ok = nullptr) const;
-		float              to_float (bool* ok = nullptr) const;
-		long               to_long (bool* ok = nullptr, int base = 10) const;
-		void               trim (length_type n);
-		string             to_uppercase() const;
-
-		string             operator+ (const string& data) const;
-		string             operator+ (const char* data) const;
-		string             operator+ (int num) const;
-		string&            operator+= (const string data);
-		string&            operator+= (const char* data);
-		string&            operator+= (int num);
-
-		static string		from_number (int a);
-		static string		from_number (long a);
-
-		inline bool is_empty() const
-		{
-			return m_string[0] == '\0';
-		}
-
-		inline void append (const char* data)
-		{
-			m_string.append (data);
-		}
-
-		inline void append (const char data)
-		{
-			m_string.push_back (data);
-		}
-
-		inline void append (const string& data)
-		{
-			m_string.append (data.chars());
-		}
-
-		inline iterator begin()
-		{
-			return m_string.begin();
-		}
-
-		inline const_iterator begin() const
-		{
-			return m_string.cbegin();
-		}
-
-		inline int capacity() const
-		{
-			return m_string.capacity();
-		}
-
-		inline const char* chars() const
-		{
-			return m_string.c_str();
-		}
-
-		inline const char* c_str() const
-		{
-			return m_string.c_str();
-		}
-
-		inline iterator end()
-		{
-			return m_string.end();
-		}
-
-		inline const_iterator end() const
-		{
-			return m_string.end();
-		}
-
-		inline void clear()
-		{
-			m_string.clear();
-		}
-
-		inline bool empty() const
-		{
-			return m_string.empty();
-		}
-
-		inline void erase (length_type pos)
-		{
-			m_string.erase (m_string.begin() + pos);
-		}
-
-		inline void insert (length_type pos, char c)
-		{
-			m_string.insert (m_string.begin() + pos, c);
-		}
-
-		inline length_type length() const
-		{
-			return m_string.length();
-		}
-
-		inline int max_size() const
-		{
-			return m_string.max_size();
-		}
-
-		inline string mid (int a, int len) const
-		{
-			return m_string.substr (a, len);
-		}
-
-		inline void remove (int pos, int len)
-		{
-			m_string.replace (pos, len, "");
-		}
-
-		inline void remove_from_start (int len)
-		{
-			remove (0, len);
-		}
-
-		inline void remove_from_end (int len)
-		{
-			remove (length() - len, len);
-		}
-
-		inline void resize (int n)
-		{
-			m_string.resize (n);
-		}
-
-		inline void replace (int pos, int n, const string& a)
-		{
-			m_string.replace (pos, n, a.chars());
-		}
-
-		inline void shrink_to_fit()
-		{
-			m_string.shrink_to_fit();
-		}
-
-		inline const std::string& std_string() const
-		{
-			return m_string;
-		}
-
-		inline string strip (char unwanted)
-		{
-			return strip ( {unwanted});
-		}
-
-		string& operator+= (const char data)
-		{
-			append (data);
-			return *this;
-		}
-
-		string operator- (int n) const
-		{
-			string new_string = m_string;
-			new_string -= n;
-			return new_string;
-		}
-
-		inline string& operator-= (int n)
-		{
-			trim (n);
-			return *this;
-		}
-
-		inline string operator+() const
-		{
-			return to_uppercase();
-		}
-
-		inline string operator-() const
-		{
-			return to_lowercase();
-		}
-
-		inline bool operator== (const string& other) const
-		{
-			return std_string() == other.std_string();
-		}
-
-		inline bool operator== (const char* other) const
-		{
-			return operator== (string (other));
-		}
-
-		inline bool operator!= (const string& other) const
-		{
-			return std_string() != other.std_string();
-		}
-
-		inline bool operator!= (const char* other) const
-		{
-			return operator!= (string (other));
-		}
-
-		inline bool operator> (const string& other) const
-		{
-			return std_string() > other.std_string();
-		}
-
-		inline bool operator< (const string& other) const
-		{
-			return std_string() < other.std_string();
-		}
-
-		inline operator const char*() const
-		{
-			return chars();
-		}
-
-		inline operator const std::string&() const
-		{
-			return std_string();
-		}
-
-		// Difference between indices @a and @b. @b can be -1, in which
-		// case it will be length() - 1.
-		inline int index_difference (int a, int b)
-		{
-			assert (b == -1 || b >= a);
-			return (b != -1 ? b - a : length() - 1 - a);
-		}
-
-	private:
-		std::string m_string;
-};
-
-// =============================================================================
-//
-class string_list : public list<string>
-{
-	public:
-		string_list() {}
-		string_list (std::initializer_list<string> vals) :
-			list<string> (vals) {}
-		string_list (const list<string>& a) : list<string> (a.std_deque()) {}
-		string_list (const list_type& a) : list<string> (a) {}
-
-		string join (const string& delim);
-};
-
-bool operator== (const char* a, const string& b);
-string operator+ (const char* a, const string& b);
-
-#endif // BOTC_STRING_H
--- a/src/stringtable.cc	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "stringtable.h"
-
-static string_list g_string_table;
-
-// ============================================================================
-//
-const string_list& get_string_table()
-{
-	return g_string_table;
-}
-
-// ============================================================================
-// Potentially adds a string to the table and returns the index of it.
-//
-int get_string_table_index (const string& a)
-{
-	// Must not be too long.
-	if (a.length() >= g_max_string_length)
-		error ("string `%1` too long (%2 characters, max is %3)\n",
-			   a, a.length(), g_max_string_length);
-
-	// Find a free slot in the table.
-	int idx;
-
-	for (idx = 0; idx < g_string_table.size(); idx++)
-	{
-		// String is already in the table, thus return it.
-		if (g_string_table[idx] == a)
-			return idx;
-	}
-
-	// Check if the table is already full
-	if (g_string_table.size() == g_max_stringlist_size - 1)
-		error ("too many strings!\n");
-
-	// Now, dump the string into the slot
-	g_string_table.push_back (a);
-	return (g_string_table.size() - 1);
-}
-
-// ============================================================================
-// Counts the amount of strings in the table.
-//
-int num_strings_in_table()
-{
-	return g_string_table.size();
-}
--- a/src/stringtable.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_STRINGTABLE_H
-#define BOTC_STRINGTABLE_H
-
-#include "main.h"
-
-int get_string_table_index (const string& a);
-const string_list& get_string_table();
-int num_strings_in_table();
-
-#endif // BOTC_STRINGTABLE_H
--- a/src/tokens.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef TOKENS_H
-#define TOKENS_H
-
-#include <climits>
-
-// =======================================================
-enum e_token
-{
-	// Non-word tokens
-	tk_equals,				// ----- 0
-	tk_brackets,			// - 1
-	tk_add_assign,			// - 2
-	tk_sub_assign,			// - 3
-	tk_multiply_assign,		// - 4
-	tk_divide_assign,		// ----- 5
-	tk_modulus_assign,		// - 6
-	tk_single_quote,		// - 7
-	tk_dollar_sign,			// - 8
-	tk_paren_start,			// - 9
-	tk_paren_end,			// ----- 10
-	tk_bracket_start,		// - 11
-	tk_bracket_end,			// - 12
-	tk_brace_start,			// - 13
-	tk_brace_end,			// - 14
-	tk_assign,				// ----- 15
-	tk_plus,				// - 16
-	tk_minus,				// - 17
-	tk_multiply,			// - 18
-	tk_divide,				// - 19
-	tk_modulus,				// ----- 20
-	tk_comma,				// - 21
-	tk_lesser,				// - 22
-	tk_greater,				// - 23
-	tk_dot,					// - 24
-	tk_colon,				// ----- 25
-	tk_semicolon,			// - 26
-	tk_hash,				// - 27
-	tk_exclamation_mark,	// - 28
-	tk_arrow,				// - 29
-
-	// --------------
-	// Named tokens
-	tk_bool,				// ----- 30
-	tk_break,				// - 31
-	tk_case,				// - 32
-	tk_continue,			// - 33
-	tk_const,				// - 34
-	tk_default,				// ----- 35
-	tk_do,					// - 36
-	tk_else,				// - 37
-	tk_event,				// - 38
-	tk_eventdef,			// - 39
-	tk_for,					// ----- 40
-	tk_funcdef,				// - 41
-	tk_goto,				// - 42
-	tk_if,					// - 43
-	tk_int,					// - 44
-	tk_mainloop,			// ----- 45
-	tk_onenter,				// - 46
-	tk_onexit,				// - 47
-	tk_state,				// - 48
-	tk_switch,				// - 49
-	tk_str,					// ----- 50
-	tk_void,				// - 51
-	tk_while,				// - 52
-
-	// 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
-	tk_enum,				// - 53
-	tk_func,				// - 54
-	tk_return,				// ----- 55
-
-	// --------------
-	// Generic tokens
-	tk_symbol,				// - 56
-	tk_number,				// - 57
-	tk_string,				// - 58
-
-	tk_first_named_token	= tk_bool,
-	tk_last_named_token		= (int) tk_symbol - 1,
-	tk_any					= INT_MAX
-};
-
-#endif
\ No newline at end of file
--- a/src/types.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_TYPES_H
-#define BOTC_TYPES_H
-
-#include <cstdlib>
-#include <stdexcept>
-#include "str.h"
-
-static const std::nullptr_t null = nullptr;
-
-// Byte datatype
-typedef int32_t word;
-typedef unsigned char byte;
-
-// Parser mode: where is the parser at?
-enum parsermode_e
-{
-	e_top_level_mode,	// at top level
-	e_event_mode,		// inside event definition
-	e_main_loop_mode,	// inside mainloop
-	e_onenter_mode,		// inside onenter
-	e_onexit_mode,		// inside onexit
-};
-
-enum type_e
-{
-	e_unknown_type = 0,
-	e_void_type,
-	e_int_type,
-	e_string_type,
-	e_bool_type,
-};
-
-// Script mark and reference
-struct byte_mark
-{
-	string		name;
-	int			pos;
-};
-
-struct mark_reference
-{
-	byte_mark*	target;
-	int			pos;
-};
-
-class script_error : public std::exception
-{
-	public:
-		script_error (const string& msg) : m_msg (msg) {}
-
-		inline const char* what() const throw()
-		{
-			return m_msg.c_str();
-		}
-
-	private:
-		string m_msg;
-};
-
-// ====================================================================
-// Generic union
-template <class T> union generic_union
-{
-	T as_t;
-	byte as_bytes[sizeof (T)];
-	char as_chars[sizeof (T)];
-	double as_double;
-	float as_float;
-	int as_int;
-	word as_word;
-};
-
-template<class T> inline T abs (T a)
-{
-	return (a >= 0) ? a : -a;
-}
-
-#ifdef IN_IDE_PARSER
-using FILE = void;
-#endif
-
-#endif // BOTC_TYPES_H
\ No newline at end of file
--- a/src/variables.cc	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "stringtable.h"
-#include "variables.h"
-#include "parser.h"
-
-list<script_variable> g_GlobalVariables;
-list<script_variable> g_LocalVariables;
-
-// ============================================================================
-// Tries to declare a new global-scope variable. Returns pointer
-// to new global variable, null if declaration failed.
-script_variable* declare_global_variable (type_e type, string name)
-{
-	// Unfortunately the VM does not support string variables so yeah.
-	if (type == e_string_type)
-		error ("variables cannot be string\n");
-
-	// Check that the variable is valid
-	if (find_command_by_name (name))
-		error ("name of variable-to-be `%s` conflicts with that of a command", name.chars());
-
-	if (g_GlobalVariables.size() >= g_max_global_vars)
-		error ("too many global variables!");
-
-	for (int i = 0; i < g_GlobalVariables.size(); i++)
-		if (g_GlobalVariables[i].name == name)
-			error ("attempted redeclaration of global variable `%s`", name.chars());
-
-	script_variable g;
-	g.index = g_GlobalVariables.size();
-	g.name = name;
-	g.statename = "";
-	g.value = 0;
-	g.type = type;
-
-	g_GlobalVariables << g;
-	return &g_GlobalVariables[g.index];
-}
-
-// ============================================================================
-// Find a global variable by name
-script_variable* find_global_variable (string name)
-{
-	for (int i = 0; i < g_GlobalVariables.size(); i++)
-	{
-		script_variable* g = &g_GlobalVariables[i];
-
-		if (g->name == name)
-			return g;
-	}
-
-	return null;
-}
\ No newline at end of file
--- a/src/variables.h	Sun Feb 02 01:50:23 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
-	Copyright 2012-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:
-
-	1. Redistributions of source code must retain the above copyright
-	   notice, this list of conditions and the following disclaimer.
-	2. 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.
-	3. The name of the author may not be used to endorse or promote products
-	   derived from this software without specific prior written permission.
-
-	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
-*/
-
-#ifndef BOTC_VARIABLES_H
-#define BOTC_VARIABLES_H
-
-#include "main.h"
-
-class botscript_parser;
-
-struct script_variable
-{
-	string name;
-	string statename;
-	type_e type;
-	int value;
-	int index;
-
-	bool is_global() const
-	{
-		return statename.is_empty();
-	}
-};
-
-extern list<script_variable> g_GlobalVariables;
-extern list<script_variable> g_LocalVariables;
-
-#define ITERATE_GLOBAL_VARS(u) \
-	for (u = 0; u < MAX_SCRIPT_VARIABLES; u++)
-#define ITERATE_SCRIPT_VARS(g) \
-	for (g = g_ScriptVariable; g != null; g = g->next)
-
-script_variable* declare_global_variable (type_e type, string name);
-script_variable* find_global_variable (string name);
-
-#endif // BOTC_VARIABLES_H
--- a/updaterevision/CMakeLists.txt	Sun Feb 02 01:50:23 2014 +0200
+++ b/updaterevision/CMakeLists.txt	Sun Feb 02 17:06:39 2014 +0200
@@ -1,3 +1,2 @@
-cmake_minimum_required( VERSION 2.4 )
-
+cmake_minimum_required (VERSION 2.4)
 add_executable (updaterevision updaterevision.c)
\ No newline at end of file

mercurial