- renamed source files to camelCase

Sun, 30 Mar 2014 21:51:23 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sun, 30 Mar 2014 21:51:23 +0300
changeset 119
bdf8d46c145f
parent 118
e3361cf7cbf4
child 120
5ea0faefa82a

- renamed source files to camelCase

CMakeLists.txt file | annotate | diff | comparison | revisions
namedenums/CMakeLists.txt file | annotate | diff | comparison | revisions
namedenums/NamedEnumerations.cc file | annotate | diff | comparison | revisions
namedenums/namedenums.cpp 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/Expression.cc file | annotate | diff | comparison | revisions
src/Expression.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/Macros.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/botStuff.h file | annotate | diff | comparison | revisions
src/commands.cpp file | annotate | diff | comparison | revisions
src/commands.h file | annotate | diff | comparison | revisions
src/dataBuffer.cpp file | annotate | diff | comparison | revisions
src/dataBuffer.h file | annotate | diff | comparison | revisions
src/events.cpp file | annotate | diff | comparison | revisions
src/events.h file | annotate | diff | comparison | revisions
src/expression.cpp file | annotate | diff | comparison | revisions
src/expression.h file | annotate | diff | comparison | revisions
src/format.cpp file | annotate | diff | comparison | revisions
src/format.h file | annotate | diff | comparison | revisions
src/lexer.cpp file | annotate | diff | comparison | revisions
src/lexer.h file | annotate | diff | comparison | revisions
src/lexerScanner.cpp file | annotate | diff | comparison | revisions
src/lexerScanner.h file | annotate | diff | comparison | revisions
src/list.h file | annotate | diff | comparison | revisions
src/macros.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/main.h file | annotate | diff | comparison | revisions
src/parser.cpp file | annotate | diff | comparison | revisions
src/parser.h file | annotate | diff | comparison | revisions
src/property.h file | annotate | diff | comparison | revisions
src/string.cpp file | annotate | diff | comparison | revisions
src/string.h file | annotate | diff | comparison | revisions
src/stringTable.cpp 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
--- a/CMakeLists.txt	Sun Mar 30 21:35:06 2014 +0300
+++ b/CMakeLists.txt	Sun Mar 30 21:51:23 2014 +0300
@@ -1,37 +1,37 @@
 cmake_minimum_required (VERSION 2.8)
 
 set (BOTC_HEADERS
-	src/BotStuff.h
-	src/Commands.h
-	src/Containers.h
-	src/DataBuffer.h
-	src/Events.h
-	src/Expression.h
-	src/Format.h
-	src/Lexer.h
-	src/LexerScanner.h
-	src/Macros.h
-	src/Main.h
-	src/Parser.h
-	src/Property.h
-	src/String.h
-	src/StringTable.h
-	src/Tokens.h
-	src/Types.h
+	src/botStuff.h
+	src/commands.h
+	src/list.h
+	src/dataBuffer.h
+	src/events.h
+	src/expression.h
+	src/format.h
+	src/lexer.h
+	src/lexerScanner.h
+	src/macros.h
+	src/main.h
+	src/parser.h
+	src/property.h
+	src/string.h
+	src/stringTable.h
+	src/tokens.h
+	src/types.h
 )
 
 set (BOTC_SOURCES
-	src/Commands.cc
-	src/DataBuffer.cc
-	src/Events.cc
-	src/Expression.cc
-	src/Format.cc
-	src/Lexer.cc
-	src/LexerScanner.cc
-	src/Main.cc
-	src/Parser.cc
-	src/String.cc
-	src/StringTable.cc
+	src/commands.cpp
+	src/dataBuffer.cpp
+	src/events.cpp
+	src/expression.cpp
+	src/format.cpp
+	src/lexer.cpp
+	src/lexerScanner.cpp
+	src/main.cpp
+	src/parser.cpp
+	src/string.cpp
+	src/stringTable.cpp
 )
 
 add_subdirectory (updaterevision)
@@ -40,14 +40,14 @@
 get_target_property (UPDATEREVISION_EXE updaterevision LOCATION)
 
 add_custom_target (revision_check ALL
-    COMMAND ${UPDATEREVISION_EXE} src/GitInformation.h
+    COMMAND ${UPDATEREVISION_EXE} src/gitinfo.h
     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
     DEPENDS updaterevision)
 
 get_target_property (NAMEDENUMS_EXE namedenums LOCATION)
 
 add_custom_target (botc_enum_strings ALL
-    COMMAND ${NAMEDENUMS_EXE} ${BOTC_HEADERS} src/EnumStrings.h
+    COMMAND ${NAMEDENUMS_EXE} ${BOTC_HEADERS} src/enumStrings.h
     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
     DEPENDS namedenums)
 
@@ -57,4 +57,4 @@
 
 if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
 	set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG")
-endif()
\ No newline at end of file
+endif()
--- a/namedenums/CMakeLists.txt	Sun Mar 30 21:35:06 2014 +0300
+++ b/namedenums/CMakeLists.txt	Sun Mar 30 21:51:23 2014 +0300
@@ -1,3 +1,3 @@
 cmake_minimum_required (VERSION 2.4)
-add_executable (namedenums NamedEnumerations.cc)
+add_executable (namedenums namedenums.cpp)
 set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -W -Wall")
--- a/namedenums/NamedEnumerations.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,251 +0,0 @@
-/*
-	Copyright 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 <string>
-#include <deque>
-#include <algorithm>
-#include <cerrno>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <cstdarg>
-
-using std::string;
-using std::deque;
-
-static int gLineNumber;
-static std::string gCurrentFile;
-
-// =============================================================================
-//
-struct NamedEnumInfo
-{
-	string			name;
-	deque<string>	enumerators;
-};
-
-// =============================================================================
-//
-void SkipWhitespace (char*& cp)
-{
-	while (isspace (*cp))
-	{
-		if (*cp == '\n')
-			gLineNumber++;
-
-		++cp;
-	}
-
-	if (strncmp (cp, "//", 2) == 0)
-	{
-		while (*(++cp) != '\n')
-			;
-
-		gLineNumber++;
-		SkipWhitespace (cp);
-	}
-}
-
-// =============================================================================
-//
-void Error (const char* fmt, ...)
-{
-	char buf[1024];
-	va_list va;
-	va_start (va, fmt);
-	vsprintf (buf, fmt, va);
-	va_end (va);
-	throw std::string (buf);
-}
-
-// =============================================================================
-//
-int main (int argc, char* argv[])
-{
-	try
-	{
-		deque<NamedEnumInfo>	namedEnumerations;
-		deque<string>			filesToInclude;
-
-		if (argc < 3)
-		{
-			fprintf (stderr, "usage: %s input [input [input [...]]] output\n", argv[0]);
-			return EXIT_FAILURE;
-		}
-
-		for (int i = 1; i < argc - 1; ++ i)
-		{
-			gCurrentFile = argv[i];
-			FILE* fp = fopen (argv[i], "r");
-			char* buf;
-			gLineNumber = 1;
-
-			if (fp == NULL)
-			{
-				fprintf (stderr, "could not open %s for writing: %s\n",
-					argv[i], strerror (errno));
-				exit (EXIT_FAILURE);
-			}
-
-			fseek (fp, 0, SEEK_END);
-			long int filesize = ftell (fp);
-			rewind (fp);
-
-			try
-			{
-				buf = new char[filesize];
-			}
-			catch (std::bad_alloc)
-			{
-				Error ("could not allocate %ld bytes for %s\n", filesize, argv[i]);
-			}
-
-			if (static_cast<long> (fread (buf, 1, filesize, fp)) < filesize)
-				Error ("could not read %ld bytes from %s\n", filesize, argv[i]);
-
-			char* const end = &buf[0] + filesize;
-
-			for (char* cp = &buf[0]; cp < end; ++cp)
-			{
-				SkipWhitespace (cp);
-
-				if (strncmp (cp, "#define ", strlen ("#define ")) == 0)
-				{
-					while (cp < end && *cp != '\n')
-						cp++;
-
-					continue;
-				}
-
-				if ((cp != &buf[0] && isspace (* (cp - 1)) == false) ||
-					strncmp (cp, "named_enum ", strlen ("named_enum ")) != 0)
-				{
-					continue;
-				}
-
-				cp += strlen ("named_enum ");
-				SkipWhitespace (cp);
-
-				NamedEnumInfo nenum;
-				auto& enumname = nenum.name;
-				auto& enumerators = nenum.enumerators;
-
-				if (isalpha (*cp) == false && *cp != '_')
-					Error ("anonymous named_enum");
-
-				while (isalnum (*cp) || *cp == '_')
-					enumname += *cp++;
-
-				SkipWhitespace (cp);
-
-				if (*cp++ != '{')
-					Error ("expected '{' after named_enum");
-
-				for (;;)
-				{
-					SkipWhitespace (cp);
-
-					if (*cp == '}')
-					{
-						cp++;
-						break;
-					}
-
-					if (isalpha (*cp) == false && *cp != '_')
-						Error ("expected identifier, got '%c'", *cp);
-
-					std::string enumerator;
-
-					while (isalnum (*cp) || *cp == '_')
-						enumerator += *cp++;
-
-					SkipWhitespace (cp);
-
-					if (*cp == ',')
-						SkipWhitespace (++cp);
-
-					if (*cp == '=')
-						Error ("named enums must not have defined values");
-
-					enumerators.push_back (enumerator);
-				}
-
-				SkipWhitespace (cp);
-
-				if (*cp != ';')
-					Error ("expected ';'");
-
-				if (enumerators.size() > 0)
-				{
-					namedEnumerations.push_back (nenum);
-					filesToInclude.push_back (argv[i]);
-				}
-			}
-		}
-
-		FILE* fp;
-
-		if ((fp = fopen (argv[argc - 1], "w")) == NULL)
-			Error ("couldn't open %s for writing: %s", argv[argc - 1], strerror (errno));
-
-		fprintf (fp, "#pragma once\n");
-
-		std::sort (filesToInclude.begin(), filesToInclude.end());
-		auto pos = std::unique (filesToInclude.begin(), filesToInclude.end());
-		filesToInclude.resize (std::distance (filesToInclude.begin(), pos));
-
-		for (const string& a : filesToInclude)
-			fprintf (fp, "#include \"%s\"\n", basename (a.c_str()));
-
-		for (NamedEnumInfo& e : namedEnumerations)
-		{
-			fprintf (fp, "\nstatic const char* g_%sNames[] =\n{\n", e.name.c_str());
-
-			for (const string& a : e.enumerators)
-				fprintf (fp, "\t\"%s\",\n", a.c_str());
-
-			fprintf (fp, "};\n\n");
-
-			fprintf (fp, "inline const char* get%sString (%s a)\n"
-				"{\n"
-				"\treturn g_%sNames[a];\n"
-				"}\n",
-				e.name.c_str(), e.name.c_str(), e.name.c_str());
-		}
-
-		printf ("Wrote named enumerations to %s\n", argv[argc - 1]);
-		fclose (fp);
-	}
-	catch (std::string a)
-	{
-		fprintf (stderr, "%s:%d: error: %s\n", gCurrentFile.c_str(), gLineNumber, a.c_str());
-		return 1;
-	}
-
-	return 0;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/namedenums/namedenums.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,251 @@
+/*
+	Copyright 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 <string>
+#include <deque>
+#include <algorithm>
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cstdarg>
+
+using std::string;
+using std::deque;
+
+static int gLineNumber;
+static std::string gCurrentFile;
+
+// =============================================================================
+//
+struct NamedEnumInfo
+{
+	string			name;
+	deque<string>	enumerators;
+};
+
+// =============================================================================
+//
+void SkipWhitespace (char*& cp)
+{
+	while (isspace (*cp))
+	{
+		if (*cp == '\n')
+			gLineNumber++;
+
+		++cp;
+	}
+
+	if (strncmp (cp, "//", 2) == 0)
+	{
+		while (*(++cp) != '\n')
+			;
+
+		gLineNumber++;
+		SkipWhitespace (cp);
+	}
+}
+
+// =============================================================================
+//
+void Error (const char* fmt, ...)
+{
+	char buf[1024];
+	va_list va;
+	va_start (va, fmt);
+	vsprintf (buf, fmt, va);
+	va_end (va);
+	throw std::string (buf);
+}
+
+// =============================================================================
+//
+int main (int argc, char* argv[])
+{
+	try
+	{
+		deque<NamedEnumInfo>	namedEnumerations;
+		deque<string>			filesToInclude;
+
+		if (argc < 3)
+		{
+			fprintf (stderr, "usage: %s input [input [input [...]]] output\n", argv[0]);
+			return EXIT_FAILURE;
+		}
+
+		for (int i = 1; i < argc - 1; ++ i)
+		{
+			gCurrentFile = argv[i];
+			FILE* fp = fopen (argv[i], "r");
+			char* buf;
+			gLineNumber = 1;
+
+			if (fp == NULL)
+			{
+				fprintf (stderr, "could not open %s for writing: %s\n",
+					argv[i], strerror (errno));
+				exit (EXIT_FAILURE);
+			}
+
+			fseek (fp, 0, SEEK_END);
+			long int filesize = ftell (fp);
+			rewind (fp);
+
+			try
+			{
+				buf = new char[filesize];
+			}
+			catch (std::bad_alloc)
+			{
+				Error ("could not allocate %ld bytes for %s\n", filesize, argv[i]);
+			}
+
+			if (static_cast<long> (fread (buf, 1, filesize, fp)) < filesize)
+				Error ("could not read %ld bytes from %s\n", filesize, argv[i]);
+
+			char* const end = &buf[0] + filesize;
+
+			for (char* cp = &buf[0]; cp < end; ++cp)
+			{
+				SkipWhitespace (cp);
+
+				if (strncmp (cp, "#define ", strlen ("#define ")) == 0)
+				{
+					while (cp < end && *cp != '\n')
+						cp++;
+
+					continue;
+				}
+
+				if ((cp != &buf[0] && isspace (* (cp - 1)) == false) ||
+					strncmp (cp, "named_enum ", strlen ("named_enum ")) != 0)
+				{
+					continue;
+				}
+
+				cp += strlen ("named_enum ");
+				SkipWhitespace (cp);
+
+				NamedEnumInfo nenum;
+				auto& enumname = nenum.name;
+				auto& enumerators = nenum.enumerators;
+
+				if (isalpha (*cp) == false && *cp != '_')
+					Error ("anonymous named_enum");
+
+				while (isalnum (*cp) || *cp == '_')
+					enumname += *cp++;
+
+				SkipWhitespace (cp);
+
+				if (*cp++ != '{')
+					Error ("expected '{' after named_enum");
+
+				for (;;)
+				{
+					SkipWhitespace (cp);
+
+					if (*cp == '}')
+					{
+						cp++;
+						break;
+					}
+
+					if (isalpha (*cp) == false && *cp != '_')
+						Error ("expected identifier, got '%c'", *cp);
+
+					std::string enumerator;
+
+					while (isalnum (*cp) || *cp == '_')
+						enumerator += *cp++;
+
+					SkipWhitespace (cp);
+
+					if (*cp == ',')
+						SkipWhitespace (++cp);
+
+					if (*cp == '=')
+						Error ("named enums must not have defined values");
+
+					enumerators.push_back (enumerator);
+				}
+
+				SkipWhitespace (cp);
+
+				if (*cp != ';')
+					Error ("expected ';'");
+
+				if (enumerators.size() > 0)
+				{
+					namedEnumerations.push_back (nenum);
+					filesToInclude.push_back (argv[i]);
+				}
+			}
+		}
+
+		FILE* fp;
+
+		if ((fp = fopen (argv[argc - 1], "w")) == NULL)
+			Error ("couldn't open %s for writing: %s", argv[argc - 1], strerror (errno));
+
+		fprintf (fp, "#pragma once\n");
+
+		std::sort (filesToInclude.begin(), filesToInclude.end());
+		auto pos = std::unique (filesToInclude.begin(), filesToInclude.end());
+		filesToInclude.resize (std::distance (filesToInclude.begin(), pos));
+
+		for (const string& a : filesToInclude)
+			fprintf (fp, "#include \"%s\"\n", basename (a.c_str()));
+
+		for (NamedEnumInfo& e : namedEnumerations)
+		{
+			fprintf (fp, "\nstatic const char* g_%sNames[] =\n{\n", e.name.c_str());
+
+			for (const string& a : e.enumerators)
+				fprintf (fp, "\t\"%s\",\n", a.c_str());
+
+			fprintf (fp, "};\n\n");
+
+			fprintf (fp, "inline const char* get%sString (%s a)\n"
+				"{\n"
+				"\treturn g_%sNames[a];\n"
+				"}\n",
+				e.name.c_str(), e.name.c_str(), e.name.c_str());
+		}
+
+		printf ("Wrote named enumerations to %s\n", argv[argc - 1]);
+		fclose (fp);
+	}
+	catch (std::string a)
+	{
+		fprintf (stderr, "%s:%d: error: %s\n", gCurrentFile.c_str(), gLineNumber, a.c_str());
+		return 1;
+	}
+
+	return 0;
+}
--- a/src/BotStuff.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +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
-
-#include "Main.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;
-
-named_enum DataHeader
-{
-	DH_Command,
-	DH_StateIndex,
-	DH_StateName,
-	DH_OnEnter,
-	DH_MainLoop,
-	DH_OnExit,
-	DH_Event,
-	DH_EndOnEnter,
-	DH_EndMainLoop,
-	DH_EndOnExit,
-	DH_EndEvent,
-	DH_IfGoto,
-	DH_IfNotGoto,
-	DH_Goto,
-	DH_OrLogical,
-	DH_AndLogical,
-	DH_OrBitwise,
-	DH_EorBitwise,
-	DH_AndBitwise,
-	DH_Equals,
-	DH_NotEquals,
-	DH_LessThan,
-	DH_AtMost,
-	DH_GreaterThan,
-	DH_AtLeast,
-	DH_NegateLogical,
-	DH_LeftShift,
-	DH_RightShift,
-	DH_Add,
-	DH_Subtract,
-	DH_UnaryMinus,
-	DH_Multiply,
-	DH_Divide,
-	DH_Modulus,
-	DH_PushNumber,
-	DH_PushStringIndex,
-	DH_PushGlobalVar,
-	DH_PushLocalVar,
-	DH_DropStackPosition,
-	DH_ScriptVarList,
-	DH_StringList,
-	DH_IncreaseGlobalVar,
-	DH_DecreaseGlobalVar,
-	DH_AssignGlobalVar,
-	DH_AddGlobalVar,
-	DH_SubtractGlobalVar,
-	DH_MultiplyGlobalVar,
-	DH_DivideGlobalVar,
-	DH_ModGlobalVar,
-	DH_IncreaseLocalVar,
-	DH_DecreaseLocalVar,
-	DH_AssignLocalVar,
-	DH_AddLocalVar,
-	DH_SubtractLocalVar,
-	DH_MultiplyLocalVar,
-	DH_DivideLocalVar,
-	DH_ModLocalVar,
-	DH_CaseGoto,
-	DH_Drop,
-	DH_IncreaseGlobalArray,
-	DH_DecreaseGlobalArray,
-	DH_AssignGlobalArray,
-	DH_AddGlobalArray,
-	DH_SubtractGlobalArray,
-	DH_MultiplyGlobalArray,
-	DH_DivideGlobalArray,
-	DH_ModGlobalArray,
-	DH_PushGlobalArray,
-	DH_Swap,
-	DH_Dup,
-	DH_ArraySet,
-	numDataHeaders
-};
-
-#endif	// BOTC_BOTSTUFF_H
--- a/src/Commands.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +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 "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::signature()
-{
-	String text;
-	text += dataTypeName (returnvalue);
-	text += ' ';
-	text += name;
-
-	if (args.isEmpty() == false)
-		text += ' ';
-
-	text += '(';
-
-	bool hasoptionals = false;
-
-	for (int i = 0; i < args.size(); i++)
-	{
-		if (i == minargs)
-		{
-			hasoptionals = true;
-			text += '[';
-		}
-
-		if (i)
-			text += ", ";
-
-		text += dataTypeName (args[i].type);
-		text += ' ';
-		text += args[i].name;
-
-		if (i >= minargs)
-		{
-			bool isString = args[i].type == TYPE_String;
-			text += " = ";
-
-			if (isString)
-				text += '"';
-
-			text += String::fromNumber (args[i].defvalue);
-
-			if (isString)
-				text += '"';
-		}
-	}
-
-	if (hasoptionals)
-		text += ']';
-
-	text += ')';
-	return text;
-}
-
-const List<CommandInfo*>& getCommands()
-{
-	return gCommands;
-}
--- a/src/Commands.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +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 "String.h"
-
-struct CommandArgument
-{
-	DataType				type;
-	String					name;
-	int						defvalue;
-};
-
-struct CommandInfo
-{
-	String					name;
-	int						number;
-	int						minargs;
-	DataType				returnvalue;
-	List<CommandArgument>	args;
-	String					origin;
-
-	String	signature();
-};
-
-void						addCommandDefinition (CommandInfo* comm);
-CommandInfo*				findCommandByName (String a);
-const List<CommandInfo*>&	getCommands();
-
-#endif // BOTC_COMMANDS_H
--- a/src/Containers.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,358 +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<typename T>
-class List
-{
-	public:
-		using WrappedList				= typename std::deque<T>;
-		using Iterator					= typename WrappedList::iterator;
-		using ConstIterator				= typename WrappedList::const_iterator;
-		using ReverseIterator			= typename WrappedList::reverse_iterator;
-		using ConstReverseIterator		= typename WrappedList::const_reverse_iterator;
-		using ValueType					= T;
-		using Self						= List<T>;
-
-		// =====================================================================
-		//
-		List() {}
-
-		// =====================================================================
-		//
-		List (std::initializer_list<ValueType> vals)
-		{
-			m_data = vals;
-		}
-
-		// =====================================================================
-		//
-		List (const WrappedList& 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 Self& other)
-		{
-			resize (size() + other.size());
-			std::copy (other.begin(), other.end(), begin() + other.size());
-		}
-
-		// =====================================================================
-		//
-		bool pop (T& val)
-		{
-			if (isEmpty())
-				return false;
-
-			val = m_data[size() - 1];
-			m_data.erase (m_data.end() - 1);
-			return true;
-		}
-
-		// =====================================================================
-		//
-		Self& operator<< (const T& value)
-		{
-			append (value);
-			return *this;
-		}
-
-		// =====================================================================
-		//
-		void operator<< (const Self& vals)
-		{
-			merge (vals);
-		}
-
-		// =====================================================================
-		//
-		bool operator>> (T& value)
-		{
-			return pop (value);
-		}
-
-		// =====================================================================
-		//
-		Self reverse() const
-		{
-			Self 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 removeOne (const ValueType& it)
-		{
-			int idx;
-
-			if ((idx = find (it)) != -1)
-				removeAt (idx);
-		}
-
-		// =====================================================================
-		//
-		inline bool isEmpty() const
-		{
-			return size() == 0;
-		}
-
-		// =====================================================================
-		//
-		Self splice (int a, int b) const
-		{
-			assert (a >= 0 && b >= 0 && a < size() && b < size() && a <= b);
-			Self result;
-
-			for (int i = a; i <= b; ++i)
-				result << operator[] (i);
-
-			return result;
-		}
-
-		// =====================================================================
-		//
-		inline const WrappedList& deque() 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;
-		}
-
-		// =====================================================================
-		//
-		Self operator+ (const Self& other) const
-		{
-			Self out (*this);
-			out.merge (other);
-			return out;
-		}
-
-	private:
-		WrappedList m_data;
-};
-
-// =============================================================================
-//
-template<typename T>
-List<T>& operator>> (const T& value, List<T>& haystack)
-{
-	haystack.prepend (value);
-	return haystack;
-}
-
-#endif // BOTC_CONTAINERS_H
--- a/src/DataBuffer.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,242 +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 "DataBuffer.h"
-
-// ============================================================================
-//
-DataBuffer::DataBuffer (int size)
-{
-	setBuffer (new char[size]);
-	setPosition (&buffer()[0]);
-	setAllocatedSize (size);
-}
-
-// ============================================================================
-//
-DataBuffer::~DataBuffer()
-{
-	assert (marks().isEmpty() && references().isEmpty());
-	delete buffer();
-}
-
-// ============================================================================
-//
-void DataBuffer::mergeAndDestroy (DataBuffer* other)
-{
-	if (other == null)
-		return;
-
-	// Note: We transfer the marks before the buffer is copied, so that the
-	// offset uses the proper value (which is the written size of @other, which
-	// we don't want our written size to be added to yet).
-	other->transferMarksTo (this);
-	copyBuffer (other);
-	delete other;
-}
-
-// ============================================================================
-//
-// Clones this databuffer to a new one and returns it. Note that the original
-// transfers its marks and references and loses them in the process.
-//
-DataBuffer* DataBuffer::clone()
-{
-	DataBuffer* other = new DataBuffer;
-	transferMarksTo (other);
-	other->copyBuffer (this);
-	return other;
-}
-
-// ============================================================================
-//
-void DataBuffer::copyBuffer (const DataBuffer* buf)
-{
-	checkSpace (buf->writtenSize());
-	memcpy (m_position, buf->buffer(), buf->writtenSize());
-	m_position += buf->writtenSize();
-}
-
-// ============================================================================
-//
-void DataBuffer::transferMarksTo (DataBuffer* dest)
-{
-	int offset = dest->writtenSize();
-
-	for (ByteMark* mark : marks())
-	{
-		mark->pos += offset;
-		dest->m_marks << mark;
-	}
-
-	for (MarkReference* ref : references())
-	{
-		ref->pos += offset;
-		dest->m_references << ref;
-	}
-
-	m_marks.clear();
-	m_references.clear();
-}
-
-// ============================================================================
-//
-ByteMark* DataBuffer::addMark (const String& name)
-{
-	ByteMark* mark = new ByteMark;
-	mark->name = name;
-	mark->pos = writtenSize();
-	m_marks << mark;
-	return mark;
-}
-
-// ============================================================================
-//
-MarkReference* DataBuffer::addReference (ByteMark* mark)
-{
-	MarkReference* ref = new MarkReference;
-	ref->target = mark;
-	ref->pos = writtenSize();
-	m_references << ref;
-
-	// Write a dummy placeholder for the reference
-	writeDWord (0xBEEFCAFE);
-
-	return ref;
-}
-
-// ============================================================================
-//
-void DataBuffer::adjustMark (ByteMark* mark)
-{
-	mark->pos = writtenSize();
-}
-
-// ============================================================================
-//
-void DataBuffer::offsetMark (ByteMark* mark, int position)
-{
-	mark->pos += position;
-}
-
-// ============================================================================
-//
-void DataBuffer::writeStringIndex (const String& a)
-{
-	writeDWord (DH_PushStringIndex);
-	writeDWord (getStringTableIndex (a));
-}
-
-// ============================================================================
-//
-void DataBuffer::dump()
-{
-	for (int i = 0; i < writtenSize(); ++i)
-		printf ("%d. [0x%X]\n", i, buffer()[i]);
-}
-
-// ============================================================================
-//
-void DataBuffer::checkSpace (int bytes)
-{
-	int writesize = writtenSize();
-
-	if (writesize + bytes < allocatedSize())
-		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[allocatedSize()];
-	memcpy (copy, buffer(), allocatedSize());
-
-	// 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 = allocatedSize() + bytes + 512;
-
-	delete buffer();
-	setBuffer (new char[newsize]);
-	setAllocatedSize (newsize);
-
-	// Now, copy the stuff back.
-	memcpy (m_buffer, copy, allocatedSize());
-	setPosition (buffer() + writesize);
-	delete copy;
-}
-
-// =============================================================================
-//
-void DataBuffer::writeByte (int8_t data)
-{
-	checkSpace (1);
-	*m_position++ = data;
-}
-
-// =============================================================================
-//
-void DataBuffer::writeWord (int16_t data)
-{
-	checkSpace (2);
-
-	for (int i = 0; i < 2; ++i)
-		*m_position++ = (data >> (i * 8)) & 0xFF;
-}
-
-// =============================================================================
-//
-void DataBuffer::writeDWord (int32_t data)
-{
-	checkSpace (4);
-
-	for (int i = 0; i < 4; ++i)
-		*m_position++ = (data >> (i * 8)) & 0xFF;
-}
-
-// =============================================================================
-//
-void DataBuffer::writeString (const String& a)
-{
-	checkSpace (a.length() + 4);
-	writeDWord (a.length());
-
-	for (char c : a)
-		writeByte (c);
-}
-
-
-// =============================================================================
-//
-ByteMark* DataBuffer::findMarkByName (const String& name)
-{
-	for (ByteMark* mark : marks())
-		if (mark->name == name)
-			return mark;
-
-	return null;
-}
--- a/src/DataBuffer.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +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"
-
-/**
- *    @class DataBuffer
- *    @brief Stores a buffer of bytecode
- *
- *    The DataBuffer class stores a section of bytecode. Buffers are allocated on
- *    the heap and written to using the @c write* functions. Buffers can be cut and
- *    pasted together with @c mergeAndDestroy, note that this function destroys the
- *    parameter buffer in the process
- *
- *    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 "bookmark" a position like that for future use.
- *
- *    A reference acts as a pointer to a mark. The reference is four bytes in the
- *    bytecode which will be replaced with its mark's position when the bytecode
- *    is written to the output file.
- *
- *    This mark/reference system is used to know bytecode offset values when
- *    compiling, even though actual final positions cannot be known.
- */
-class DataBuffer
-{
-	//! @
-	PROPERTY (private, char*,					buffer,			setBuffer,			STOCK_WRITE)
-	PROPERTY (private, int,						allocatedSize,	setAllocatedSize,	STOCK_WRITE)
-	PROPERTY (private, char*,					position,		setPosition,		STOCK_WRITE)
-	PROPERTY (private, List<ByteMark*>,			marks,			setMarks,			STOCK_WRITE)
-	PROPERTY (private, List<MarkReference*>,	references,		setReferences,		STOCK_WRITE)
-
-	public:
-		//! Constructs a new databuffer with @c size bytes.
-		DataBuffer (int size = 128);
-
-		//! Destructs the databuffer.
-		~DataBuffer();
-
-		//! Adds a new mark to the current position with the name @c name.
-		//! @param name the name of the new mark
-		//! @return a pointer to the new mark
-		ByteMark*		addMark (const String& name);
-
-		//! Adds a new reference to @c mark at the current position. This
-		//! function will write 4 bytes to the buffer whose value will
-		//! be determined at final output writing.
-		//! @param mark the mark which the new reference will attach to
-		//! @return a pointer to the new reference
-		MarkReference*	addReference (ByteMark* mark);
-
-		//! Moves @c mark to the current bytecode position.
-		//! @param mark the mark to adjust
-		void			adjustMark (ByteMark* mark);
-
-		//! Ensures there's at least @c bytes left in the buffer. Will resize
-		//! if necessary, no-op if not. On resize, 512 extra bytes are allocated
-		//! to reduce the amount of resizes.
-		//! @param bytes the amount of space in bytes to ensure allocated
-		void			checkSpace (int bytes);
-
-		//! Creates a clone of this data buffer.
-		//! @note All marks will be moved into the new databuffer as marks are
-		//! @note never duplicated.
-		//! @return The newly cloned databuffer.
-		DataBuffer*		clone();
-
-		//! Prints the buffer to stdout. Useful for debugging.
-		void			dump();
-
-		//! Finds the given mark by name.
-		//! @param name the name of the mark to find
-		ByteMark*		findMarkByName (const String& name);
-
-		//! Merge another data buffer into this one.
-		//! Note: @c other is destroyed in the process.
-		//! @param other the buffer to merge in
-		void			mergeAndDestroy (DataBuffer* other);
-
-		//! Moves @c mark to the given bytecode position.
-		//! @param mark the mark to adjust
-		//! @param position where to adjust the mark
-		void			offsetMark (ByteMark* mark, int position);
-
-		//! Transfers all marks of this buffer to @c other.
-		//! @param other the data buffer to transfer marks to
-		void			transferMarksTo (DataBuffer* other);
-
-		//! Writes the index of the given string to the databuffer.
-		//! 4 bytes will be written to the bytecode.
-		//! @param a the string whose index to write
-		void			writeStringIndex (const String& a);
-
-		//! Writes the given string as-is into the databuffer.
-		//! @c a.length + 4 bytes will be written to the buffer.
-		//! @param a the string to write
-		void			writeString (const String& a);
-
-		//! Writes the given byte to the buffer. 1 byte will be written.
-		//! @c data the byte to write
-		void			writeByte (int8_t data);
-
-		//! Writes the given word to the buffer. 2 byte will be written.
-		//! @c data the word to write
-		void			writeWord (int16_t data);
-
-		//! Writes the given double word to the buffer. 4 bytes will be written.
-		//! @c data the double word to write
-		void			writeDWord (int32_t data);
-
-		//! @return the amount of bytes written to this buffer.
-		inline int		writtenSize() const
-		{
-			return position() - buffer();
-		}
-
-	protected:
-		//! Writes the buffer's contents from @c buf.
-		//! @c buf.writtenSize() bytes will be written.
-		//! @param buf the buffer to copy
-		void			copyBuffer (const DataBuffer* buf);
-};
-
-#endif // BOTC_DATABUFFER_H
--- a/src/Events.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +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.
-*/
-
-// TODO: this file is a freeloader, I should probably get rid of it
-
-#include <stdlib.h>
-#include <stdio.h>
-#include "Main.h"
-#include "String.h"
-#include "Events.h"
-#include "Lexer.h"
-
-static List<EventDefinition*> g_Events;
-
-// ============================================================================
-//
-void addEvent (EventDefinition* e)
-{
-	g_Events << e;
-}
-
-// ============================================================================
-//
-// Finds an event definition by index
-//
-EventDefinition* findEventByIndex (int idx)
-{
-	return g_Events[idx];
-}
-
-// ============================================================================
-//
-// Finds an event definition by name
-//
-EventDefinition* findEventByName (String a)
-{
-	for (EventDefinition* e : g_Events)
-		if (a.toUppercase() == e->name.toUppercase())
-			return e;
-
-	return null;
-}
--- a/src/Events.h	Sun Mar 30 21:35:06 2014 +0300
+++ /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 "String.h"
-
-struct EventDefinition
-{
-	String name;
-	int number;
-};
-
-void addEvent (EventDefinition* e);
-EventDefinition* findEventByIndex (int idx);
-EventDefinition* findEventByName (String a);
-
-#endif // BOTC_EVENTS_H
--- a/src/Expression.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,651 +0,0 @@
-#include "Expression.h"
-#include "DataBuffer.h"
-#include "Lexer.h"
-
-struct OperatorInfo
-{
-	ETokenType	token;
-	int			priority;
-	int			numoperands;
-	DataHeader	header;
-};
-
-static const OperatorInfo g_Operators[] =
-{
-	{TK_ExclamationMark,	0,		1,	DH_NegateLogical,	},
-	{TK_Minus,				0,		1,	DH_UnaryMinus,		},
-	{TK_Multiply,			10,		2,	DH_Multiply,		},
-	{TK_Divide,				10,		2,	DH_Divide,			},
-	{TK_Modulus,			10,		2,	DH_Modulus,			},
-	{TK_Plus,				20,		2,	DH_Add,				},
-	{TK_Minus,				20,		2,	DH_Subtract,		},
-	{TK_LeftShift,			30,		2,	DH_LeftShift,		},
-	{TK_RightShift,			30,		2,	DH_RightShift,		},
-	{TK_Lesser,				40,		2,	DH_LessThan,		},
-	{TK_Greater,			40,		2,	DH_GreaterThan,		},
-	{TK_AtLeast,			40,		2,	DH_AtLeast,			},
-	{TK_AtMost,				40,		2,	DH_AtMost,			},
-	{TK_Equals,				50,		2,	DH_Equals			},
-	{TK_NotEquals,			50,		2,	DH_NotEquals		},
-	{TK_Amperstand,			60,		2,	DH_AndBitwise		},
-	{TK_Caret,				70,		2,	DH_EorBitwise		},
-	{TK_Bar,				80,		2,	DH_OrBitwise		},
-	{TK_DoubleAmperstand,	90,		2,	DH_AndLogical		},
-	{TK_DoubleBar,			100,	2,	DH_OrLogical		},
-	{TK_QuestionMark,		110,	3,	(DataHeader) 0		},
-};
-
-// =============================================================================
-//
-Expression::Expression (BotscriptParser* parser, Lexer* lx, DataType reqtype) :
-	m_parser (parser),
-	m_lexer (lx),
-	m_type (reqtype)
-{
-	ExpressionSymbol* sym;
-
-	while ((sym = parseSymbol()) != null)
-		m_symbols << sym;
-
-	// If we were unable to get any expression symbols, something's wonky with
-	// the script. Report an error. mBadTokenText is set to the token that
-	// ParseSymbol ends at when it returns false.
-	if (m_symbols.isEmpty())
-		error ("unknown identifier '%1'", m_badTokenText);
-
-	adjustOperators();
-	verify();
-	evaluate();
-}
-
-// =============================================================================
-//
-Expression::~Expression()
-{
-	for (ExpressionSymbol* sym : m_symbols)
-		delete sym;
-}
-
-// =============================================================================
-//
-// Try to parse an expression symbol (i.e. an OPER_erator or OPER_erand or a colon)
-// from the lexer.
-//
-ExpressionSymbol* Expression::parseSymbol()
-{
-	int pos = m_lexer->position();
-	ExpressionValue* op = null;
-
-	if (m_lexer->next (TK_Colon))
-		return new ExpressionColon;
-
-	// Check for OPER_erator
-	for (const OperatorInfo& op : g_Operators)
-		if (m_lexer->next (op.token))
-			return new ExpressionOperator ((ExpressionOperatorType) (&op - &g_Operators[0]));
-
-	// Check sub-expression
-	if (m_lexer->next (TK_ParenStart))
-	{
-		Expression expr (m_parser, m_lexer, m_type);
-		m_lexer->mustGetNext (TK_ParenEnd);
-		return expr.getResult()->clone();
-	}
-
-	op = new ExpressionValue (m_type);
-
-	// Check function
-	if (CommandInfo* comm = findCommandByName (m_lexer->peekNextString()))
-	{
-		m_lexer->skip();
-
-		if (m_type != TYPE_Unknown && comm->returnvalue != m_type)
-			error ("%1 returns an incompatible data type", comm->name);
-
-		op->setBuffer (m_parser->parseCommand (comm));
-		return op;
-	}
-
-	// Check for variables
-	if (m_lexer->next (TK_DollarSign))
-	{
-		m_lexer->mustGetNext (TK_Symbol);
-		Variable* var = m_parser->findVariable (getTokenString());
-
-		if (var == null)
-			error ("unknown variable %1", getTokenString());
-
-		if (var->type != m_type)
-			error ("expression requires %1, variable $%2 is of type %3",
-				dataTypeName (m_type), var->name, dataTypeName (var->type));
-
-		if (var->isarray)
-		{
-			m_lexer->mustGetNext (TK_BracketStart);
-			Expression expr (m_parser, m_lexer, TYPE_Int);
-			expr.getResult()->convertToBuffer();
-			DataBuffer* buf = expr.getResult()->buffer()->clone();
-			buf->writeDWord (DH_PushGlobalArray);
-			buf->writeDWord (var->index);
-			op->setBuffer (buf);
-			m_lexer->mustGetNext (TK_BracketEnd);
-		}
-		elif (var->writelevel == WRITE_Constexpr)
-			op->setValue (var->value);
-		else
-		{
-			DataBuffer* buf = new DataBuffer (8);
-
-			if (var->IsGlobal())
-				buf->writeDWord (DH_PushGlobalVar);
-			else
-				buf->writeDWord (DH_PushLocalVar);
-
-			buf->writeDWord (var->index);
-			op->setBuffer (buf);
-		}
-
-		return op;
-	}
-
-	// Check for literal
-	switch (m_type)
-	{
-		case TYPE_Void:
-		case TYPE_Unknown:
-		{
-			error ("unknown identifier `%1` (expected keyword, function or variable)", getTokenString());
-			break;
-		}
-
-		case TYPE_Bool:
-		{
-			if (m_lexer->next (TK_True) || m_lexer->next (TK_False))
-			{
-				ETokenType tt = m_lexer->tokenType();
-				op->setValue (tt == TK_True ? 1 : 0);
-				return op;
-			}
-		}
-
-		case TYPE_Int:
-		{
-			if (m_lexer->next (TK_Number))
-			{
-				op->setValue (getTokenString().toLong());
-				return op;
-			}
-		}
-
-		case TYPE_String:
-		{
-			if (m_lexer->next (TK_String))
-			{
-				op->setValue (getStringTableIndex (getTokenString()));
-				return op;
-			}
-		}
-	}
-
-	m_badTokenText = m_lexer->token()->text;
-	m_lexer->setPosition (pos);
-	delete op;
-	return null;
-}
-
-// =============================================================================
-//
-// The symbol parsing process only does token-based checking for OPER_erators.
-// Thus ALL minus OPER_erators are actually unary minuses simply because both
-// have TK_Minus as their token and the unary minus is prior to the binary minus
-// in the OPER_erator table. Now that we have all symbols present, we can
-// correct cases like this.
-//
-void Expression::adjustOperators()
-{
-	for (auto it = m_symbols.begin() + 1; it != m_symbols.end(); ++it)
-	{
-		if ((*it)->type() != EXPRSYM_Operator)
-			continue;
-
-		ExpressionOperator* op = static_cast<ExpressionOperator*> (*it);
-
-		// Unary minus with a value as the previous symbol cannot really be
-		// unary; replace with binary minus.
-		if (op->id() == OPER_UnaryMinus && (*(it - 1))->type() == EXPRSYM_Value)
-			op->setID (OPER_Subtraction);
-	}
-}
-
-// =============================================================================
-//
-// Verifies a single value. Helper function for Expression::verify.
-//
-void Expression::tryVerifyValue (bool* verified, SymbolList::Iterator it)
-{
-	// If it's an unary OPER_erator we skip to its value. The actual OPER_erator will
-	// be verified separately.
-	if ((*it)->type() == EXPRSYM_Operator &&
-			g_Operators[static_cast<ExpressionOperator*> (*it)->id()].numoperands == 1)
-	{
-		++it;
-	}
-
-	int i = it - m_symbols.begin();
-
-	// Ensure it's an actual value
-	if ((*it)->type() != EXPRSYM_Value)
-		error ("malformed expression (symbol #%1 is not a value)", i);
-
-	verified[i] = true;
-}
-
-// =============================================================================
-//
-// Ensures the expression is valid and well-formed and not OMGWTFBBQ. Throws an
-// error if this is not the case.
-//
-void Expression::verify()
-{
-	if (m_symbols.size() == 1)
-	{
-		if (m_symbols[0]->type() != EXPRSYM_Value)
-			error ("bad expression");
-
-		return;
-	}
-
-	if (m_type == TYPE_String)
-		error ("Cannot perform OPER_erations on strings");
-
-	bool* verified = new bool[m_symbols.size()];
-	memset (verified, 0, m_symbols.size() * sizeof (decltype (*verified)));
-	const auto last = m_symbols.end() - 1;
-	const auto first = m_symbols.begin();
-
-	for (auto it = m_symbols.begin(); it != m_symbols.end(); ++it)
-	{
-		int i = (it - first);
-
-		if ((*it)->type() != EXPRSYM_Operator)
-			continue;
-
-		ExpressionOperator* op = static_cast<ExpressionOperator*> (*it);
-		int numoperands = g_Operators[op->id()].numoperands;
-
-		switch (numoperands)
-		{
-			case 1:
-			{
-				// Ensure that:
-				// -	unary OPER_erator is not the last symbol
-				// -	unary OPER_erator is succeeded by a value symbol
-				// -	neither symbol overlaps with something already verified
-				tryVerifyValue (verified, it + 1);
-
-				if (it == last || verified[i] == true)
-					error ("malformed expression");
-
-				verified[i] = true;
-				break;
-			}
-
-			case 2:
-			{
-				// Ensure that:
-				// -	binary OPER_erator is not the first or last symbol
-				// -	is preceded and succeeded by values
-				// -	none of the three tokens are already verified
-				//
-				// Basically similar logic as above.
-				if (it == first || it == last || verified[i] == true)
-					error ("malformed expression");
-
-				tryVerifyValue (verified, it + 1);
-				tryVerifyValue (verified, it - 1);
-				verified[i] = true;
-				break;
-			}
-
-			case 3:
-			{
-				// Ternary OPER_erator case. This goes a bit nuts.
-				// This time we have the following:
-				//
-				// (VALUE) ? (VALUE) : (VALUE)
-				//         ^
-				// --------/ we are here
-				//
-				// Check that the:
-				// -	questionmark OPER_erator is not misplaced (first or last)
-				// -	the value behind the OPER_erator (-1) is valid
-				// -	the value after the OPER_erator (+1) is valid
-				// -	the value after the colon (+3) is valid
-				// -	none of the five tokens are verified
-				//
-				tryVerifyValue (verified, it - 1);
-				tryVerifyValue (verified, it + 1);
-				tryVerifyValue (verified, it + 3);
-
-				if (it == first ||
-					it >= m_symbols.end() - 3 ||
-					verified[i] == true ||
-					verified[i + 2] == true ||
-					(*(it + 2))->type() != EXPRSYM_Colon)
-				{
-					error ("malformed expression");
-				}
-
-				verified[i] = true;
-				verified[i + 2] = true;
-				break;
-			}
-
-			default:
-				error ("WTF OPER_erator with %1 OPER_erands", numoperands);
-		}
-	}
-
-	for (int i = 0; i < m_symbols.size(); ++i)
-		if (verified[i] == false)
-			error ("malformed expression: expr symbol #%1 is was left unverified", i);
-
-	delete verified;
-}
-
-
-// =============================================================================
-//
-// Which operator to evaluate?
-//
-Expression::SymbolList::Iterator Expression::findPrioritizedOperator()
-{
-	SymbolList::Iterator	best = m_symbols.end();
-	int						bestpriority = __INT_MAX__;
-
-	for (SymbolList::Iterator it = m_symbols.begin(); it != m_symbols.end(); ++it)
-	{
-		if ((*it)->type() != EXPRSYM_Operator)
-			continue;
-
-		ExpressionOperator* op = static_cast<ExpressionOperator*> (*it);
-		const OperatorInfo* info = &g_Operators[op->id()];
-
-		if (info->priority < bestpriority)
-		{
-			best = it;
-			bestpriority = info->priority;
-		}
-	}
-
-	return best;
-}
-
-// =============================================================================
-//
-// Process the given OPER_erator and values into a new value.
-//
-ExpressionValue* Expression::evaluateOperator (const ExpressionOperator* op,
-											   const List<ExpressionValue*>& values)
-{
-	const OperatorInfo* info = &g_Operators[op->id()];
-	bool isconstexpr = true;
-	assert (values.size() == info->numoperands);
-
-	for (ExpressionValue* val : values)
-	{
-		if (val->isConstexpr() == false)
-		{
-			isconstexpr = false;
-			break;
-		}
-	}
-
-	// If not all of the values are constant expressions, none of them shall be.
-	if (isconstexpr == false)
-		for (ExpressionValue* val : values)
-			val->convertToBuffer();
-
-	ExpressionValue* newval = new ExpressionValue (m_type);
-
-	if (isconstexpr == false)
-	{
-		// This is not a constant expression so we'll have to use databuffers
-		// to convey the expression to bytecode. Actual value cannot be evaluated
-		// until Zandronum processes it at run-time.
-		newval->setBuffer (new DataBuffer);
-
-		if (op->id() == OPER_Ternary)
-		{
-			// There isn't a dataheader for ternary OPER_erator. Instead, we use DH_IfNotGoto
-			// to create an "if-block" inside an expression.
-			// Behold, big block of writing madness! :P
-			//
-			DataBuffer* buf = newval->buffer();
-			DataBuffer* b0 = values[0]->buffer();
-			DataBuffer* b1 = values[1]->buffer();
-			DataBuffer* b2 = values[2]->buffer();
-			ByteMark* mark1 = buf->addMark (""); // start of "else" case
-			ByteMark* mark2 = buf->addMark (""); // end of expression
-			buf->mergeAndDestroy (b0);
-			buf->writeDWord (DH_IfNotGoto); // if the first OPER_erand (condition)
-			buf->addReference (mark1); // didn't eval true, jump into mark1
-			buf->mergeAndDestroy (b1); // otherwise, perform second OPER_erand (true case)
-			buf->writeDWord (DH_Goto); // afterwards, jump to the end, which is
-			buf->addReference (mark2); // marked by mark2.
-			buf->adjustMark (mark1); // move mark1 at the end of the true case
-			buf->mergeAndDestroy (b2); // perform third OPER_erand (false case)
-			buf->adjustMark (mark2); // move the ending mark2 here
-
-			for (int i = 0; i < 3; ++i)
-				values[i]->setBuffer (null);
-		}
-		else
-		{
-			// Generic case: write all arguments and apply the OPER_erator's
-			// data header.
-			for (ExpressionValue* val : values)
-			{
-				newval->buffer()->mergeAndDestroy (val->buffer());
-
-				// Null the pointer out so that the value's destructor will not
-				// attempt to double-free it.
-				val->setBuffer (null);
-			}
-
-			newval->buffer()->writeDWord (info->header);
-		}
-	}
-	else
-	{
-		// We have a constant expression. We know all the values involved and
-		// can thus compute the result of this expression on compile-time.
-		List<int> nums;
-		int a;
-
-		for (ExpressionValue* val : values)
-			nums << val->value();
-
-		switch (op->id())
-		{
-			case OPER_Addition:				a = nums[0] + nums[1];					break;
-			case OPER_Subtraction:			a = nums[0] - nums[1];					break;
-			case OPER_Multiplication:		a = nums[0] * nums[1];					break;
-			case OPER_UnaryMinus:			a = -nums[0];							break;
-			case OPER_NegateLogical:		a = !nums[0];							break;
-			case OPER_LeftShift:			a = nums[0] << nums[1];					break;
-			case OPER_RightShift:			a = nums[0] >> nums[1];					break;
-			case OPER_CompareLesser:		a = (nums[0] < nums[1]) ? 1 : 0;		break;
-			case OPER_CompareGreater:		a = (nums[0] > nums[1]) ? 1 : 0;		break;
-			case OPER_CompareAtLeast:		a = (nums[0] <= nums[1]) ? 1 : 0;		break;
-			case OPER_CompareAtMost:		a = (nums[0] >= nums[1]) ? 1 : 0;		break;
-			case OPER_CompareEquals:		a = (nums[0] == nums[1]) ? 1 : 0;		break;
-			case OPER_CompareNotEquals:		a = (nums[0] != nums[1]) ? 1 : 0;		break;
-			case OPER_BitwiseAnd:			a = nums[0] & nums[1];					break;
-			case OPER_BitwiseOr:			a = nums[0] | nums[1];					break;
-			case OPER_BitwiseXOr:			a = nums[0] ^ nums[1];					break;
-			case OPER_LogicalAnd:			a = (nums[0] && nums[1]) ? 1 : 0;		break;
-			case OPER_LogicalOr:			a = (nums[0] || nums[1]) ? 1 : 0;		break;
-			case OPER_Ternary:				a = (nums[0] != 0) ? nums[1] : nums[2];	break;
-
-			case OPER_Division:
-			{
-				if (nums[1] == 0)
-					error ("division by zero in constant expression");
-
-				a = nums[0] / nums[1];
-				break;
-			}
-
-			case OPER_Modulus:
-			{
-				if (nums[1] == 0)
-					error ("modulus by zero in constant expression");
-
-				a = nums[0] % nums[1];
-				break;
-			}
-		}
-
-		newval->setValue (a);
-	}
-
-	// The new value has been generated. We don't need the old stuff anymore.
-	for (ExpressionValue* val : values)
-		delete val;
-
-	delete op;
-	return newval;
-}
-
-// =============================================================================
-//
-ExpressionValue* Expression::evaluate()
-{
-	SymbolList::Iterator it;
-
-	while ((it = findPrioritizedOperator()) != m_symbols.end())
-	{
-		int i = it - m_symbols.begin();
-		List<SymbolList::Iterator> OPER_erands;
-		ExpressionOperator* op = static_cast<ExpressionOperator*> (*it);
-		const OperatorInfo* info = &g_Operators[op->id()];
-		int lower, upper; // Boundaries of area to replace
-
-		switch (info->numoperands)
-		{
-			case 1:
-			{
-				lower = i;
-				upper = i + 1;
-				OPER_erands << it + 1;
-				break;
-			}
-
-			case 2:
-			{
-				lower = i - 1;
-				upper = i + 1;
-				OPER_erands << it - 1
-				         << it + 1;
-				break;
-			}
-
-			case 3:
-			{
-				lower = i - 1;
-				upper = i + 3;
-				OPER_erands << it - 1
-				         << it + 1
-				         << it + 3;
-				break;
-			}
-
-			default:
-				assert (false);
-		}
-
-		List<ExpressionValue*> values;
-
-		for (auto it : OPER_erands)
-			values << static_cast<ExpressionValue*> (*it);
-
-		// Note: @op and all of @values are invalid after this call.
-		ExpressionValue* newvalue = evaluateOperator (op, values);
-
-		for (int i = upper; i >= lower; --i)
-			m_symbols.removeAt (i);
-
-		m_symbols.insert (lower, newvalue);
-	}
-
-	assert (m_symbols.size() == 1 && m_symbols.first()->type() == EXPRSYM_Value);
-	ExpressionValue* val = static_cast<ExpressionValue*> (m_symbols.first());
-	return val;
-}
-
-// =============================================================================
-//
-ExpressionValue* Expression::getResult()
-{
-	return static_cast<ExpressionValue*> (m_symbols.first());
-}
-
-// =============================================================================
-//
-String Expression::getTokenString()
-{
-	return m_lexer->token()->text;
-}
-
-// =============================================================================
-//
-ExpressionOperator::ExpressionOperator (ExpressionOperatorType id) :
-	ExpressionSymbol (EXPRSYM_Operator),
-	m_id (id) {}
-
-// =============================================================================
-//
-ExpressionValue::ExpressionValue (DataType valuetype) :
-	ExpressionSymbol (EXPRSYM_Value),
-	m_buffer (null),
-	m_valueType (valuetype) {}
-
-// =============================================================================
-//
-ExpressionValue::~ExpressionValue()
-{
-	delete m_buffer;
-}
-
-// =============================================================================
-//
-void ExpressionValue::convertToBuffer()
-{
-	if (isConstexpr() == false)
-		return;
-
-	setBuffer (new DataBuffer);
-
-	switch (m_valueType)
-	{
-		case TYPE_Bool:
-		case TYPE_Int:
-			buffer()->writeDWord (DH_PushNumber);
-			buffer()->writeDWord (abs (value()));
-
-			if (value() < 0)
-				buffer()->writeDWord (DH_UnaryMinus);
-			break;
-
-		case TYPE_String:
-			buffer()->writeDWord (DH_PushStringIndex);
-			buffer()->writeDWord (value());
-			break;
-
-		case TYPE_Void:
-		case TYPE_Unknown:
-			assert (false);
-			break;
-	}
-}
--- a/src/Expression.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-#ifndef BOTC_EXPRESSION_H
-#define BOTC_EXPRESSION_H
-#include "Parser.h"
-
-class DataBuffer;
-class ExpressionSymbol;
-class ExpressionValue;
-class ExpressionOperator;
-
-// =============================================================================
-//
-named_enum ExpressionOperatorType
-{
-	OPER_NegateLogical,
-	OPER_UnaryMinus,
-	OPER_Multiplication,
-	OPER_Division,
-	OPER_Modulus,
-	OPER_Addition,
-	OPER_Subtraction,
-	OPER_LeftShift,
-	OPER_RightShift,
-	OPER_CompareLesser,
-	OPER_CompareGreater,
-	OPER_CompareAtLeast,
-	OPER_CompareAtMost,
-	OPER_CompareEquals,
-	OPER_CompareNotEquals,
-	OPER_BitwiseAnd,
-	OPER_BitwiseXOr,
-	OPER_BitwiseOr,
-	OPER_LogicalAnd,
-	OPER_LogicalOr,
-	OPER_Ternary,
-};
-
-// =============================================================================
-//
-enum ExpressionSymbolType
-{
-	EXPRSYM_Operator,
-	EXPRSYM_Value,
-	EXPRSYM_Colon,
-};
-
-class Expression final
-{
-	public:
-		using SymbolList = List<ExpressionSymbol*>;
-
-		Expression (BotscriptParser* parser, Lexer* lx, DataType reqtype);
-		~Expression();
-		ExpressionValue*		getResult();
-
-	private:
-		BotscriptParser*		m_parser;
-		Lexer*					m_lexer;
-		SymbolList				m_symbols;
-		DataType				m_type;
-		String					m_badTokenText;
-
-		ExpressionValue*		evaluate(); // Process the expression and yield a result
-		ExpressionSymbol*		parseSymbol();
-		String					getTokenString();
-		void					adjustOperators();
-		void					verify(); // Ensure the expr is valid
-		void					tryVerifyValue (bool* verified, SymbolList::Iterator it);
-		ExpressionValue*		evaluateOperator (const ExpressionOperator* op,
-												  const List<ExpressionValue*>& values);
-		SymbolList::Iterator	findPrioritizedOperator();
-};
-
-// =============================================================================
-//
-class ExpressionSymbol
-{
-	public:
-		ExpressionSymbol (ExpressionSymbolType type) :
-			m_type (type) {}
-
-	PROPERTY (private, ExpressionSymbolType, type, setType, STOCK_WRITE)
-};
-
-// =============================================================================
-//
-class ExpressionOperator final : public ExpressionSymbol
-{
-	PROPERTY (public, ExpressionOperatorType, id, setID, STOCK_WRITE)
-
-	public:
-		ExpressionOperator (ExpressionOperatorType id);
-};
-
-// =============================================================================
-//
-class ExpressionValue final : public ExpressionSymbol
-{
-	PROPERTY (public, int,			value,		setValue,		STOCK_WRITE)
-	PROPERTY (public, DataBuffer*,	buffer,		setBuffer,		STOCK_WRITE)
-	PROPERTY (public, DataType,		valueType,	setValueType,	STOCK_WRITE)
-
-	public:
-		ExpressionValue (DataType valuetype);
-		~ExpressionValue();
-
-		void					convertToBuffer();
-
-		inline ExpressionValue* clone() const
-		{
-			return new ExpressionValue (*this);
-		}
-
-		inline bool isConstexpr() const
-		{
-			return buffer() == null;
-		}
-};
-
-// =============================================================================
-//
-// This class represents a ":" in the expression. It serves as the colon for the
-// ternary ?: OPER_erator. It's not an OPER_erand nor is an OPER_erator, nor can we just
-// skip it so it is its own thing here.
-//
-class ExpressionColon final : public ExpressionSymbol
-{
-	public:
-		ExpressionColon() :
-			ExpressionSymbol (EXPRSYM_Colon) {}
-};
-
-#endif // BOTC_EXPRESSION_H
--- a/src/Format.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +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 formatError (String fmtstr, const String errdescribe, int pos)
-{
-	fmtstr.replace ("\n", " ");
-	fmtstr.replace ("\t", " ");
-	String errmsg ("With format string:\n" + fmtstr + "\n");
-
-	for (int x = 0; x < pos; ++x)
-		errmsg += "-";
-
-	errmsg += "^\n" + errdescribe;
-	throw std::logic_error (errmsg.stdString());
-}
-
-// =============================================================================
-//
-String formatArgs (const String& fmtstr, const std::vector<String>& args)
-{
-	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' || fmt[pos + ofs] == 'd')
-		{
-			mod = fmt[pos + ofs];
-			ofs++;
-		}
-
-		if (!isdigit (fmt[pos + ofs]))
-			formatError (fmtstr, "bad format string, expected digit with optional "
-				"modifier after '%%'", pos);
-
-		int i = fmt[pos + ofs]  - '0';
-
-		if (i > static_cast<signed> (args.size()))
-			formatError (fmtstr, String ("Format argument #") + i + " used but not defined.", pos);
-
-		String replacement = args[i - 1];
-
-		switch (mod)
-		{
-			case 's': replacement = (replacement == "1") ? "" : "s";		break;
-			case 'd': replacement.sprintf ("%d", replacement[0]);			break;
-			case 'x': replacement.sprintf ("0x%X", replacement.toLong());	break;
-			default: break;
-		}
-
-		fmt.replace (pos, 1 + ofs, replacement);
-		pos += replacement.length();
-	}
-
-	return fmt;
-}
-
-// =============================================================================
-//
-void error (String msg)
-{
-	Lexer* lx = Lexer::getCurrentLexer();
-	String fileinfo;
-
-	if (lx != null && lx->hasValidToken())
-	{
-		Lexer::TokenInfo* tk = lx->token();
-		fileinfo = format ("%1:%2:%3: ", tk->file, tk->line, tk->column);
-	}
-
-	throw std::runtime_error (fileinfo + msg);
-}
--- a/src/Format.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,231 +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 "String.h"
-#include "Containers.h"
-
-class FormatArgument
-{
-	public:
-		FormatArgument (const String& a) : m_text (a) {}
-		FormatArgument (char a) : m_text (a) {}
-		FormatArgument (int a) : m_text (String::fromNumber (a)) {}
-		FormatArgument (long a) : m_text (String::fromNumber (a)) {}
-		FormatArgument (const char* a) : m_text (a) {}
-
-		FormatArgument (void* a)
-		{
-			m_text.sprintf ("%p", a);
-		}
-
-		FormatArgument (const void* a)
-		{
-			m_text.sprintf ("%p", a);
-		}
-
-		template<class T> FormatArgument (const List<T>& list)
-		{
-			if (list.isEmpty())
-			{
-				m_text = "{}";
-				return;
-			}
-
-			m_text = "{ ";
-
-			for (const T& a : list)
-			{
-				if (&a != &list[0])
-					m_text += ", ";
-
-				m_text += FormatArgument (a).text();
-			}
-
-			m_text += " }";
-		}
-
-		inline const String& text() const
-		{
-			return m_text;
-		}
-
-	private:
-		String m_text;
-};
-
-#ifndef IN_IDE_PARSER
-# ifdef DEBUG
-#  define devf(...) PrintTo (stderr, __VA_ARGS__)
-#  define dvalof( A ) PrintTo (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
-
-
-/**
- * Formats the given string with the given args.
- *
- * @param fmtstr Formatter string to process.
- * @param args Args to format with the string.
- * @see format()
- */
-String formatArgs (const String& fmtstr, const std::vector<String>& args);
-
-/**
- * Expands the given arguments into a vector of strings.
- *
- * @param data Where to insert the strings.
- * @param arg First argument to process
- * @param rest... Rest of the arguments.
- */
-template<typename T, typename... RestTypes>
-void expandFormatArguments (std::vector<String>& data, const T& arg, const RestTypes& ... rest)
-{
-	data.push_back (FormatArgument (arg).text());
-	expandFormatArguments (data, rest...);
-}
-
-/**
- * This is an overload of @c ExpandFormatArguments for end-of-args support.
- */
-static void expandFormatArguments (std::vector<String>& data) __attribute__ ( (unused));
-static void expandFormatArguments (std::vector<String>& data)
-{
-	(void) data;
-}
-
-/**
- * Formats the given formatter string and args and returns the string.
- * This is essentially a modernized sprintf.
- *
- * Args in the format string take the form %n where n is a digit. The argument
- * will be expanded to the nth argument passed. This is essentially Qt's
- * QString::arg() syntax. Note: %0 is invalid.
- *
- * Arguments can be passed a modifier which takes the form of a character
- * just before the digit. Currently supported modifiers are s, d and x.
- *
- * - s: The argument will expand into "s" if it would've not expanded into "1"
- *      otherwise. If it would have expanded into "1" it will expand into an
- *      empty string.
- *
- * - d: The argument expands into the numeric form of the first character of
- *      its previous expansion. Use this to get numeric forms of @c char
- *      arguments.
- *
- * - x: The numeric argument will be represented in hexadecimal notation. This
- *      will work if the argument is a string representing a number. If the
- *      argument did not expand into a number in the first place, 0 is used
- *      (and 0x0 is printed).
- *
- * @param fmtstr Formatter string to process
- * @param raw_args Arguments for the formatter string.
- * @return the formatted string.
- * @see Print
- * @see PrintTo
- */
-template<typename... argtypes>
-String format (const String& fmtstr, const argtypes&... raw_args)
-{
-	std::vector<String> args;
-	expandFormatArguments (args, raw_args...);
-	assert (args.size() == sizeof... (raw_args));
-	return formatArgs (fmtstr, args);
-}
-
-/**
- * This is an overload of @c format where no arguments are supplied.
- * @return the formatter string as-is.
- */
-static String format (const String& fmtstr) __attribute__ ( (unused));
-static String format (const String& fmtstr)
-{
-	return fmtstr;
-}
-
-/**
- * Processes the given formatter string using @c format and prints it to the
- * specified file pointer.
- *
- * @param fp File pointer to print the formatted string to
- * @param fmtstr Formatter string for @c format
- * @param args Arguments for @c fmtstr
- */
-template<typename... argtypes>
-void printTo (FILE* fp, const String& fmtstr, const argtypes&... args)
-{
-	fprintf (fp, "%s", format (fmtstr, args...).c_str());
-}
-
-/**
- * Processes the given formatter string using @c format and prints the result to
- * @c stdout.
- *
- * @param fmtstr Formatter string for @c format
- * @param args Arguments for @c fmtstr
- */
-template<typename... argtypes>
-void print (const String& fmtstr, const argtypes&... args)
-{
-	printTo (stdout, fmtstr, args...);
-}
-
-/**
- * Throws an std::runtime_error with the processed formatted string. The program
- * execution terminates after a call to this function as the exception is first
- * caught in @c main which prints the error to stderr and then exits.
- *
- * @param fmtstr The formatter string of the error.
- * @param args The args to the formatter string.
- * @see Format
- */
-template<typename... argtypes>
-void error (const String& fmtstr, const argtypes&... args)
-{
-	error (format (fmtstr, args...));
-}
-
-/**
- * An overload of @c Error with no string formatting in between.
- *
- * @param msg The error message.
- */
-void error (String msg);
-
-#endif // BOTC_FORMAT_H
--- a/src/Lexer.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,344 +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 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() ==TK_Hash)
-		{
-			mustGetFromScanner (sc,TK_Symbol);
-
-			if (sc.getTokenText() == "include")
-			{
-				mustGetFromScanner (sc,TK_String);
-				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
-		{
-			TokenInfo 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),
-			// 	GetTokenTypeString (tok.type));
-
-			m_tokens << tok;
-		}
-	}
-
-	m_tokenPosition = m_tokens.begin() - 1;
-	gFileNameStack.removeOne (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), versionString (false));
-
-	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::next (ETokenType req)
-{
-	Iterator pos = m_tokenPosition;
-
-	if (m_tokens.isEmpty())
-		return false;
-
-	m_tokenPosition++;
-
-	if (isAtEnd() || (req !=TK_Any && tokenType() != req))
-	{
-		m_tokenPosition = pos;
-		return false;
-	}
-
-	return true;
-}
-
-// =============================================================================
-//
-void Lexer::mustGetNext (ETokenType tok)
-{
-	if (!next())
-		error ("unexpected EOF");
-
-	if (tok !=TK_Any)
-		tokenMustBe (tok);
-}
-
-// =============================================================================
-// eugh..
-//
-void Lexer::mustGetFromScanner (LexerScanner& sc, ETokenType tt)
-{
-	if (sc.getNextToken() == false)
-		error ("unexpected EOF");
-
-	if (tt != TK_Any && sc.getTokenType() != tt)
-	{
-		// TODO
-		TokenInfo 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<ETokenType>& toks)
-{
-	if (!next())
-		error ("unexpected EOF");
-
-	for (ETokenType tok : toks)
-		if (tokenType() == tok)
-			return;
-
-	String toknames;
-
-	for (const ETokenType& tokType : toks)
-	{
-		if (&tokType == &toks.last())
-			toknames += " or ";
-		elif (toknames.isEmpty() == false)
-			toknames += ", ";
-
-		toknames += describeTokenType (tokType);
-	}
-
-	error ("expected %1, got %2", toknames, describeToken (token()));
-}
-
-// =============================================================================
-//
-int Lexer::getOneSymbol (const StringList& syms)
-{
-	if (!next())
-		error ("unexpected EOF");
-
-	if (tokenType() ==TK_Symbol)
-	{
-		for (int i = 0; i < syms.size(); ++i)
-		{
-			if (syms[i] == token()->text)
-				return i;
-		}
-	}
-
-	error ("expected one of %1, got %2", syms, describeToken (token()));
-	return -1;
-}
-
-// =============================================================================
-//
-void Lexer::tokenMustBe (ETokenType tok)
-{
-	if (tokenType() != tok)
-		error ("expected %1, got %2", describeTokenType (tok),
-			describeToken (token()));
-}
-
-// =============================================================================
-//
-String Lexer::describeTokenPrivate (ETokenType tokType, Lexer::TokenInfo* tok)
-{
-	if (tokType <gLastNamedToken)
-		return "\"" + LexerScanner::getTokenString (tokType) + "\"";
-
-	switch (tokType)
-	{
-		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::peekNext (Lexer::TokenInfo* tk)
-{
-	Iterator pos = m_tokenPosition;
-	bool r = next();
-
-	if (r && tk != null)
-		*tk = *m_tokenPosition;
-
-	m_tokenPosition = pos;
-	return r;
-}
-
-// =============================================================================
-//
-bool Lexer::peekNextType (ETokenType req)
-{
-	Iterator pos = m_tokenPosition;
-	bool result = false;
-
-	if (next() && tokenType() == req)
-		result = true;
-
-	m_tokenPosition = pos;
-	return result;
-}
-
-// =============================================================================
-//
-Lexer* Lexer::getCurrentLexer()
-{
-	return gMainLexer;
-}
-
-// =============================================================================
-//
-String Lexer::peekNextString (int a)
-{
-	if (m_tokenPosition + a >= m_tokens.end())
-		return "";
-
-	Iterator oldpos = m_tokenPosition;
-	m_tokenPosition += a;
-	String result = token()->text;
-	m_tokenPosition = oldpos;
-	return result;
-}
-
-// =============================================================================
-//
-String Lexer::describeCurrentPosition()
-{
-	return token()->file + ":" + token()->line;
-}
-
-// =============================================================================
-//
-String Lexer::describeTokenPosition()
-{
-	return format ("%1 / %2", m_tokenPosition - m_tokens.begin(), m_tokens.size());
-}
-
-// =============================================================================
-//
-void Lexer::mustGetSymbol (const String& a)
-{
-	mustGetNext (TK_Any);
-	if (token()->text != a)
-		error ("expected \"%1\", got \"%2\"", a, token()->text);
-}
--- a/src/Lexer.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +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 "LexerScanner.h"
-
-class Lexer
-{
-public:
-	struct TokenInfo
-	{
-		ETokenType	type;
-		String		text;
-		String		file;
-		int			line;
-		int			column;
-	};
-
-	using TokenList	= List<TokenInfo>;
-	using Iterator	= TokenList::Iterator;
-
-public:
-	Lexer();
-	~Lexer();
-
-	void	processFile (String fileName);
-	bool	next (ETokenType req = TK_Any);
-	void	mustGetNext (ETokenType tok);
-	void	mustGetAnyOf (const List<ETokenType>& toks);
-	void	mustGetSymbol (const String& a);
-	int		getOneSymbol (const StringList& syms);
-	void	tokenMustBe (ETokenType tok);
-	bool	peekNext (TokenInfo* tk = null);
-	bool	peekNextType (ETokenType req);
-	String	peekNextString (int a = 1);
-	String	describeCurrentPosition();
-	String	describeTokenPosition();
-
-	static Lexer* getCurrentLexer();
-
-	inline bool hasValidToken() const
-	{
-		return (m_tokenPosition < m_tokens.end() && m_tokenPosition >= m_tokens.begin());
-	}
-
-	inline TokenInfo* token() const
-	{
-		assert (hasValidToken() == true);
-		return &(*m_tokenPosition);
-	}
-
-	inline bool isAtEnd() const
-	{
-		return m_tokenPosition == m_tokens.end();
-	}
-
-	inline ETokenType tokenType() const
-	{
-		return token()->type;
-	}
-
-	inline void skip (int a = 1)
-	{
-		m_tokenPosition += a;
-	}
-
-	inline int position()
-	{
-		return m_tokenPosition - m_tokens.begin();
-	}
-
-	inline void setPosition (int pos)
-	{
-		m_tokenPosition = m_tokens.begin() + pos;
-	}
-
-	// If @tok is given, describes the token. If not, describes @tok_type.
-	static inline String describeTokenType (ETokenType toktype)
-	{
-		return describeTokenPrivate (toktype, null);
-	}
-
-	static inline String describeToken (TokenInfo* tok)
-	{
-		return describeTokenPrivate (tok->type, tok);
-	}
-
-private:
-	TokenList		m_tokens;
-	Iterator		m_tokenPosition;
-
-	// read a mandatory token from scanner
-	void mustGetFromScanner (LexerScanner& sc, ETokenType tt =TK_Any);
-	void checkFileHeader (LexerScanner& sc);
-
-	static String describeTokenPrivate (ETokenType tok_type, TokenInfo* tok);
-};
-
-#endif // BOTC_LEXER_H
--- a/src/LexerScanner.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +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 "LexerScanner.h"
-#include "Lexer.h"
-
-static const String gTokenStrings[] =
-{
-	"<<=",
-	">>=",
-	"==",
-	"!=",
-	"+=",
-	"-=",
-	"*=",
-	"/=",
-	"%=",
-	"<<",
-	">>",
-	">=",
-	"<=",
-	"&&",
-	"||",
-	"++",
-	"--",
-	"'",
-	"$",
-	"(",
-	")",
-	"[",
-	"]",
-	"{",
-	"}",
-	"=",
-	"+",
-	"-",
-	"*",
-	"/",
-	"%",
-	",",
-	"<",
-	">",
-	".",
-	":",
-	";",
-	"#",
-	"!",
-	"&",
-	"|",
-	"^",
-	"?",
-	"->",
-	"bool",
-	"break",
-	"case",
-	"continue",
-	"const",
-	"constexpr",
-	"default",
-	"do",
-	"else",
-	"event",
-	"eventdef",
-	"for",
-	"funcdef",
-	"if",
-	"int",
-	"mainloop",
-	"onenter",
-	"onexit",
-	"state",
-	"switch",
-	"str",
-	"using",
-	"var",
-	"void",
-	"while",
-	"true",
-	"false",
-	"enum",
-	"func",
-	"return",
-};
-
-static_assert (countof (gTokenStrings) == (int)gLastNamedToken + 1,
-	"Count of gTokenStrings is not the same as the amount of named token identifiers.");
-
-// =============================================================================
-//
-LexerScanner::LexerScanner (FILE* fp) :
-	m_line (1)
-{
-	long fsize, bytes;
-
-	fseek (fp, 0l, SEEK_END);
-	fsize = ftell (fp);
-	rewind (fp);
-	m_data = new char[fsize];
-	m_position = m_lineBreakPosition = &m_data[0];
-	bytes = fread (m_data, 1, fsize, fp);
-	assert (bytes >= fsize);
-}
-
-// =============================================================================
-//
-LexerScanner::~LexerScanner()
-{
-	delete m_data;
-}
-
-// =============================================================================
-//
-bool LexerScanner::checkString (const char* c, int flags)
-{
-	bool r = strncmp (m_position, c, strlen (c)) == 0;
-
-	// There is to be a non-symbol character after words
-	if (r && (flags & FCheckWord) && isSymbolChar (m_position[strlen (c)], true))
-		r = false;
-
-	// Advance the cursor unless we want to just peek
-	if (r && !(flags & FCheckPeek))
-		m_position += strlen (c);
-
-	return r;
-}
-
-// =============================================================================
-//
-bool LexerScanner::getNextToken()
-{
-	m_tokenText = "";
-
-	while (isspace (*m_position))
-		skip();
-
-	// Check for comments
-	if (strncmp (m_position, "//", 2) == 0)
-	{
-		m_position += 2;
-
-		while (*m_position != '\n')
-			skip();
-
-		return getNextToken();
-	}
-	elif (strncmp (m_position, "/*", 2) == 0)
-	{
-		skip (2); // skip the start symbols
-
-		while (strncmp (m_position, "*/", 2) != 0)
-			skip();
-
-		skip (2); // skip the end symbols
-		return getNextToken();
-	}
-
-	if (*m_position == '\0')
-		return false;
-
-	// Check tokens
-	for (int i = 0; i < countof (gTokenStrings); ++i)
-	{
-		int flags = 0;
-
-		if (i >= gFirstNamedToken)
-			flags |= FCheckWord;
-
-		if (checkString (gTokenStrings[i], flags))
-		{
-			m_tokenText = gTokenStrings[i];
-			m_tokenType = (ETokenType) i;
-			return true;
-		}
-	}
-
-	// Check and parse string
-	if (*m_position == '\"')
-	{
-		m_position++;
-
-		while (*m_position != '\"')
-		{
-			if (!*m_position)
-				error ("unterminated string");
-
-			if (checkString ("\\n"))
-			{
-				m_tokenText += '\n';
-				continue;
-			}
-			elif (checkString ("\\t"))
-			{
-				m_tokenText += '\t';
-				continue;
-			}
-			elif (checkString ("\\\""))
-			{
-				m_tokenText += '"';
-				continue;
-			}
-
-			m_tokenText += *m_position++;
-		}
-
-		m_tokenType =TK_String;
-		skip(); // skip the final quote
-		return true;
-	}
-
-	if (isdigit (*m_position))
-	{
-		while (isdigit (*m_position))
-			m_tokenText += *m_position++;
-
-		m_tokenType =TK_Number;
-		return true;
-	}
-
-	if (isSymbolChar (*m_position, false))
-	{
-		m_tokenType =TK_Symbol;
-
-		do
-		{
-			if (!isSymbolChar (*m_position, true))
-				break;
-
-			m_tokenText += *m_position++;
-		} while (*m_position != '\0');
-
-		return true;
-	}
-
-	error ("unknown character \"%1\"", *m_position);
-	return false;
-}
-
-// =============================================================================
-//
-void LexerScanner::skip()
-{
-	if (*m_position == '\n')
-	{
-		m_line++;
-		m_lineBreakPosition = m_position;
-	}
-
-	m_position++;
-}
-
-// =============================================================================
-//
-void LexerScanner::skip (int chars)
-{
-	for (int i = 0; i < chars; ++i)
-		skip();
-}
-
-// =============================================================================
-//
-String LexerScanner::getTokenString (ETokenType a)
-{
-	assert ((int) a <= gLastNamedToken);
-	return gTokenStrings[a];
-}
-
-// =============================================================================
-//
-String LexerScanner::readLine()
-{
-	String line;
-
-	while (*m_position != '\n')
-		line += *(m_position++);
-
-	return line;
-}
--- a/src/LexerScanner.h	Sun Mar 30 21:35:06 2014 +0300
+++ /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 BOTC_LEXER_SCANNER_H
-#define BOTC_LEXER_SCANNER_H
-
-#include <climits>
-#include "Main.h"
-
-class LexerScanner
-{
-	public:
-		struct PositionInfo
-		{
-			char*	pos;
-			int		line;
-		};
-
-		// Flags for check_string()
-		enum
-		{
-			FCheckWord = (1 << 0),   // must be followed by whitespace
-			FCheckPeek = (1 << 1),   // don't advance cursor
-		};
-
-		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 m_tokenText;
-		}
-
-		inline int getLine() const
-		{
-			return m_line;
-		}
-
-		inline int getColumn() const
-		{
-			return m_position - m_lineBreakPosition;
-		}
-
-		inline ETokenType getTokenType() const
-		{
-			return m_tokenType;
-		}
-
-		static String getTokenString (ETokenType a);
-
-	private:
-		char*			m_data;
-		char*			m_position;
-		char*			m_lineBreakPosition;
-		String			m_tokenText,
-						m_lastToken;
-		ETokenType		m_tokenType;
-		int				m_line;
-
-		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
--- a/src/Macros.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +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.
-*/
-
-#pragma once
-
-#if !defined (__cplusplus) || __cplusplus < 201103L
-# error botc requires a C++11-compliant compiler to be built
-#endif
-
-// 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 named_enum enum
-#define elif else if
-#define types public
-#define countof(A) ((int) (sizeof A / sizeof *A))
-
-#ifndef __GNUC__
-# define __attribute__(X)
-#endif
-
-#define deprecated __attribute__ ((deprecated))
\ No newline at end of file
--- a/src/Main.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +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 "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->signature());
-
-			print ("------------------------------------------------------\n");
-			print ("End of command list\n");
-			exit (0);
-		}
-
-		// Print header
-		String header;
-		String headerline;
-		header = format (APPNAME " version %1", versionString (true));
-
-#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 = parser->getHighestVarIndex (true) + 1;
-		int statelocalcount = parser->getHighestVarIndex (false) + 1;
-		int stringcount = countStringsInTable();
-		print ("%1 / %2 strings\n", stringcount, gMaxStringlistSize);
-		print ("%1 / %2 global variable indices\n", globalcount, gMaxGlobalVars);
-		print ("%1 / %2 state variable indices\n", statelocalcount, gMaxGlobalVars);
-		print ("%1 / %2 events\n", parser->numEvents(), gMaxEvents);
-		print ("%1 state%s1\n", parser->numStates());
-
-		parser->writeToFile (outfile);
-		delete parser;
-		return 0;
-	}
-	catch (std::exception& e)
-	{
-		fprintf (stderr, "error: %s\n", e.what());
-		return 1;
-	}
-}
-
-// ============================================================================
-//
-// 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;
-}
-
-// ============================================================================
-//
-DataType getTypeByName (String token)
-{
-	token = token.toLowercase();
-	return	(token == "int") ? TYPE_Int :
-			(token == "str") ? TYPE_String :
-			(token == "void") ? TYPE_Void :
-			(token == "bool") ? TYPE_Bool :
-			TYPE_Unknown;
-}
-
-
-// ============================================================================
-//
-// Inverse operation - type name by value
-//
-String dataTypeName (DataType type)
-{
-	switch (type)
-	{
-		case TYPE_Int: return "int"; break;
-		case TYPE_String: return "str"; break;
-		case TYPE_Void: return "void"; break;
-		case TYPE_Bool: return "bool"; break;
-		case TYPE_Unknown: 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 versionString (bool longform)
-{
-	String tag (GIT_DESCRIPTION);
-	String version = tag;
-
-	if (longform && tag.endsWith ("-pre"))
-		version += "-" + String (GIT_HASH).mid (0, 8);
-
-	return version;
-}
--- a/src/Main.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +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
-
-#include <cstdio>
-#include <cstdarg>
-#include <cstdint>
-#include "Macros.h"
-#include "Property.h"
-#include "Types.h"
-#include "Containers.h"
-#include "String.h"
-#include "Format.h"
-#include "BotStuff.h"
-#include "Tokens.h"
-
-String makeObjectFileName (String s);
-DataType getTypeByName (String token);
-String dataTypeName (DataType type);
-String versionString (bool longform);
-String makeVersionString (int major, int minor, int patch);
-
-template<typename T>
-inline T max (T a, T b)
-{
-	return a > b ? a : b;
-}
-
-template<typename T>
-inline T min (T a, T b)
-{
-	return a < b ? a : b;
-}
-
-#endif // BOTC_MAIN_H
--- a/src/Parser.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1417 +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 "Containers.h"
-#include "Lexer.h"
-#include "DataBuffer.h"
-#include "Expression.h"
-
-#define SCOPE(n) (m_scopeStack[m_scopeCursor - n])
-
-static const StringList g_validZandronumVersions = {"1.2", "1.3", "2.0"};
-
-// ============================================================================
-//
-BotscriptParser::BotscriptParser() :
-	m_isReadOnly (false),
-	m_mainBuffer (new DataBuffer),
-	m_onenterBuffer (new DataBuffer),
-	m_mainLoopBuffer (new DataBuffer),
-	m_lexer (new Lexer),
-	m_numStates (0),
-	m_numEvents (0),
-	m_currentMode (PARSERMODE_TopLevel),
-	m_isStateSpawnDefined (false),
-	m_gotMainLoop (false),
-	m_scopeCursor (-1),
-	m_isElseAllowed (false),
-	m_highestGlobalVarIndex (0),
-	m_highestStateVarIndex (0),
-	m_zandronumVersion (10200), // 1.2
-	m_defaultZandronumVersion (true) {}
-
-// ============================================================================
-//
-BotscriptParser::~BotscriptParser()
-{
-	delete m_lexer;
-}
-
-// ============================================================================
-//
-void BotscriptParser::checkToplevel()
-{
-	if (m_currentMode != PARSERMODE_TopLevel)
-		error ("%1-statements may only be defined at top level!", getTokenString());
-}
-
-// ============================================================================
-//
-void BotscriptParser::checkNotToplevel()
-{
-	if (m_currentMode == PARSERMODE_TopLevel)
-		error ("%1-statements must not be defined at top level!", getTokenString());
-}
-
-// ============================================================================
-//
-// Main compiler 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
-	m_lexer->processFile (fileName);
-	pushScope();
-
-	while (m_lexer->next())
-	{
-		// Check if else is potentically valid
-		if (tokenIs (TK_Else) && m_isElseAllowed == false)
-			error ("else without preceding if");
-
-		if (tokenIs (TK_Else) == false)
-			m_isElseAllowed = false;
-
-		switch (m_lexer->token()->type)
-		{
-			case TK_State:
-				parseStateBlock();
-				break;
-
-			case TK_Event:
-				parseEventBlock();
-				break;
-
-			case TK_Mainloop:
-				parseMainloop();
-				break;
-
-			case TK_Onenter:
-			case TK_Onexit:
-				parseOnEnterExit();
-				break;
-
-			case TK_Var:
-				parseVar();
-				break;
-
-			case TK_If:
-				parseIf();
-				break;
-
-			case TK_Else:
-				parseElse();
-				break;
-
-			case TK_While:
-				parseWhileBlock();
-				break;
-
-			case TK_For:
-				parseForBlock();
-				break;
-
-			case TK_Do:
-				parseDoBlock();
-				break;
-
-			case TK_Switch:
-				parseSwitchBlock();
-				break;
-
-			case TK_Case:
-				parseSwitchCase();
-				break;
-
-			case TK_Default:
-				parseSwitchDefault();
-				break;
-
-			case TK_Break:
-				parseBreak();
-				break;
-
-			case TK_Continue:
-				parseContinue();
-				break;
-
-			case TK_BraceEnd:
-				parseBlockEnd();
-				break;
-
-			case TK_Eventdef:
-				parseEventdef();
-				break;
-
-			case TK_Funcdef:
-				parseFuncdef();
-				break;
-
-			case TK_Semicolon:
-				break;
-
-			case TK_Using:
-				parseUsing();
-				break;
-
-			default:
-			{
-				// Check if it's a command
-				CommandInfo* comm = findCommandByName (getTokenString());
-
-				if (comm)
-				{
-					currentBuffer()->mergeAndDestroy (parseCommand (comm));
-					m_lexer->mustGetNext (TK_Semicolon);
-					continue;
-				}
-
-				// If nothing else, parse it as a statement
-				m_lexer->skip (-1);
-				DataBuffer* b = parseStatement();
-
-				if (b == false)
-				{
-					m_lexer->next();
-					error ("unknown token `%1`", getTokenString());
-				}
-
-				currentBuffer()->mergeAndDestroy (b);
-				m_lexer->mustGetNext (TK_Semicolon);
-				break;
-			}
-		}
-	}
-
-	// ===============================================================================
-	// Script file ended. Do some last checks and write the last things to main buffer
-	if (m_currentMode != PARSERMODE_TopLevel)
-		error ("script did not end at top level; a `}` is missing somewhere");
-
-	if (isReadOnly() == false)
-	{
-		// stateSpawn must be defined!
-		if (m_isStateSpawnDefined == false)
-			error ("script must have a state named `stateSpawn`!");
-
-		if (m_defaultZandronumVersion)
-		{
-			print ("\n");
-			print ("note: use the 'using' directive to define a target Zandronum version\n");
-			print ("usage: using zandronum <version>, possible versions: %1\n", g_validZandronumVersions);
-			print ("\n");
-		}
-
-		// Dump the last state's onenter and mainloop
-		writeMemberBuffers();
-
-		// String table
-		writeStringTable();
-	}
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseStateBlock()
-{
-	checkToplevel();
-	m_lexer->mustGetNext (TK_String);
-	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.toLowercase() == "statespawn")
-		m_isStateSpawnDefined = true;
-
-	// Must end in a colon
-	m_lexer->mustGetNext (TK_Colon);
-
-	// write the previous state's onenter and
-	// mainloop buffers to file now
-	if (m_currentState.isEmpty() == false)
-		writeMemberBuffers();
-
-	currentBuffer()->writeDWord (DH_StateName);
-	currentBuffer()->writeString (statename);
-	currentBuffer()->writeDWord (DH_StateIndex);
-	currentBuffer()->writeDWord (m_numStates);
-
-	m_numStates++;
-	m_currentState = statename;
-	m_gotMainLoop = false;
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseEventBlock()
-{
-	checkToplevel();
-	m_lexer->mustGetNext (TK_String);
-
-	EventDefinition* e = findEventByName (getTokenString());
-
-	if (e == null)
-		error ("bad event, got `%1`\n", getTokenString());
-
-	m_lexer->mustGetNext (TK_BraceStart);
-	m_currentMode = PARSERMODE_Event;
-	currentBuffer()->writeDWord (DH_Event);
-	currentBuffer()->writeDWord (e->number);
-	m_numEvents++;
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseMainloop()
-{
-	checkToplevel();
-	m_lexer->mustGetNext (TK_BraceStart);
-
-	m_currentMode = PARSERMODE_MainLoop;
-	m_mainLoopBuffer->writeDWord (DH_MainLoop);
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseOnEnterExit()
-{
-	checkToplevel();
-	bool onenter = (tokenIs (TK_Onenter));
-	m_lexer->mustGetNext (TK_BraceStart);
-
-	m_currentMode = onenter ? PARSERMODE_Onenter : PARSERMODE_Onexit;
-	currentBuffer()->writeDWord (onenter ? DH_OnEnter : DH_OnExit);
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseVar()
-{
-	Variable* var = new Variable;
-	var->origin = m_lexer->describeCurrentPosition();
-	var->isarray = false;
-	const bool isconst = m_lexer->next (TK_Const);
-	m_lexer->mustGetAnyOf ({TK_Int,TK_Str,TK_Void});
-
-	DataType vartype =	(tokenIs (TK_Int)) ? TYPE_Int :
-					(tokenIs (TK_Str)) ? TYPE_String :
-					TYPE_Bool;
-
-	m_lexer->mustGetNext (TK_DollarSign);
-	m_lexer->mustGetNext (TK_Symbol);
-	String name = getTokenString();
-
-	if (m_lexer->next (TK_BracketStart))
-	{
-		m_lexer->mustGetNext (TK_BracketEnd);
-		var->isarray = true;
-
-		if (isconst)
-			error ("arrays cannot be const");
-	}
-
-	for (Variable* var : SCOPE(0).globalVariables + SCOPE(0).localVariables)
-	{
-		if (var->name == name)
-			error ("Variable $%1 is already declared on this scope; declared at %2",
-				var->name, var->origin);
-	}
-
-	var->name = name;
-	var->statename = "";
-	var->type = vartype;
-
-	if (isconst == false)
-	{
-		var->writelevel = WRITE_Mutable;
-	}
-	else
-	{
-		m_lexer->mustGetNext (TK_Assign);
-		Expression expr (this, m_lexer, vartype);
-
-		// If the expression was constexpr, we know its value and thus
-		// can store it in the variable.
-		if (expr.getResult()->isConstexpr())
-		{
-			var->writelevel = WRITE_Constexpr;
-			var->value = expr.getResult()->value();
-		}
-		else
-		{
-			// TODO: might need a VM-wise oninit for this...
-			error ("const variables must be constexpr");
-		}
-	}
-
-	// Assign an index for the variable if it is not constexpr. Constexpr
-	// variables can simply be substituted out for their value when used
-	// so they need no index.
-	if (var->writelevel != WRITE_Constexpr)
-	{
-		bool isglobal = isInGlobalState();
-		var->index = isglobal ? SCOPE(0).globalVarIndexBase++ : SCOPE(0).localVarIndexBase++;
-
-		if ((isglobal == true && var->index >= gMaxGlobalVars) ||
-			(isglobal == false && var->index >= gMaxStateVars))
-		{
-			error ("too many %1 variables", isglobal ? "global" : "state-local");
-		}
-	}
-
-	if (isInGlobalState())
-		SCOPE(0).globalVariables << var;
-	else
-		SCOPE(0).localVariables << var;
-
-	suggestHighestVarIndex (isInGlobalState(), var->index);
-	m_lexer->mustGetNext (TK_Semicolon);
-	print ("Declared %3 variable #%1 $%2\n", var->index, var->name, isInGlobalState() ? "global" : "state-local");
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseIf()
-{
-	checkNotToplevel();
-	pushScope();
-
-	// Condition
-	m_lexer->mustGetNext (TK_ParenStart);
-
-	// Read the expression and write it.
-	DataBuffer* c = parseExpression (TYPE_Int);
-	currentBuffer()->mergeAndDestroy (c);
-
-	m_lexer->mustGetNext (TK_ParenEnd);
-	m_lexer->mustGetNext (TK_BraceStart);
-
-	// Add a mark - to here temporarily - and add a reference to it.
-	// Upon a closing brace, the mark will be adjusted.
-	ByteMark* mark = currentBuffer()->addMark ("");
-
-	// Use DH_IfNotGoto - if the expression is not true, we goto the mark
-	// we just defined - and this mark will be at the end of the scope block.
-	currentBuffer()->writeDWord (DH_IfNotGoto);
-	currentBuffer()->addReference (mark);
-
-	// Store it
-	SCOPE (0).mark1 = mark;
-	SCOPE (0).type = SCOPE_If;
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseElse()
-{
-	checkNotToplevel();
-	m_lexer->mustGetNext (TK_BraceStart);
-	pushScope (eNoReset);
-
-	if (SCOPE (0).type != SCOPE_If)
-		error ("else without preceding if");
-
-	// write down to jump to the end of the else statement
-	// Otherwise we have fall-throughs
-	SCOPE (0).mark2 = currentBuffer()->addMark ("");
-
-	// Instruction to jump to the end after if block is complete
-	currentBuffer()->writeDWord (DH_Goto);
-	currentBuffer()->addReference (SCOPE (0).mark2);
-
-	// Move the ifnot mark here and set type to else
-	currentBuffer()->adjustMark (SCOPE (0).mark1);
-	SCOPE (0).type = SCOPE_Else;
-}
-
-// ============================================================================
-//
-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 = currentBuffer()->addMark (""); // start
-	ByteMark* mark2 = currentBuffer()->addMark (""); // end
-
-	// Condition
-	m_lexer->mustGetNext (TK_ParenStart);
-	DataBuffer* expr = parseExpression (TYPE_Int);
-	m_lexer->mustGetNext (TK_ParenEnd);
-	m_lexer->mustGetNext (TK_BraceStart);
-
-	// write condition
-	currentBuffer()->mergeAndDestroy (expr);
-
-	// Instruction to go to the end if it fails
-	currentBuffer()->writeDWord (DH_IfNotGoto);
-	currentBuffer()->addReference (mark2);
-
-	// Store the needed stuff
-	SCOPE (0).mark1 = mark1;
-	SCOPE (0).mark2 = mark2;
-	SCOPE (0).type = SCOPE_While;
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseForBlock()
-{
-	checkNotToplevel();
-	pushScope();
-
-	// Initializer
-	m_lexer->mustGetNext (TK_ParenStart);
-	DataBuffer* init = parseStatement();
-
-	if (init == null)
-		error ("bad statement for initializer of for");
-
-	m_lexer->mustGetNext (TK_Semicolon);
-
-	// Condition
-	DataBuffer* cond = parseExpression (TYPE_Int);
-
-	if (cond == null)
-		error ("bad statement for condition of for");
-
-	m_lexer->mustGetNext (TK_Semicolon);
-
-	// Incrementor
-	DataBuffer* incr = parseStatement();
-
-	if (incr == null)
-		error ("bad statement for incrementor of for");
-
-	m_lexer->mustGetNext (TK_ParenEnd);
-	m_lexer->mustGetNext (TK_BraceStart);
-
-	// First, write out the initializer
-	currentBuffer()->mergeAndDestroy (init);
-
-	// Init two marks
-	ByteMark* mark1 = currentBuffer()->addMark ("");
-	ByteMark* mark2 = currentBuffer()->addMark ("");
-
-	// Add the condition
-	currentBuffer()->mergeAndDestroy (cond);
-	currentBuffer()->writeDWord (DH_IfNotGoto);
-	currentBuffer()->addReference (mark2);
-
-	// Store the marks and incrementor
-	SCOPE (0).mark1 = mark1;
-	SCOPE (0).mark2 = mark2;
-	SCOPE (0).buffer1 = incr;
-	SCOPE (0).type = SCOPE_For;
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseDoBlock()
-{
-	checkNotToplevel();
-	pushScope();
-	m_lexer->mustGetNext (TK_BraceStart);
-	SCOPE (0).mark1 = currentBuffer()->addMark ("");
-	SCOPE (0).type = SCOPE_Do;
-}
-
-// ============================================================================
-//
-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();
-	m_lexer->mustGetNext (TK_ParenStart);
-	currentBuffer()->mergeAndDestroy (parseExpression (TYPE_Int));
-	m_lexer->mustGetNext (TK_ParenEnd);
-	m_lexer->mustGetNext (TK_BraceStart);
-	SCOPE (0).type = SCOPE_Switch;
-	SCOPE (0).mark1 = currentBuffer()->addMark (""); // end mark
-	SCOPE (0).buffer1 = null; // default header
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseSwitchCase()
-{
-	// case is only allowed inside switch
-	if (SCOPE (0).type != SCOPE_Switch)
-		error ("case label outside switch");
-
-	// Get a literal value for the case block. Zandronum does not support
-	// expressions here.
-	m_lexer->mustGetNext (TK_Number);
-	int num = m_lexer->token()->text.toLong();
-	m_lexer->mustGetNext (TK_Colon);
-
-	for (const CaseInfo& info : SCOPE(0).cases)
-		if (info.number == num)
-			error ("multiple case %1 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_switchBuffer = null;
-	currentBuffer()->writeDWord (DH_CaseGoto);
-	currentBuffer()->writeDWord (num);
-	addSwitchCase (null);
-	SCOPE (0).casecursor->number = num;
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseSwitchDefault()
-{
-	if (SCOPE (0).type != SCOPE_Switch)
-		error ("default label outside switch");
-
-	if (SCOPE (0).buffer1 != null)
-		error ("multiple default labels in one switch");
-
-	m_lexer->mustGetNext (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.
-	DataBuffer* buf = new DataBuffer;
-	SCOPE (0).buffer1 = buf;
-	buf->writeDWord (DH_Drop);
-	buf->writeDWord (DH_Goto);
-	addSwitchCase (buf);
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseBreak()
-{
-	if (m_scopeCursor == 0)
-		error ("unexpected `break`");
-
-	currentBuffer()->writeDWord (DH_Goto);
-
-	// switch and if use mark1 for the closing point,
-	// for and while use mark2.
-	switch (SCOPE (0).type)
-	{
-		case SCOPE_If:
-		case SCOPE_Switch:
-		{
-			currentBuffer()->addReference (SCOPE (0).mark1);
-		} break;
-
-		case SCOPE_For:
-		case SCOPE_While:
-		{
-			currentBuffer()->addReference (SCOPE (0).mark2);
-		} break;
-
-		default:
-		{
-			error ("unexpected `break`");
-		} break;
-	}
-
-	m_lexer->mustGetNext (TK_Semicolon);
-}
-
-// ============================================================================
-//
-void BotscriptParser::parseContinue()
-{
-	m_lexer->mustGetNext (TK_Semicolon);
-
-	int curs;
-	bool found = false;
-
-	// Fall through the scope until we find a loop block
-	for (curs = m_scopeCursor; curs > 0 && !found; curs--)
-	{
-		switch (m_scopeStack[curs].type)
-		{
-			case SCOPE_For:
-			case SCOPE_While:
-			case SCOPE_Do:
-			{
-				currentBuffer()->writeDWord (DH_Goto);
-				currentBuffer()->addReference (m_scopeStack[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 (m_scopeCursor > 0)
-	{
-		switch (SCOPE (0).type)
-		{
-			case SCOPE_If:
-			{
-				// Adjust the closing mark.
-				currentBuffer()->adjustMark (SCOPE (0).mark1);
-
-				// We're returning from `if`, thus `else` follow
-				m_isElseAllowed = true;
-				break;
-			}
-
-			case SCOPE_Else:
-			{
-				// else instead uses mark1 for itself (so if expression
-				// fails, jump to else), mark2 means end of else
-				currentBuffer()->adjustMark (SCOPE (0).mark2);
-				break;
-			}
-
-			case SCOPE_For:
-			{	// write the incrementor at the end of the loop block
-				currentBuffer()->mergeAndDestroy (SCOPE (0).buffer1);
-			}
-			case SCOPE_While:
-			{	// write down the instruction to go back to the start of the loop
-				currentBuffer()->writeDWord (DH_Goto);
-				currentBuffer()->addReference (SCOPE (0).mark1);
-
-				// Move the closing mark here since we're at the end of the while loop
-				currentBuffer()->adjustMark (SCOPE (0).mark2);
-				break;
-			}
-
-			case SCOPE_Do:
-			{
-				m_lexer->mustGetNext (TK_While);
-				m_lexer->mustGetNext (TK_ParenStart);
-				DataBuffer* expr = parseExpression (TYPE_Int);
-				m_lexer->mustGetNext (TK_ParenEnd);
-				m_lexer->mustGetNext (TK_Semicolon);
-
-				// If the condition runs true, go back to the start.
-				currentBuffer()->mergeAndDestroy (expr);
-				currentBuffer()->writeDWord (DH_IfGoto);
-				currentBuffer()->addReference (SCOPE (0).mark1);
-				break;
-			}
-
-			case SCOPE_Switch:
-			{
-				// Switch closes. Move down to the record buffer of
-				// the lower block.
-				if (SCOPE (1).casecursor != SCOPE (1).cases.begin() - 1)
-					m_switchBuffer = SCOPE (1).casecursor->data;
-				else
-					m_switchBuffer = null;
-
-				// If there was a default in the switch, write its header down now.
-				// If not, write instruction to jump to the end of switch after
-				// the headers (thus won't fall-through if no case matched)
-				if (SCOPE (0).buffer1)
-					currentBuffer()->mergeAndDestroy (SCOPE (0).buffer1);
-				else
-				{
-					currentBuffer()->writeDWord (DH_Drop);
-					currentBuffer()->writeDWord (DH_Goto);
-					currentBuffer()->addReference (SCOPE (0).mark1);
-				}
-
-				// Go through all of the buffers we
-				// recorded down and write them.
-				for (CaseInfo& info : SCOPE (0).cases)
-				{
-					currentBuffer()->adjustMark (info.mark);
-					currentBuffer()->mergeAndDestroy (info.data);
-				}
-
-				// Move the closing mark here
-				currentBuffer()->adjustMark (SCOPE (0).mark1);
-				break;
-			}
-
-			case SCOPE_Unknown:
-				break;
-		}
-
-		// Descend down the stack
-		m_scopeCursor--;
-		return;
-	}
-
-	int dataheader =	(m_currentMode == PARSERMODE_Event) ? DH_EndEvent :
-						(m_currentMode == PARSERMODE_MainLoop) ? DH_EndMainLoop :
-						(m_currentMode == PARSERMODE_Onenter) ? DH_EndOnEnter :
-						(m_currentMode == PARSERMODE_Onexit) ? DH_EndOnExit : -1;
-
-	if (dataheader == -1)
-		error ("unexpected `}`");
-
-	// Data header must be written before mode is changed because
-	// onenter and mainloop go into special buffers, and we want
-	// the closing data headers into said buffers too.
-	currentBuffer()->writeDWord (dataheader);
-	m_currentMode = PARSERMODE_TopLevel;
-	m_lexer->next (TK_Semicolon);
-}
-
-// =============================================================================
-//
-void BotscriptParser::parseEventdef()
-{
-	EventDefinition* e = new EventDefinition;
-
-	m_lexer->mustGetNext (TK_Number);
-	e->number = getTokenString().toLong();
-	m_lexer->mustGetNext (TK_Colon);
-	m_lexer->mustGetNext (TK_Symbol);
-	e->name = m_lexer->token()->text;
-	m_lexer->mustGetNext (TK_ParenStart);
-	m_lexer->mustGetNext (TK_ParenEnd);
-	m_lexer->mustGetNext (TK_Semicolon);
-	addEvent (e);
-}
-
-// =============================================================================
-//
-void BotscriptParser::parseFuncdef()
-{
-	CommandInfo* comm = new CommandInfo;
-	comm->origin = m_lexer->describeCurrentPosition();
-
-	// Return value
-	m_lexer->mustGetAnyOf ({TK_Int,TK_Void,TK_Bool,TK_Str});
-	comm->returnvalue = getTypeByName (m_lexer->token()->text); // TODO
-	assert (comm->returnvalue != -1);
-
-	// Number
-	m_lexer->mustGetNext (TK_Number);
-	comm->number = m_lexer->token()->text.toLong();
-	m_lexer->mustGetNext (TK_Colon);
-
-	// Name
-	m_lexer->mustGetNext (TK_Symbol);
-	comm->name = m_lexer->token()->text;
-
-	// Arguments
-	m_lexer->mustGetNext (TK_ParenStart);
-	comm->minargs = 0;
-
-	while (m_lexer->peekNextType (TK_ParenEnd) == false)
-	{
-		if (comm->args.isEmpty() == false)
-			m_lexer->mustGetNext (TK_Comma);
-
-		CommandArgument arg;
-		m_lexer->mustGetAnyOf ({TK_Int,TK_Bool,TK_Str});
-		DataType type = getTypeByName (m_lexer->token()->text); // TODO
-		assert (type != -1 && type != TYPE_Void);
-		arg.type = type;
-
-		m_lexer->mustGetNext (TK_Symbol);
-		arg.name = m_lexer->token()->text;
-
-		// If this is an optional parameter, we need the default value.
-		if (comm->minargs < comm->args.size() || m_lexer->peekNextType (TK_Assign))
-		{
-			m_lexer->mustGetNext (TK_Assign);
-
-			switch (type)
-			{
-				case TYPE_Int:
-				case TYPE_Bool:
-					m_lexer->mustGetNext (TK_Number);
-					break;
-
-				case TYPE_String:
-					error ("string arguments cannot have default values");
-
-				case TYPE_Unknown:
-				case TYPE_Void:
-					break;
-			}
-
-			arg.defvalue = m_lexer->token()->text.toLong();
-		}
-		else
-			comm->minargs++;
-
-		comm->args << arg;
-	}
-
-	m_lexer->mustGetNext (TK_ParenEnd);
-	m_lexer->mustGetNext (TK_Semicolon);
-	addCommandDefinition (comm);
-}
-
-// ============================================================================
-//
-// Parses a using statement
-//
-void BotscriptParser::parseUsing()
-{
-	checkToplevel();
-	m_lexer->mustGetSymbol ("zandronum");
-	String versionText;
-
-	while (m_lexer->next() && (m_lexer->tokenType() == TK_Number || m_lexer->tokenType() == TK_Dot))
-		versionText += getTokenString();
-
-	// Note: at this point the lexer's pointing at the token after the version.
-	if (versionText.isEmpty())
-		error ("expected version string, got `%1`", getTokenString());
-	if (g_validZandronumVersions.contains (versionText) == false)
-		error ("unknown version string `%2`: valid versions: `%1`\n", g_validZandronumVersions, versionText);
-
-	StringList versionTokens = versionText.split (".");
-	m_zandronumVersion = versionTokens[0].toLong() * 10000 + versionTokens[1].toLong() * 100;
-	m_defaultZandronumVersion = false;
-	m_lexer->tokenMustBe (TK_Semicolon);
-}
-
-// ============================================================================/
-//
-// Parses a command call
-//
-DataBuffer* BotscriptParser::parseCommand (CommandInfo* comm)
-{
-	DataBuffer* r = new DataBuffer (64);
-
-	if (m_currentMode == PARSERMODE_TopLevel && comm->returnvalue == TYPE_Void)
-		error ("command call at top level");
-
-	m_lexer->mustGetNext (TK_ParenStart);
-	m_lexer->mustGetNext (TK_Any);
-
-	int curarg = 0;
-
-	for (;;)
-	{
-		if (tokenIs (TK_ParenEnd))
-		{
-			if (curarg < comm->minargs)
-				error ("too few arguments passed to %1\n\tusage is: %2",
-					comm->name, comm->signature());
-
-			break;
-		}
-
-		if (curarg >= comm->args.size())
-			error ("too many arguments (%3) passed to %1\n\tusage is: %2",
-				comm->name, comm->signature());
-
-		r->mergeAndDestroy (parseExpression (comm->args[curarg].type, true));
-		m_lexer->mustGetNext (TK_Any);
-
-		if (curarg < comm->minargs - 1)
-		{
-			m_lexer->tokenMustBe (TK_Comma);
-			m_lexer->mustGetNext (TK_Any);
-		}
-		else if (curarg < comm->args.size() - 1)
-		{
-			// Can continue, but can terminate as well.
-			if (tokenIs (TK_ParenEnd))
-			{
-				curarg++;
-				break;
-			}
-			else
-			{
-				m_lexer->tokenMustBe (TK_Comma);
-				m_lexer->mustGetNext (TK_Any);
-			}
-		}
-
-		curarg++;
-	}
-
-	// If the script skipped any optional arguments, fill in defaults.
-	while (curarg < comm->args.size())
-	{
-		r->writeDWord (DH_PushNumber);
-		r->writeDWord (comm->args[curarg].defvalue);
-		curarg++;
-	}
-
-	r->writeDWord (DH_Command);
-	r->writeDWord (comm->number);
-	r->writeDWord (comm->args.size());
-
-	return r;
-}
-
-// ============================================================================
-//
-String BotscriptParser::parseFloat()
-{
-	m_lexer->tokenMustBe (TK_Number);
-	String floatstring = getTokenString();
-	Lexer::TokenInfo tok;
-
-	// Go after the decimal point
-	if (m_lexer->peekNext (&tok) && tok.type ==TK_Dot)
-	{
-		m_lexer->skip();
-		m_lexer->mustGetNext (TK_Number);
-		floatstring += ".";
-		floatstring += getTokenString();
-	}
-
-	return floatstring;
-}
-
-// ============================================================================
-//
-// Parses an assignment operator.
-//
-AssignmentOperator BotscriptParser::parseAssignmentOperator()
-{
-	const List<ETokenType> tokens =
-	{
-		TK_Assign,
-		TK_AddAssign,
-		TK_SubAssign,
-		TK_MultiplyAssign,
-		TK_DivideAssign,
-		TK_ModulusAssign,
-		TK_DoublePlus,
-		TK_DoubleMinus,
-	};
-
-	m_lexer->mustGetAnyOf (tokens);
-
-	switch (m_lexer->tokenType())
-	{
-		case TK_Assign:			return ASSIGNOP_Assign;
-		case TK_AddAssign:		return ASSIGNOP_Add;
-		case TK_SubAssign:		return ASSIGNOP_Subtract;
-		case TK_MultiplyAssign:	return ASSIGNOP_Multiply;
-		case TK_DivideAssign:	return ASSIGNOP_Divide;
-		case TK_ModulusAssign:	return ASSIGNOP_Modulus;
-		case TK_DoublePlus:		return ASSIGNOP_Increase;
-		case TK_DoubleMinus:	return ASSIGNOP_Decrease;
-		default: break;
-	}
-
-	assert (false);
-	return (AssignmentOperator) 0;
-}
-
-// ============================================================================
-//
-struct AssignmentDataHeaderInfo
-{
-	AssignmentOperator	op;
-	DataHeader			local;
-	DataHeader			global;
-	DataHeader			array;
-};
-
-const AssignmentDataHeaderInfo gAssignmentDataHeaders[] =
-{
-	{ ASSIGNOP_Assign,		DH_AssignLocalVar,		DH_AssignGlobalVar,		DH_AssignGlobalArray },
-	{ ASSIGNOP_Add,			DH_AddLocalVar,			DH_AddGlobalVar,		DH_AddGlobalArray },
-	{ ASSIGNOP_Subtract,	DH_SubtractLocalVar,	DH_SubtractGlobalVar,	DH_SubtractGlobalArray },
-	{ ASSIGNOP_Multiply,	DH_MultiplyLocalVar,	DH_MultiplyGlobalVar,	DH_MultiplyGlobalArray },
-	{ ASSIGNOP_Divide,		DH_DivideLocalVar,		DH_DivideGlobalVar,		DH_DivideGlobalArray },
-	{ ASSIGNOP_Modulus,		DH_ModLocalVar,			DH_ModGlobalVar,		DH_ModGlobalArray },
-	{ ASSIGNOP_Increase,	DH_IncreaseLocalVar,	DH_IncreaseGlobalVar,	DH_IncreaseGlobalArray },
-	{ ASSIGNOP_Decrease,	DH_DecreaseLocalVar,	DH_DecreaseGlobalVar,	DH_DecreaseGlobalArray },
-};
-
-DataHeader BotscriptParser::getAssigmentDataHeader (AssignmentOperator op, Variable* var)
-{
-	for (const auto& a : gAssignmentDataHeaders)
-	{
-		if (a.op != op)
-			continue;
-
-		if (var->isarray)
-			return a.array;
-
-		if (var->IsGlobal())
-			return a.global;
-
-		return a.local;
-	}
-
-	error ("WTF: couldn't find data header for operator #%1", op);
-	return (DataHeader) 0;
-}
-
-// ============================================================================
-//
-// 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 (Variable* var)
-{
-	DataBuffer* retbuf = new DataBuffer;
-	DataBuffer* arrayindex = null;
-
-	if (var->writelevel != WRITE_Mutable)
-		error ("cannot alter read-only variable $%1", var->name);
-
-	if (var->isarray)
-	{
-		m_lexer->mustGetNext (TK_BracketStart);
-		Expression expr (this, m_lexer, TYPE_Int);
-		expr.getResult()->convertToBuffer();
-		arrayindex = expr.getResult()->buffer()->clone();
-		m_lexer->mustGetNext (TK_BracketEnd);
-	}
-
-	// Get an operator
-	AssignmentOperator oper = parseAssignmentOperator();
-
-	if (m_currentMode == PARSERMODE_TopLevel)
-		error ("can't alter variables at top level");
-
-	if (var->isarray)
-		retbuf->mergeAndDestroy (arrayindex);
-
-	// Parse the right operand
-	if (oper != ASSIGNOP_Increase && oper != ASSIGNOP_Decrease)
-	{
-		DataBuffer* expr = parseExpression (var->type);
-		retbuf->mergeAndDestroy (expr);
-	}
-
-#if 0
-	// <<= and >>= do not have data headers. Solution: expand them.
-	// a <<= b -> a = a << b
-	// a >>= b -> a = a >> b
-	retbuf->WriteDWord (var->IsGlobal() ? DH_PushGlobalVar : DH_PushLocalVar);
-	retbuf->WriteDWord (var->index);
-	retbuf->MergeAndDestroy (expr);
-	retbuf->WriteDWord ((oper == OPER_ASSIGNLEFTSHIFT) ? DH_LeftShift : DH_RightShift);
-	retbuf->WriteDWord (var->IsGlobal() ? DH_AssignGlobalVar : DH_AssignLocalVar);
-	retbuf->WriteDWord (var->index);
-#endif
-
-	DataHeader dh = getAssigmentDataHeader (oper, var);
-	retbuf->writeDWord (dh);
-	retbuf->writeDWord (var->index);
-	return retbuf;
-}
-
-// ============================================================================
-//
-void BotscriptParser::pushScope (EReset reset)
-{
-	m_scopeCursor++;
-
-	if (m_scopeStack.size() < m_scopeCursor + 1)
-	{
-		ScopeInfo newscope;
-		m_scopeStack << newscope;
-		reset = SCOPE_Reset;
-	}
-
-	if (reset == SCOPE_Reset)
-	{
-		ScopeInfo* info = &SCOPE (0);
-		info->type = SCOPE_Unknown;
-		info->mark1 = null;
-		info->mark2 = null;
-		info->buffer1 = null;
-		info->cases.clear();
-		info->casecursor = info->cases.begin() - 1;
-	}
-
-	// Reset variable stuff in any case
-	SCOPE(0).globalVarIndexBase = (m_scopeCursor == 0) ? 0 : SCOPE(1).globalVarIndexBase;
-	SCOPE(0).localVarIndexBase = (m_scopeCursor == 0) ? 0 : SCOPE(1).localVarIndexBase;
-
-	for (Variable* var : SCOPE(0).globalVariables + SCOPE(0).localVariables)
-		delete var;
-
-	SCOPE(0).localVariables.clear();
-	SCOPE(0).globalVariables.clear();
-}
-
-// ============================================================================
-//
-DataBuffer* BotscriptParser::parseExpression (DataType reqtype, bool fromhere)
-{
-	// hehe
-	if (fromhere == true)
-		m_lexer->skip (-1);
-
-	Expression expr (this, m_lexer, reqtype);
-	expr.getResult()->convertToBuffer();
-
-	// The buffer will be destroyed once the function ends so we need to
-	// clone it now.
-	return expr.getResult()->buffer()->clone();
-}
-
-// ============================================================================
-//
-DataBuffer* BotscriptParser::parseStatement()
-{
-	// If it's a variable, expect assignment.
-	if (m_lexer->next (TK_DollarSign))
-	{
-		m_lexer->mustGetNext (TK_Symbol);
-		Variable* var = findVariable (getTokenString());
-
-		if (var == null)
-			error ("unknown variable $%1", var->name);
-
-		return parseAssignment (var);
-	}
-
-	return null;
-}
-
-// ============================================================================
-//
-void BotscriptParser::addSwitchCase (DataBuffer* casebuffer)
-{
-	ScopeInfo* info = &SCOPE (0);
-	CaseInfo casedata;
-
-	// Init a mark for the case buffer
-	ByteMark* casemark = currentBuffer()->addMark ("");
-	casedata.mark = casemark;
-
-	// Add a reference to the mark. "case" and "default" both
-	// add the necessary bytecode before the reference.
-	if (casebuffer != null)
-		casebuffer->addReference (casemark);
-	else
-		currentBuffer()->addReference (casemark);
-
-	// Init a buffer for the case block and tell the object
-	// writer to record all written data to it.
-	casedata.data = m_switchBuffer = new DataBuffer;
-	SCOPE(0).cases << casedata;
-	info->casecursor++;
-}
-
-// ============================================================================
-//
-bool BotscriptParser::tokenIs (ETokenType a)
-{
-	return (m_lexer->tokenType() == a);
-}
-
-// ============================================================================
-//
-String BotscriptParser::getTokenString()
-{
-	return m_lexer->token()->text;
-}
-
-// ============================================================================
-//
-String BotscriptParser::describePosition() const
-{
-	Lexer::TokenInfo* tok = m_lexer->token();
-	return tok->file + ":" + String (tok->line) + ":" + String (tok->column);
-}
-
-// ============================================================================
-//
-DataBuffer* BotscriptParser::currentBuffer()
-{
-	if (m_switchBuffer != null)
-		return m_switchBuffer;
-
-	if (m_currentMode == PARSERMODE_MainLoop)
-		return m_mainLoopBuffer;
-
-	if (m_currentMode == PARSERMODE_Onenter)
-		return m_onenterBuffer;
-
-	return m_mainBuffer;
-}
-
-// ============================================================================
-//
-void BotscriptParser::writeMemberBuffers()
-{
-	// If there was no mainloop defined, write a dummy one now.
-	if (m_gotMainLoop == false)
-	{
-		m_mainLoopBuffer->writeDWord (DH_MainLoop);
-		m_mainLoopBuffer->writeDWord (DH_EndMainLoop);
-	}
-
-	// Write the onenter and mainloop buffers, in that order in particular.
-	for (DataBuffer** bufp : List<DataBuffer**> ({&m_onenterBuffer, &m_mainLoopBuffer}))
-	{
-		currentBuffer()->mergeAndDestroy (*bufp);
-
-		// Clear the buffer afterwards for potential next state
-		*bufp = new DataBuffer;
-	}
-
-	// Next state definitely has no mainloop yet
-	m_gotMainLoop = false;
-}
-
-// ============================================================================
-//
-// Write string table
-//
-void BotscriptParser::writeStringTable()
-{
-	int stringcount = countStringsInTable();
-
-	if (stringcount == 0)
-		return;
-
-	// Write header
-	m_mainBuffer->writeDWord (DH_StringList);
-	m_mainBuffer->writeDWord (stringcount);
-
-	// Write all strings
-	for (int i = 0; i < stringcount; i++)
-		m_mainBuffer->writeString (getStringTable()[i]);
-}
-
-// ============================================================================
-//
-// Write the compiled bytecode to a file
-//
-void BotscriptParser::writeToFile (String outfile)
-{
-	FILE* fp = fopen (outfile, "wb");
-
-	if (fp == null)
-		error ("couldn't open %1 for writing: %2", outfile, strerror (errno));
-
-	// First, resolve references
-	for (MarkReference* ref : m_mainBuffer->references())
-		for (int i = 0; i < 4; ++i)
-			m_mainBuffer->buffer()[ref->pos + i] = (ref->target->pos >> (8 * i)) & 0xFF;
-
-	// Then, dump the main buffer to the file
-	fwrite (m_mainBuffer->buffer(), 1, m_mainBuffer->writtenSize(), fp);
-	print ("-- %1 byte%s1 written to %2\n", m_mainBuffer->writtenSize(), outfile);
-	fclose (fp);
-}
-
-// ============================================================================
-//
-// Attempt to find the variable by the given name. Looks from current scope
-// downwards.
-//
-Variable* BotscriptParser::findVariable (const String& name)
-{
-	for (int i = m_scopeCursor; i >= 0; --i)
-	{
-		for (Variable* var : m_scopeStack[i].globalVariables + m_scopeStack[i].localVariables)
-		{
-			if (var->name == name)
-				return var;
-		}
-	}
-
-	return null;
-}
-
-// ============================================================================
-//
-// Is the parser currently in global state (i.e. not in any specific state)?
-//
-bool BotscriptParser::isInGlobalState() const
-{
-	return m_currentState.isEmpty();
-}
-
-// ============================================================================
-//
-void BotscriptParser::suggestHighestVarIndex (bool global, int index)
-{
-	if (global)
-		m_highestGlobalVarIndex = max (m_highestGlobalVarIndex, index);
-	else
-		m_highestStateVarIndex = max (m_highestStateVarIndex, index);
-}
-
-// ============================================================================
-//
-int BotscriptParser::getHighestVarIndex (bool global)
-{
-	if (global)
-		return m_highestGlobalVarIndex;
-
-	return m_highestStateVarIndex;
-}
--- a/src/Parser.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,258 +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 "LexerScanner.h"
-#include "Tokens.h"
-
-class DataBuffer;
-class Lexer;
-class Variable;
-
-// ============================================================================
-// Mark types
-//
-named_enum MarkType
-{
-	MARK_Label,
-	MARK_If,
-	MARK_Internal, // internal structures
-};
-
-// ============================================================================
-// Scope types
-//
-named_enum ScopeType
-{
-	SCOPE_Unknown,
-	SCOPE_If,
-	SCOPE_While,
-	SCOPE_For,
-	SCOPE_Do,
-	SCOPE_Switch,
-	SCOPE_Else,
-};
-
-named_enum AssignmentOperator
-{
-	ASSIGNOP_Assign,
-	ASSIGNOP_Add,
-	ASSIGNOP_Subtract,
-	ASSIGNOP_Multiply,
-	ASSIGNOP_Divide,
-	ASSIGNOP_Modulus,
-	ASSIGNOP_Increase,
-	ASSIGNOP_Decrease,
-};
-
-named_enum Writability
-{
-	WRITE_Mutable,		// normal read-many-write-many variable
-	WRITE_Const,		// write-once const variable
-	WRITE_Constexpr,	// const variable whose value is known to compiler
-};
-
-// =============================================================================
-//
-// Parser mode: where is the parser at?
-//
-named_enum ParserMode
-{
-	PARSERMODE_TopLevel,	// at top level
-	PARSERMODE_Event,		// inside event definition
-	PARSERMODE_MainLoop,	// inside mainloop
-	PARSERMODE_Onenter,		// inside onenter
-	PARSERMODE_Onexit,		// inside onexit
-};
-
-// ============================================================================
-//
-struct Variable
-{
-	String			name;
-	String			statename;
-	DataType		type;
-	int				index;
-	Writability		writelevel;
-	int				value;
-	String			origin;
-	bool			isarray;
-
-	inline bool IsGlobal() const
-	{
-		return statename.isEmpty();
-	}
-};
-
-// ============================================================================
-//
-struct CaseInfo
-{
-	ByteMark*		mark;
-	int				number;
-	DataBuffer*		data;
-};
-
-// ============================================================================
-//
-// Meta-data about scopes
-//
-struct ScopeInfo
-{
-	ByteMark*					mark1;
-	ByteMark*					mark2;
-	ScopeType					type;
-	DataBuffer*					buffer1;
-	int							globalVarIndexBase;
-	int							globalArrayIndexBase;
-	int							localVarIndexBase;
-
-	// switch-related stuff
-	List<CaseInfo>::Iterator	casecursor;
-	List<CaseInfo>				cases;
-	List<Variable*>				localVariables;
-	List<Variable*>				globalVariables;
-	List<Variable*>				globalArrays;
-};
-
-// ============================================================================
-//
-class BotscriptParser
-{
-	PROPERTY (public, bool, isReadOnly, setReadOnly, STOCK_WRITE)
-
-	public:
-		enum EReset
-		{
-			eNoReset,
-			SCOPE_Reset,
-		};
-
-		BotscriptParser();
-		~BotscriptParser();
-		void					parseBotscript (String fileName);
-		DataBuffer*				parseCommand (CommandInfo* comm);
-		DataBuffer*				parseAssignment (Variable* var);
-		AssignmentOperator		parseAssignmentOperator();
-		String					parseFloat();
-		void					pushScope (EReset reset = SCOPE_Reset);
-		DataBuffer*				parseStatement();
-		void					addSwitchCase (DataBuffer* b);
-		void					checkToplevel();
-		void					checkNotToplevel();
-		bool					tokenIs (ETokenType a);
-		String					getTokenString();
-		String					describePosition() const;
-		void					writeToFile (String outfile);
-		Variable*				findVariable (const String& name);
-		bool					isInGlobalState() const;
-		void					suggestHighestVarIndex (bool global, int index);
-		int						getHighestVarIndex (bool global);
-
-		inline ScopeInfo& scope (int offset)
-		{
-			return m_scopeStack[m_scopeCursor - offset];
-		}
-
-		inline int numEvents() const
-		{
-			return m_numEvents;
-		}
-
-		inline int numStates() const
-		{
-			return m_numStates;
-		}
-
-	private:
-		// The main buffer - the contents of this is what we
-		// write to file after parsing is complete
-		DataBuffer*		m_mainBuffer;
-
-		// onenter buffer - the contents of the onenter {} block
-		// is buffered here and is merged further at the end of state
-		DataBuffer*		m_onenterBuffer;
-
-		// Mainloop buffer - the contents of the mainloop {} block
-		// is buffered here and is merged further at the end of state
-		DataBuffer*		m_mainLoopBuffer;
-
-		// Switch buffer - switch case data is recorded to this
-		// buffer initially, instead of into main buffer.
-		DataBuffer*		m_switchBuffer;
-
-		Lexer*			m_lexer;
-		int				m_numStates;
-		int				m_numEvents;
-		ParserMode		m_currentMode;
-		String			m_currentState;
-		bool			m_isStateSpawnDefined;
-		bool			m_gotMainLoop;
-		int				m_scopeCursor;
-		bool			m_isElseAllowed;
-		int				m_highestGlobalVarIndex;
-		int				m_highestStateVarIndex;
-		int				m_numWrittenBytes;
-		List<ScopeInfo>	m_scopeStack;
-		int				m_zandronumVersion;
-		bool			m_defaultZandronumVersion;
-
-		DataBuffer*		currentBuffer();
-		void			parseStateBlock();
-		void			parseEventBlock();
-		void			parseMainloop();
-		void			parseOnEnterExit();
-		void			parseVar();
-		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			parseLabel();
-		void			parseEventdef();
-		void			parseFuncdef();
-		void			parseUsing();
-		void			writeMemberBuffers();
-		void			writeStringTable();
-		DataBuffer*		parseExpression (DataType reqtype, bool fromhere = false);
-		DataHeader		getAssigmentDataHeader (AssignmentOperator op, Variable* var);
-};
-
-#endif // BOTC_PARSER_H
--- a/src/Property.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +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.
-*/
-
-#pragma once
-
-#define PROPERTY( ACCESS, TYPE, READ, WRITE, WRITETYPE )			\
-	private:														\
-		TYPE m_##READ;												\
-																	\
-	public:															\
-		inline TYPE const& READ() const								\
-		{															\
-			return m_##READ; 										\
-		}															\
-																	\
-	ACCESS:															\
-		void WRITE( TYPE const& a ) PROPERTY_##WRITETYPE( READ )	\
-
-#define PROPERTY_STOCK_WRITE( READ )	\
-		{								\
-			m_##READ = a;				\
-		}
-
-#define PROPERTY_CUSTOM_WRITE( READ ) ;
\ No newline at end of file
--- a/src/String.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,451 +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 "String.h"
-
-// =============================================================================
-//
-int String::compare (const String& other) const
-{
-	return m_string.compare (other.stdString());
-}
-
-// =============================================================================
-//
-void String::trim (int n)
-{
-	if (n > 0)
-		m_string = mid (0, length() - n).stdString();
-	else
-		m_string = mid (n, -1).stdString();
-}
-
-// =============================================================================
-//
-String String::strip (const List< char >& unwanted)
-{
-	String copy (m_string);
-
-	for (char c : unwanted)
-		for (int i = 0; i < copy.length(); ++i)
-			if (copy[i] == c)
-				copy.removeAt (i);
-
-	/*
-	int pos = 0;
-	while ((pos = copy.First (c)) != -1)
-		copy.RemoveAt (pos--);
-	*/
-
-	return copy;
-}
-
-// =============================================================================
-//
-String String::toUppercase() const
-{
-	String newstr = m_string;
-
-	for (char& c : newstr)
-		if (c >= 'a' && c <= 'z')
-			c -= 'a' - 'A';
-
-	return newstr;
-}
-
-// =============================================================================
-//
-String String::toLowercase() const
-{
-	String newstr = m_string;
-
-	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 (const 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)
-		m_string = m_string.replace (pos, strlen (a), b);
-}
-
-// =============================================================================
-//
-int String::count (char needle) const
-{
-	int needles = 0;
-
-	for (const char & c : m_string)
-		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, m_string.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 (m_string[i] != ' ')
-			continue;
-
-		if (++count < n)
-			continue;
-
-		return i;
-	}
-
-	return -1;
-}
-
-// =============================================================================
-//
-int String::firstIndexOf (const char* c, int 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::lastIndexOf (const char* c, int 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::toLong (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::toFloat (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::toDouble (bool* ok) const
-{
-	errno = 0;
-	char* endptr;
-	double i = strtod (m_string.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 : 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::endsWith (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::startsWith (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;
-}
-
-// =============================================================================
-//
-String StringList::join (const String& delim)
-{
-	String result;
-
-	for (const String& it : deque())
-	{
-		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.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::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);
-}
--- a/src/String.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,372 +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 StringList;
-
-// =============================================================================
-//
-class String
-{
-	public:
-		using StringType	= std::string;
-		using Iterator		= typename StringType::iterator;
-		using ConstIterator	= StringType::const_iterator;
-
-		String() {}
-
-		explicit String (char a) :
-			m_string (&a) {}
-
-		String (const char* data) :
-			m_string (data) {}
-
-		String (const StringType& data) :
-			m_string (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 (const 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 m_string[0] == '\0';
-		}
-
-		inline void append (const char* data)
-		{
-			m_string.append (data);
-		}
-
-		inline void append (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 ConstIterator begin() const
-		{
-			return m_string.cbegin();
-		}
-
-		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 ConstIterator end() const
-		{
-			return m_string.end();
-		}
-
-		inline void clear()
-		{
-			m_string.clear();
-		}
-
-		inline void removeAt (int pos)
-		{
-			m_string.erase (m_string.begin() + pos);
-		}
-
-		inline void insert (int pos, char c)
-		{
-			m_string.insert (m_string.begin() + pos, c);
-		}
-
-		inline int length() const
-		{
-			return m_string.length();
-		}
-
-		inline void remove (int pos, int len)
-		{
-			m_string.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)
-		{
-			m_string.replace (pos, n, a.chars());
-		}
-
-		inline void shrinkToFit()
-		{
-			m_string.shrink_to_fit();
-		}
-
-		inline const StringType& stdString() const
-		{
-			return m_string;
-		}
-
-		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)
-		{
-			m_string = (a + m_string).stdString();
-		}
-
-		// =============================================================================
-		//
-		inline String& operator+= (const char data)
-		{
-			append (data);
-			return *this;
-		}
-
-		// =============================================================================
-		//
-		inline String operator- (int n) const
-		{
-			String newString = m_string;
-			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 chars();
-		}
-
-		// =============================================================================
-		//
-		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 m_string;
-};
-
-// =============================================================================
-//
-class StringList : public List<String>
-{
-	public:
-		StringList() {}
-		StringList (std::initializer_list<String> vals) :
-			List<String> (vals) {}
-		StringList (const List<String>& a) : List<String> (a.deque()) {}
-		StringList (const WrappedList& 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
--- a/src/StringTable.cc	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +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.
-*/
-
-// TODO: Another freeloader...
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "StringTable.h"
-
-static StringList g_StringTable;
-
-// ============================================================================
-//
-const StringList& getStringTable()
-{
-	return g_StringTable;
-}
-
-// ============================================================================
-//
-// 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 < g_StringTable.size(); idx++)
-	{
-		// String is already in the table, thus return it.
-		if (g_StringTable[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 (g_StringTable.size() == gMaxStringlistSize - 1)
-		error ("too many strings!\n");
-
-	// Now, dump the string into the slot
-	g_StringTable.append (a);
-	return (g_StringTable.size() - 1);
-}
-
-// ============================================================================
-//
-// Counts the amount of strings in the table.
-//
-int countStringsInTable()
-{
-	return g_StringTable.size();
-}
--- a/src/StringTable.h	Sun Mar 30 21:35:06 2014 +0300
+++ /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 getStringTableIndex (const String& a);
-const StringList& getStringTable();
-int countStringsInTable();
-
-#endif // BOTC_STRINGTABLE_H
--- a/src/Tokens.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +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>
-#include "Macros.h"
-
-// =======================================================
-named_enum ETokenType
-{
-	// Non-word tokens
-	TK_LeftShiftAssign,
-	TK_RightShiftAssign,
-	TK_Equals,
-	TK_NotEquals,
-	TK_AddAssign,
-	TK_SubAssign,
-	TK_MultiplyAssign,
-	TK_DivideAssign,
-	TK_ModulusAssign,
-	TK_LeftShift,
-	TK_RightShift,
-	TK_AtLeast,
-	TK_AtMost,
-	TK_DoubleAmperstand,
-	TK_DoubleBar,
-	TK_DoublePlus,
-	TK_DoubleMinus,
-	TK_SingleQuote,
-	TK_DollarSign,
-	TK_ParenStart,
-	TK_ParenEnd,
-	TK_BracketStart,
-	TK_BracketEnd,
-	TK_BraceStart,
-	TK_BraceEnd,
-	TK_Assign,
-	TK_Plus,
-	TK_Minus,
-	TK_Multiply,
-	TK_Divide,
-	TK_Modulus,
-	TK_Comma,
-	TK_Lesser,
-	TK_Greater,
-	TK_Dot,
-	TK_Colon,
-	TK_Semicolon,
-	TK_Hash,
-	TK_ExclamationMark,
-	TK_Amperstand,
-	TK_Bar,
-	TK_Caret,
-	TK_QuestionMark,
-	TK_Arrow,
-
-	// --------------
-	// Named tokens
-	TK_Bool,
-	TK_Break,
-	TK_Case,
-	TK_Continue,
-	TK_Const,
-	TK_Constexpr,
-	TK_Default,
-	TK_Do,
-	TK_Else,
-	TK_Event,
-	TK_Eventdef,
-	TK_For,
-	TK_Funcdef,
-	TK_If,
-	TK_Int,
-	TK_Mainloop,
-	TK_Onenter,
-	TK_Onexit,
-	TK_State,
-	TK_Switch,
-	TK_Str,
-	TK_Using,
-	TK_Var,
-	TK_Void,
-	TK_While,
-	TK_True,
-	TK_False,
-
-	// 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,
-	TK_Func,
-	TK_Return,
-
-	// --------------
-	// Generic tokens
-	TK_Symbol,
-	TK_Number,
-	TK_String,
-	TK_Any,
-};
-
-static const int gFirstNamedToken		= TK_Bool;
-static const int gLastNamedToken		= (int) TK_Symbol - 1;
-
-#endif
--- a/src/Types.h	Sun Mar 30 21:35:06 2014 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +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 "Macros.h"
-#include "String.h"
-
-static const std::nullptr_t null = nullptr;
-
-// =============================================================================
-//
-named_enum DataType
-{
-	TYPE_Unknown,
-	TYPE_Void,
-	TYPE_Int,
-	TYPE_String,
-	TYPE_Bool,
-};
-
-// =============================================================================
-//
-struct ByteMark
-{
-	String		name;
-	int			pos;
-};
-
-// =============================================================================
-//
-struct MarkReference
-{
-	ByteMark*	target;
-	int			pos;
-};
-
-// =============================================================================
-//
-// 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/botStuff.h	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,125 @@
+/*
+	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
+
+#include "main.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;
+
+named_enum DataHeader
+{
+	DH_Command,
+	DH_StateIndex,
+	DH_StateName,
+	DH_OnEnter,
+	DH_MainLoop,
+	DH_OnExit,
+	DH_Event,
+	DH_EndOnEnter,
+	DH_EndMainLoop,
+	DH_EndOnExit,
+	DH_EndEvent,
+	DH_IfGoto,
+	DH_IfNotGoto,
+	DH_Goto,
+	DH_OrLogical,
+	DH_AndLogical,
+	DH_OrBitwise,
+	DH_EorBitwise,
+	DH_AndBitwise,
+	DH_Equals,
+	DH_NotEquals,
+	DH_LessThan,
+	DH_AtMost,
+	DH_GreaterThan,
+	DH_AtLeast,
+	DH_NegateLogical,
+	DH_LeftShift,
+	DH_RightShift,
+	DH_Add,
+	DH_Subtract,
+	DH_UnaryMinus,
+	DH_Multiply,
+	DH_Divide,
+	DH_Modulus,
+	DH_PushNumber,
+	DH_PushStringIndex,
+	DH_PushGlobalVar,
+	DH_PushLocalVar,
+	DH_DropStackPosition,
+	DH_ScriptVarList,
+	DH_StringList,
+	DH_IncreaseGlobalVar,
+	DH_DecreaseGlobalVar,
+	DH_AssignGlobalVar,
+	DH_AddGlobalVar,
+	DH_SubtractGlobalVar,
+	DH_MultiplyGlobalVar,
+	DH_DivideGlobalVar,
+	DH_ModGlobalVar,
+	DH_IncreaseLocalVar,
+	DH_DecreaseLocalVar,
+	DH_AssignLocalVar,
+	DH_AddLocalVar,
+	DH_SubtractLocalVar,
+	DH_MultiplyLocalVar,
+	DH_DivideLocalVar,
+	DH_ModLocalVar,
+	DH_CaseGoto,
+	DH_Drop,
+	DH_IncreaseGlobalArray,
+	DH_DecreaseGlobalArray,
+	DH_AssignGlobalArray,
+	DH_AddGlobalArray,
+	DH_SubtractGlobalArray,
+	DH_MultiplyGlobalArray,
+	DH_DivideGlobalArray,
+	DH_ModGlobalArray,
+	DH_PushGlobalArray,
+	DH_Swap,
+	DH_Dup,
+	DH_ArraySet,
+	numDataHeaders
+};
+
+#endif	// BOTC_BOTSTUFF_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/commands.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,123 @@
+/*
+	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::signature()
+{
+	String text;
+	text += dataTypeName (returnvalue);
+	text += ' ';
+	text += name;
+
+	if (args.isEmpty() == false)
+		text += ' ';
+
+	text += '(';
+
+	bool hasoptionals = false;
+
+	for (int i = 0; i < args.size(); i++)
+	{
+		if (i == minargs)
+		{
+			hasoptionals = true;
+			text += '[';
+		}
+
+		if (i)
+			text += ", ";
+
+		text += dataTypeName (args[i].type);
+		text += ' ';
+		text += args[i].name;
+
+		if (i >= minargs)
+		{
+			bool isString = args[i].type == TYPE_String;
+			text += " = ";
+
+			if (isString)
+				text += '"';
+
+			text += String::fromNumber (args[i].defvalue);
+
+			if (isString)
+				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 Mar 30 21:51:23 2014 +0300
@@ -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
+{
+	DataType				type;
+	String					name;
+	int						defvalue;
+};
+
+struct CommandInfo
+{
+	String					name;
+	int						number;
+	int						minargs;
+	DataType				returnvalue;
+	List<CommandArgument>	args;
+	String					origin;
+
+	String	signature();
+};
+
+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/dataBuffer.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,242 @@
+/*
+	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 char[size]);
+	setPosition (&buffer()[0]);
+	setAllocatedSize (size);
+}
+
+// ============================================================================
+//
+DataBuffer::~DataBuffer()
+{
+	assert (marks().isEmpty() && references().isEmpty());
+	delete buffer();
+}
+
+// ============================================================================
+//
+void DataBuffer::mergeAndDestroy (DataBuffer* other)
+{
+	if (other == null)
+		return;
+
+	// Note: We transfer the marks before the buffer is copied, so that the
+	// offset uses the proper value (which is the written size of @other, which
+	// we don't want our written size to be added to yet).
+	other->transferMarksTo (this);
+	copyBuffer (other);
+	delete other;
+}
+
+// ============================================================================
+//
+// Clones this databuffer to a new one and returns it. Note that the original
+// transfers its marks and references and loses them in the process.
+//
+DataBuffer* DataBuffer::clone()
+{
+	DataBuffer* other = new DataBuffer;
+	transferMarksTo (other);
+	other->copyBuffer (this);
+	return other;
+}
+
+// ============================================================================
+//
+void DataBuffer::copyBuffer (const DataBuffer* buf)
+{
+	checkSpace (buf->writtenSize());
+	memcpy (m_position, buf->buffer(), buf->writtenSize());
+	m_position += buf->writtenSize();
+}
+
+// ============================================================================
+//
+void DataBuffer::transferMarksTo (DataBuffer* dest)
+{
+	int offset = dest->writtenSize();
+
+	for (ByteMark* mark : marks())
+	{
+		mark->pos += offset;
+		dest->m_marks << mark;
+	}
+
+	for (MarkReference* ref : references())
+	{
+		ref->pos += offset;
+		dest->m_references << ref;
+	}
+
+	m_marks.clear();
+	m_references.clear();
+}
+
+// ============================================================================
+//
+ByteMark* DataBuffer::addMark (const String& name)
+{
+	ByteMark* mark = new ByteMark;
+	mark->name = name;
+	mark->pos = writtenSize();
+	m_marks << mark;
+	return mark;
+}
+
+// ============================================================================
+//
+MarkReference* DataBuffer::addReference (ByteMark* mark)
+{
+	MarkReference* ref = new MarkReference;
+	ref->target = mark;
+	ref->pos = writtenSize();
+	m_references << ref;
+
+	// Write a dummy placeholder for the reference
+	writeDWord (0xBEEFCAFE);
+
+	return ref;
+}
+
+// ============================================================================
+//
+void DataBuffer::adjustMark (ByteMark* mark)
+{
+	mark->pos = writtenSize();
+}
+
+// ============================================================================
+//
+void DataBuffer::offsetMark (ByteMark* mark, int position)
+{
+	mark->pos += position;
+}
+
+// ============================================================================
+//
+void DataBuffer::writeStringIndex (const String& a)
+{
+	writeDWord (DH_PushStringIndex);
+	writeDWord (getStringTableIndex (a));
+}
+
+// ============================================================================
+//
+void DataBuffer::dump()
+{
+	for (int i = 0; i < writtenSize(); ++i)
+		printf ("%d. [0x%X]\n", i, buffer()[i]);
+}
+
+// ============================================================================
+//
+void DataBuffer::checkSpace (int bytes)
+{
+	int writesize = writtenSize();
+
+	if (writesize + bytes < allocatedSize())
+		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[allocatedSize()];
+	memcpy (copy, buffer(), allocatedSize());
+
+	// 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 = allocatedSize() + bytes + 512;
+
+	delete buffer();
+	setBuffer (new char[newsize]);
+	setAllocatedSize (newsize);
+
+	// Now, copy the stuff back.
+	memcpy (m_buffer, copy, allocatedSize());
+	setPosition (buffer() + writesize);
+	delete copy;
+}
+
+// =============================================================================
+//
+void DataBuffer::writeByte (int8_t data)
+{
+	checkSpace (1);
+	*m_position++ = data;
+}
+
+// =============================================================================
+//
+void DataBuffer::writeWord (int16_t data)
+{
+	checkSpace (2);
+
+	for (int i = 0; i < 2; ++i)
+		*m_position++ = (data >> (i * 8)) & 0xFF;
+}
+
+// =============================================================================
+//
+void DataBuffer::writeDWord (int32_t data)
+{
+	checkSpace (4);
+
+	for (int i = 0; i < 4; ++i)
+		*m_position++ = (data >> (i * 8)) & 0xFF;
+}
+
+// =============================================================================
+//
+void DataBuffer::writeString (const String& a)
+{
+	checkSpace (a.length() + 4);
+	writeDWord (a.length());
+
+	for (char c : a)
+		writeByte (c);
+}
+
+
+// =============================================================================
+//
+ByteMark* DataBuffer::findMarkByName (const String& name)
+{
+	for (ByteMark* mark : marks())
+		if (mark->name == name)
+			return mark;
+
+	return null;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dataBuffer.h	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,157 @@
+/*
+	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"
+
+/**
+ *    @class DataBuffer
+ *    @brief Stores a buffer of bytecode
+ *
+ *    The DataBuffer class stores a section of bytecode. Buffers are allocated on
+ *    the heap and written to using the @c write* functions. Buffers can be cut and
+ *    pasted together with @c mergeAndDestroy, note that this function destroys the
+ *    parameter buffer in the process
+ *
+ *    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 "bookmark" a position like that for future use.
+ *
+ *    A reference acts as a pointer to a mark. The reference is four bytes in the
+ *    bytecode which will be replaced with its mark's position when the bytecode
+ *    is written to the output file.
+ *
+ *    This mark/reference system is used to know bytecode offset values when
+ *    compiling, even though actual final positions cannot be known.
+ */
+class DataBuffer
+{
+	//! @
+	PROPERTY (private, char*,					buffer,			setBuffer,			STOCK_WRITE)
+	PROPERTY (private, int,						allocatedSize,	setAllocatedSize,	STOCK_WRITE)
+	PROPERTY (private, char*,					position,		setPosition,		STOCK_WRITE)
+	PROPERTY (private, List<ByteMark*>,			marks,			setMarks,			STOCK_WRITE)
+	PROPERTY (private, List<MarkReference*>,	references,		setReferences,		STOCK_WRITE)
+
+	public:
+		//! Constructs a new databuffer with @c size bytes.
+		DataBuffer (int size = 128);
+
+		//! Destructs the databuffer.
+		~DataBuffer();
+
+		//! Adds a new mark to the current position with the name @c name.
+		//! @param name the name of the new mark
+		//! @return a pointer to the new mark
+		ByteMark*		addMark (const String& name);
+
+		//! Adds a new reference to @c mark at the current position. This
+		//! function will write 4 bytes to the buffer whose value will
+		//! be determined at final output writing.
+		//! @param mark the mark which the new reference will attach to
+		//! @return a pointer to the new reference
+		MarkReference*	addReference (ByteMark* mark);
+
+		//! Moves @c mark to the current bytecode position.
+		//! @param mark the mark to adjust
+		void			adjustMark (ByteMark* mark);
+
+		//! Ensures there's at least @c bytes left in the buffer. Will resize
+		//! if necessary, no-op if not. On resize, 512 extra bytes are allocated
+		//! to reduce the amount of resizes.
+		//! @param bytes the amount of space in bytes to ensure allocated
+		void			checkSpace (int bytes);
+
+		//! Creates a clone of this data buffer.
+		//! @note All marks will be moved into the new databuffer as marks are
+		//! @note never duplicated.
+		//! @return The newly cloned databuffer.
+		DataBuffer*		clone();
+
+		//! Prints the buffer to stdout. Useful for debugging.
+		void			dump();
+
+		//! Finds the given mark by name.
+		//! @param name the name of the mark to find
+		ByteMark*		findMarkByName (const String& name);
+
+		//! Merge another data buffer into this one.
+		//! Note: @c other is destroyed in the process.
+		//! @param other the buffer to merge in
+		void			mergeAndDestroy (DataBuffer* other);
+
+		//! Moves @c mark to the given bytecode position.
+		//! @param mark the mark to adjust
+		//! @param position where to adjust the mark
+		void			offsetMark (ByteMark* mark, int position);
+
+		//! Transfers all marks of this buffer to @c other.
+		//! @param other the data buffer to transfer marks to
+		void			transferMarksTo (DataBuffer* other);
+
+		//! Writes the index of the given string to the databuffer.
+		//! 4 bytes will be written to the bytecode.
+		//! @param a the string whose index to write
+		void			writeStringIndex (const String& a);
+
+		//! Writes the given string as-is into the databuffer.
+		//! @c a.length + 4 bytes will be written to the buffer.
+		//! @param a the string to write
+		void			writeString (const String& a);
+
+		//! Writes the given byte to the buffer. 1 byte will be written.
+		//! @c data the byte to write
+		void			writeByte (int8_t data);
+
+		//! Writes the given word to the buffer. 2 byte will be written.
+		//! @c data the word to write
+		void			writeWord (int16_t data);
+
+		//! Writes the given double word to the buffer. 4 bytes will be written.
+		//! @c data the double word to write
+		void			writeDWord (int32_t data);
+
+		//! @return the amount of bytes written to this buffer.
+		inline int		writtenSize() const
+		{
+			return position() - buffer();
+		}
+
+	protected:
+		//! Writes the buffer's contents from @c buf.
+		//! @c buf.writtenSize() bytes will be written.
+		//! @param buf the buffer to copy
+		void			copyBuffer (const DataBuffer* buf);
+};
+
+#endif // BOTC_DATABUFFER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/events.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,67 @@
+/*
+	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.
+*/
+
+// TODO: this file is a freeloader, I should probably get rid of it
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "main.h"
+#include "string.h"
+#include "events.h"
+#include "lexer.h"
+
+static List<EventDefinition*> g_Events;
+
+// ============================================================================
+//
+void addEvent (EventDefinition* e)
+{
+	g_Events << e;
+}
+
+// ============================================================================
+//
+// Finds an event definition by index
+//
+EventDefinition* findEventByIndex (int idx)
+{
+	return g_Events[idx];
+}
+
+// ============================================================================
+//
+// Finds an event definition by name
+//
+EventDefinition* findEventByName (String a)
+{
+	for (EventDefinition* e : g_Events)
+		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 Mar 30 21:51:23 2014 +0300
@@ -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/expression.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,651 @@
+#include "expression.h"
+#include "dataBuffer.h"
+#include "lexer.h"
+
+struct OperatorInfo
+{
+	ETokenType	token;
+	int			priority;
+	int			numoperands;
+	DataHeader	header;
+};
+
+static const OperatorInfo g_Operators[] =
+{
+	{TK_ExclamationMark,	0,		1,	DH_NegateLogical,	},
+	{TK_Minus,				0,		1,	DH_UnaryMinus,		},
+	{TK_Multiply,			10,		2,	DH_Multiply,		},
+	{TK_Divide,				10,		2,	DH_Divide,			},
+	{TK_Modulus,			10,		2,	DH_Modulus,			},
+	{TK_Plus,				20,		2,	DH_Add,				},
+	{TK_Minus,				20,		2,	DH_Subtract,		},
+	{TK_LeftShift,			30,		2,	DH_LeftShift,		},
+	{TK_RightShift,			30,		2,	DH_RightShift,		},
+	{TK_Lesser,				40,		2,	DH_LessThan,		},
+	{TK_Greater,			40,		2,	DH_GreaterThan,		},
+	{TK_AtLeast,			40,		2,	DH_AtLeast,			},
+	{TK_AtMost,				40,		2,	DH_AtMost,			},
+	{TK_Equals,				50,		2,	DH_Equals			},
+	{TK_NotEquals,			50,		2,	DH_NotEquals		},
+	{TK_Amperstand,			60,		2,	DH_AndBitwise		},
+	{TK_Caret,				70,		2,	DH_EorBitwise		},
+	{TK_Bar,				80,		2,	DH_OrBitwise		},
+	{TK_DoubleAmperstand,	90,		2,	DH_AndLogical		},
+	{TK_DoubleBar,			100,	2,	DH_OrLogical		},
+	{TK_QuestionMark,		110,	3,	(DataHeader) 0		},
+};
+
+// =============================================================================
+//
+Expression::Expression (BotscriptParser* parser, Lexer* lx, DataType reqtype) :
+	m_parser (parser),
+	m_lexer (lx),
+	m_type (reqtype)
+{
+	ExpressionSymbol* sym;
+
+	while ((sym = parseSymbol()) != null)
+		m_symbols << sym;
+
+	// If we were unable to get any expression symbols, something's wonky with
+	// the script. Report an error. mBadTokenText is set to the token that
+	// ParseSymbol ends at when it returns false.
+	if (m_symbols.isEmpty())
+		error ("unknown identifier '%1'", m_badTokenText);
+
+	adjustOperators();
+	verify();
+	evaluate();
+}
+
+// =============================================================================
+//
+Expression::~Expression()
+{
+	for (ExpressionSymbol* sym : m_symbols)
+		delete sym;
+}
+
+// =============================================================================
+//
+// Try to parse an expression symbol (i.e. an OPER_erator or OPER_erand or a colon)
+// from the lexer.
+//
+ExpressionSymbol* Expression::parseSymbol()
+{
+	int pos = m_lexer->position();
+	ExpressionValue* op = null;
+
+	if (m_lexer->next (TK_Colon))
+		return new ExpressionColon;
+
+	// Check for OPER_erator
+	for (const OperatorInfo& op : g_Operators)
+		if (m_lexer->next (op.token))
+			return new ExpressionOperator ((ExpressionOperatorType) (&op - &g_Operators[0]));
+
+	// Check sub-expression
+	if (m_lexer->next (TK_ParenStart))
+	{
+		Expression expr (m_parser, m_lexer, m_type);
+		m_lexer->mustGetNext (TK_ParenEnd);
+		return expr.getResult()->clone();
+	}
+
+	op = new ExpressionValue (m_type);
+
+	// Check function
+	if (CommandInfo* comm = findCommandByName (m_lexer->peekNextString()))
+	{
+		m_lexer->skip();
+
+		if (m_type != TYPE_Unknown && comm->returnvalue != m_type)
+			error ("%1 returns an incompatible data type", comm->name);
+
+		op->setBuffer (m_parser->parseCommand (comm));
+		return op;
+	}
+
+	// Check for variables
+	if (m_lexer->next (TK_DollarSign))
+	{
+		m_lexer->mustGetNext (TK_Symbol);
+		Variable* var = m_parser->findVariable (getTokenString());
+
+		if (var == null)
+			error ("unknown variable %1", getTokenString());
+
+		if (var->type != m_type)
+			error ("expression requires %1, variable $%2 is of type %3",
+				dataTypeName (m_type), var->name, dataTypeName (var->type));
+
+		if (var->isarray)
+		{
+			m_lexer->mustGetNext (TK_BracketStart);
+			Expression expr (m_parser, m_lexer, TYPE_Int);
+			expr.getResult()->convertToBuffer();
+			DataBuffer* buf = expr.getResult()->buffer()->clone();
+			buf->writeDWord (DH_PushGlobalArray);
+			buf->writeDWord (var->index);
+			op->setBuffer (buf);
+			m_lexer->mustGetNext (TK_BracketEnd);
+		}
+		elif (var->writelevel == WRITE_Constexpr)
+			op->setValue (var->value);
+		else
+		{
+			DataBuffer* buf = new DataBuffer (8);
+
+			if (var->IsGlobal())
+				buf->writeDWord (DH_PushGlobalVar);
+			else
+				buf->writeDWord (DH_PushLocalVar);
+
+			buf->writeDWord (var->index);
+			op->setBuffer (buf);
+		}
+
+		return op;
+	}
+
+	// Check for literal
+	switch (m_type)
+	{
+		case TYPE_Void:
+		case TYPE_Unknown:
+		{
+			error ("unknown identifier `%1` (expected keyword, function or variable)", getTokenString());
+			break;
+		}
+
+		case TYPE_Bool:
+		{
+			if (m_lexer->next (TK_True) || m_lexer->next (TK_False))
+			{
+				ETokenType tt = m_lexer->tokenType();
+				op->setValue (tt == TK_True ? 1 : 0);
+				return op;
+			}
+		}
+
+		case TYPE_Int:
+		{
+			if (m_lexer->next (TK_Number))
+			{
+				op->setValue (getTokenString().toLong());
+				return op;
+			}
+		}
+
+		case TYPE_String:
+		{
+			if (m_lexer->next (TK_String))
+			{
+				op->setValue (getStringTableIndex (getTokenString()));
+				return op;
+			}
+		}
+	}
+
+	m_badTokenText = m_lexer->token()->text;
+	m_lexer->setPosition (pos);
+	delete op;
+	return null;
+}
+
+// =============================================================================
+//
+// The symbol parsing process only does token-based checking for OPER_erators.
+// Thus ALL minus OPER_erators are actually unary minuses simply because both
+// have TK_Minus as their token and the unary minus is prior to the binary minus
+// in the OPER_erator table. Now that we have all symbols present, we can
+// correct cases like this.
+//
+void Expression::adjustOperators()
+{
+	for (auto it = m_symbols.begin() + 1; it != m_symbols.end(); ++it)
+	{
+		if ((*it)->type() != EXPRSYM_Operator)
+			continue;
+
+		ExpressionOperator* op = static_cast<ExpressionOperator*> (*it);
+
+		// Unary minus with a value as the previous symbol cannot really be
+		// unary; replace with binary minus.
+		if (op->id() == OPER_UnaryMinus && (*(it - 1))->type() == EXPRSYM_Value)
+			op->setID (OPER_Subtraction);
+	}
+}
+
+// =============================================================================
+//
+// Verifies a single value. Helper function for Expression::verify.
+//
+void Expression::tryVerifyValue (bool* verified, SymbolList::Iterator it)
+{
+	// If it's an unary OPER_erator we skip to its value. The actual OPER_erator will
+	// be verified separately.
+	if ((*it)->type() == EXPRSYM_Operator &&
+			g_Operators[static_cast<ExpressionOperator*> (*it)->id()].numoperands == 1)
+	{
+		++it;
+	}
+
+	int i = it - m_symbols.begin();
+
+	// Ensure it's an actual value
+	if ((*it)->type() != EXPRSYM_Value)
+		error ("malformed expression (symbol #%1 is not a value)", i);
+
+	verified[i] = true;
+}
+
+// =============================================================================
+//
+// Ensures the expression is valid and well-formed and not OMGWTFBBQ. Throws an
+// error if this is not the case.
+//
+void Expression::verify()
+{
+	if (m_symbols.size() == 1)
+	{
+		if (m_symbols[0]->type() != EXPRSYM_Value)
+			error ("bad expression");
+
+		return;
+	}
+
+	if (m_type == TYPE_String)
+		error ("Cannot perform OPER_erations on strings");
+
+	bool* verified = new bool[m_symbols.size()];
+	memset (verified, 0, m_symbols.size() * sizeof (decltype (*verified)));
+	const auto last = m_symbols.end() - 1;
+	const auto first = m_symbols.begin();
+
+	for (auto it = m_symbols.begin(); it != m_symbols.end(); ++it)
+	{
+		int i = (it - first);
+
+		if ((*it)->type() != EXPRSYM_Operator)
+			continue;
+
+		ExpressionOperator* op = static_cast<ExpressionOperator*> (*it);
+		int numoperands = g_Operators[op->id()].numoperands;
+
+		switch (numoperands)
+		{
+			case 1:
+			{
+				// Ensure that:
+				// -	unary OPER_erator is not the last symbol
+				// -	unary OPER_erator is succeeded by a value symbol
+				// -	neither symbol overlaps with something already verified
+				tryVerifyValue (verified, it + 1);
+
+				if (it == last || verified[i] == true)
+					error ("malformed expression");
+
+				verified[i] = true;
+				break;
+			}
+
+			case 2:
+			{
+				// Ensure that:
+				// -	binary OPER_erator is not the first or last symbol
+				// -	is preceded and succeeded by values
+				// -	none of the three tokens are already verified
+				//
+				// Basically similar logic as above.
+				if (it == first || it == last || verified[i] == true)
+					error ("malformed expression");
+
+				tryVerifyValue (verified, it + 1);
+				tryVerifyValue (verified, it - 1);
+				verified[i] = true;
+				break;
+			}
+
+			case 3:
+			{
+				// Ternary OPER_erator case. This goes a bit nuts.
+				// This time we have the following:
+				//
+				// (VALUE) ? (VALUE) : (VALUE)
+				//         ^
+				// --------/ we are here
+				//
+				// Check that the:
+				// -	questionmark OPER_erator is not misplaced (first or last)
+				// -	the value behind the OPER_erator (-1) is valid
+				// -	the value after the OPER_erator (+1) is valid
+				// -	the value after the colon (+3) is valid
+				// -	none of the five tokens are verified
+				//
+				tryVerifyValue (verified, it - 1);
+				tryVerifyValue (verified, it + 1);
+				tryVerifyValue (verified, it + 3);
+
+				if (it == first ||
+					it >= m_symbols.end() - 3 ||
+					verified[i] == true ||
+					verified[i + 2] == true ||
+					(*(it + 2))->type() != EXPRSYM_Colon)
+				{
+					error ("malformed expression");
+				}
+
+				verified[i] = true;
+				verified[i + 2] = true;
+				break;
+			}
+
+			default:
+				error ("WTF OPER_erator with %1 OPER_erands", numoperands);
+		}
+	}
+
+	for (int i = 0; i < m_symbols.size(); ++i)
+		if (verified[i] == false)
+			error ("malformed expression: expr symbol #%1 is was left unverified", i);
+
+	delete verified;
+}
+
+
+// =============================================================================
+//
+// Which operator to evaluate?
+//
+Expression::SymbolList::Iterator Expression::findPrioritizedOperator()
+{
+	SymbolList::Iterator	best = m_symbols.end();
+	int						bestpriority = __INT_MAX__;
+
+	for (SymbolList::Iterator it = m_symbols.begin(); it != m_symbols.end(); ++it)
+	{
+		if ((*it)->type() != EXPRSYM_Operator)
+			continue;
+
+		ExpressionOperator* op = static_cast<ExpressionOperator*> (*it);
+		const OperatorInfo* info = &g_Operators[op->id()];
+
+		if (info->priority < bestpriority)
+		{
+			best = it;
+			bestpriority = info->priority;
+		}
+	}
+
+	return best;
+}
+
+// =============================================================================
+//
+// Process the given OPER_erator and values into a new value.
+//
+ExpressionValue* Expression::evaluateOperator (const ExpressionOperator* op,
+											   const List<ExpressionValue*>& values)
+{
+	const OperatorInfo* info = &g_Operators[op->id()];
+	bool isconstexpr = true;
+	assert (values.size() == info->numoperands);
+
+	for (ExpressionValue* val : values)
+	{
+		if (val->isConstexpr() == false)
+		{
+			isconstexpr = false;
+			break;
+		}
+	}
+
+	// If not all of the values are constant expressions, none of them shall be.
+	if (isconstexpr == false)
+		for (ExpressionValue* val : values)
+			val->convertToBuffer();
+
+	ExpressionValue* newval = new ExpressionValue (m_type);
+
+	if (isconstexpr == false)
+	{
+		// This is not a constant expression so we'll have to use databuffers
+		// to convey the expression to bytecode. Actual value cannot be evaluated
+		// until Zandronum processes it at run-time.
+		newval->setBuffer (new DataBuffer);
+
+		if (op->id() == OPER_Ternary)
+		{
+			// There isn't a dataheader for ternary OPER_erator. Instead, we use DH_IfNotGoto
+			// to create an "if-block" inside an expression.
+			// Behold, big block of writing madness! :P
+			//
+			DataBuffer* buf = newval->buffer();
+			DataBuffer* b0 = values[0]->buffer();
+			DataBuffer* b1 = values[1]->buffer();
+			DataBuffer* b2 = values[2]->buffer();
+			ByteMark* mark1 = buf->addMark (""); // start of "else" case
+			ByteMark* mark2 = buf->addMark (""); // end of expression
+			buf->mergeAndDestroy (b0);
+			buf->writeDWord (DH_IfNotGoto); // if the first OPER_erand (condition)
+			buf->addReference (mark1); // didn't eval true, jump into mark1
+			buf->mergeAndDestroy (b1); // otherwise, perform second OPER_erand (true case)
+			buf->writeDWord (DH_Goto); // afterwards, jump to the end, which is
+			buf->addReference (mark2); // marked by mark2.
+			buf->adjustMark (mark1); // move mark1 at the end of the true case
+			buf->mergeAndDestroy (b2); // perform third OPER_erand (false case)
+			buf->adjustMark (mark2); // move the ending mark2 here
+
+			for (int i = 0; i < 3; ++i)
+				values[i]->setBuffer (null);
+		}
+		else
+		{
+			// Generic case: write all arguments and apply the OPER_erator's
+			// data header.
+			for (ExpressionValue* val : values)
+			{
+				newval->buffer()->mergeAndDestroy (val->buffer());
+
+				// Null the pointer out so that the value's destructor will not
+				// attempt to double-free it.
+				val->setBuffer (null);
+			}
+
+			newval->buffer()->writeDWord (info->header);
+		}
+	}
+	else
+	{
+		// We have a constant expression. We know all the values involved and
+		// can thus compute the result of this expression on compile-time.
+		List<int> nums;
+		int a;
+
+		for (ExpressionValue* val : values)
+			nums << val->value();
+
+		switch (op->id())
+		{
+			case OPER_Addition:				a = nums[0] + nums[1];					break;
+			case OPER_Subtraction:			a = nums[0] - nums[1];					break;
+			case OPER_Multiplication:		a = nums[0] * nums[1];					break;
+			case OPER_UnaryMinus:			a = -nums[0];							break;
+			case OPER_NegateLogical:		a = !nums[0];							break;
+			case OPER_LeftShift:			a = nums[0] << nums[1];					break;
+			case OPER_RightShift:			a = nums[0] >> nums[1];					break;
+			case OPER_CompareLesser:		a = (nums[0] < nums[1]) ? 1 : 0;		break;
+			case OPER_CompareGreater:		a = (nums[0] > nums[1]) ? 1 : 0;		break;
+			case OPER_CompareAtLeast:		a = (nums[0] <= nums[1]) ? 1 : 0;		break;
+			case OPER_CompareAtMost:		a = (nums[0] >= nums[1]) ? 1 : 0;		break;
+			case OPER_CompareEquals:		a = (nums[0] == nums[1]) ? 1 : 0;		break;
+			case OPER_CompareNotEquals:		a = (nums[0] != nums[1]) ? 1 : 0;		break;
+			case OPER_BitwiseAnd:			a = nums[0] & nums[1];					break;
+			case OPER_BitwiseOr:			a = nums[0] | nums[1];					break;
+			case OPER_BitwiseXOr:			a = nums[0] ^ nums[1];					break;
+			case OPER_LogicalAnd:			a = (nums[0] && nums[1]) ? 1 : 0;		break;
+			case OPER_LogicalOr:			a = (nums[0] || nums[1]) ? 1 : 0;		break;
+			case OPER_Ternary:				a = (nums[0] != 0) ? nums[1] : nums[2];	break;
+
+			case OPER_Division:
+			{
+				if (nums[1] == 0)
+					error ("division by zero in constant expression");
+
+				a = nums[0] / nums[1];
+				break;
+			}
+
+			case OPER_Modulus:
+			{
+				if (nums[1] == 0)
+					error ("modulus by zero in constant expression");
+
+				a = nums[0] % nums[1];
+				break;
+			}
+		}
+
+		newval->setValue (a);
+	}
+
+	// The new value has been generated. We don't need the old stuff anymore.
+	for (ExpressionValue* val : values)
+		delete val;
+
+	delete op;
+	return newval;
+}
+
+// =============================================================================
+//
+ExpressionValue* Expression::evaluate()
+{
+	SymbolList::Iterator it;
+
+	while ((it = findPrioritizedOperator()) != m_symbols.end())
+	{
+		int i = it - m_symbols.begin();
+		List<SymbolList::Iterator> OPER_erands;
+		ExpressionOperator* op = static_cast<ExpressionOperator*> (*it);
+		const OperatorInfo* info = &g_Operators[op->id()];
+		int lower, upper; // Boundaries of area to replace
+
+		switch (info->numoperands)
+		{
+			case 1:
+			{
+				lower = i;
+				upper = i + 1;
+				OPER_erands << it + 1;
+				break;
+			}
+
+			case 2:
+			{
+				lower = i - 1;
+				upper = i + 1;
+				OPER_erands << it - 1
+				         << it + 1;
+				break;
+			}
+
+			case 3:
+			{
+				lower = i - 1;
+				upper = i + 3;
+				OPER_erands << it - 1
+				         << it + 1
+				         << it + 3;
+				break;
+			}
+
+			default:
+				assert (false);
+		}
+
+		List<ExpressionValue*> values;
+
+		for (auto it : OPER_erands)
+			values << static_cast<ExpressionValue*> (*it);
+
+		// Note: @op and all of @values are invalid after this call.
+		ExpressionValue* newvalue = evaluateOperator (op, values);
+
+		for (int i = upper; i >= lower; --i)
+			m_symbols.removeAt (i);
+
+		m_symbols.insert (lower, newvalue);
+	}
+
+	assert (m_symbols.size() == 1 && m_symbols.first()->type() == EXPRSYM_Value);
+	ExpressionValue* val = static_cast<ExpressionValue*> (m_symbols.first());
+	return val;
+}
+
+// =============================================================================
+//
+ExpressionValue* Expression::getResult()
+{
+	return static_cast<ExpressionValue*> (m_symbols.first());
+}
+
+// =============================================================================
+//
+String Expression::getTokenString()
+{
+	return m_lexer->token()->text;
+}
+
+// =============================================================================
+//
+ExpressionOperator::ExpressionOperator (ExpressionOperatorType id) :
+	ExpressionSymbol (EXPRSYM_Operator),
+	m_id (id) {}
+
+// =============================================================================
+//
+ExpressionValue::ExpressionValue (DataType valuetype) :
+	ExpressionSymbol (EXPRSYM_Value),
+	m_buffer (null),
+	m_valueType (valuetype) {}
+
+// =============================================================================
+//
+ExpressionValue::~ExpressionValue()
+{
+	delete m_buffer;
+}
+
+// =============================================================================
+//
+void ExpressionValue::convertToBuffer()
+{
+	if (isConstexpr() == false)
+		return;
+
+	setBuffer (new DataBuffer);
+
+	switch (m_valueType)
+	{
+		case TYPE_Bool:
+		case TYPE_Int:
+			buffer()->writeDWord (DH_PushNumber);
+			buffer()->writeDWord (abs (value()));
+
+			if (value() < 0)
+				buffer()->writeDWord (DH_UnaryMinus);
+			break;
+
+		case TYPE_String:
+			buffer()->writeDWord (DH_PushStringIndex);
+			buffer()->writeDWord (value());
+			break;
+
+		case TYPE_Void:
+		case TYPE_Unknown:
+			assert (false);
+			break;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/expression.h	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,132 @@
+#ifndef BOTC_EXPRESSION_H
+#define BOTC_EXPRESSION_H
+#include "parser.h"
+
+class DataBuffer;
+class ExpressionSymbol;
+class ExpressionValue;
+class ExpressionOperator;
+
+// =============================================================================
+//
+named_enum ExpressionOperatorType
+{
+	OPER_NegateLogical,
+	OPER_UnaryMinus,
+	OPER_Multiplication,
+	OPER_Division,
+	OPER_Modulus,
+	OPER_Addition,
+	OPER_Subtraction,
+	OPER_LeftShift,
+	OPER_RightShift,
+	OPER_CompareLesser,
+	OPER_CompareGreater,
+	OPER_CompareAtLeast,
+	OPER_CompareAtMost,
+	OPER_CompareEquals,
+	OPER_CompareNotEquals,
+	OPER_BitwiseAnd,
+	OPER_BitwiseXOr,
+	OPER_BitwiseOr,
+	OPER_LogicalAnd,
+	OPER_LogicalOr,
+	OPER_Ternary,
+};
+
+// =============================================================================
+//
+enum ExpressionSymbolType
+{
+	EXPRSYM_Operator,
+	EXPRSYM_Value,
+	EXPRSYM_Colon,
+};
+
+class Expression final
+{
+	public:
+		using SymbolList = List<ExpressionSymbol*>;
+
+		Expression (BotscriptParser* parser, Lexer* lx, DataType reqtype);
+		~Expression();
+		ExpressionValue*		getResult();
+
+	private:
+		BotscriptParser*		m_parser;
+		Lexer*					m_lexer;
+		SymbolList				m_symbols;
+		DataType				m_type;
+		String					m_badTokenText;
+
+		ExpressionValue*		evaluate(); // Process the expression and yield a result
+		ExpressionSymbol*		parseSymbol();
+		String					getTokenString();
+		void					adjustOperators();
+		void					verify(); // Ensure the expr is valid
+		void					tryVerifyValue (bool* verified, SymbolList::Iterator it);
+		ExpressionValue*		evaluateOperator (const ExpressionOperator* op,
+												  const List<ExpressionValue*>& values);
+		SymbolList::Iterator	findPrioritizedOperator();
+};
+
+// =============================================================================
+//
+class ExpressionSymbol
+{
+	public:
+		ExpressionSymbol (ExpressionSymbolType type) :
+			m_type (type) {}
+
+	PROPERTY (private, ExpressionSymbolType, type, setType, STOCK_WRITE)
+};
+
+// =============================================================================
+//
+class ExpressionOperator final : public ExpressionSymbol
+{
+	PROPERTY (public, ExpressionOperatorType, id, setID, STOCK_WRITE)
+
+	public:
+		ExpressionOperator (ExpressionOperatorType id);
+};
+
+// =============================================================================
+//
+class ExpressionValue final : public ExpressionSymbol
+{
+	PROPERTY (public, int,			value,		setValue,		STOCK_WRITE)
+	PROPERTY (public, DataBuffer*,	buffer,		setBuffer,		STOCK_WRITE)
+	PROPERTY (public, DataType,		valueType,	setValueType,	STOCK_WRITE)
+
+	public:
+		ExpressionValue (DataType valuetype);
+		~ExpressionValue();
+
+		void					convertToBuffer();
+
+		inline ExpressionValue* clone() const
+		{
+			return new ExpressionValue (*this);
+		}
+
+		inline bool isConstexpr() const
+		{
+			return buffer() == null;
+		}
+};
+
+// =============================================================================
+//
+// This class represents a ":" in the expression. It serves as the colon for the
+// ternary ?: OPER_erator. It's not an OPER_erand nor is an OPER_erator, nor can we just
+// skip it so it is its own thing here.
+//
+class ExpressionColon final : public ExpressionSymbol
+{
+	public:
+		ExpressionColon() :
+			ExpressionSymbol (EXPRSYM_Colon) {}
+};
+
+#endif // BOTC_EXPRESSION_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/format.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,116 @@
+/*
+	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 formatError (String fmtstr, const String errdescribe, int pos)
+{
+	fmtstr.replace ("\n", " ");
+	fmtstr.replace ("\t", " ");
+	String errmsg ("With format string:\n" + fmtstr + "\n");
+
+	for (int x = 0; x < pos; ++x)
+		errmsg += "-";
+
+	errmsg += "^\n" + errdescribe;
+	throw std::logic_error (errmsg.stdString());
+}
+
+// =============================================================================
+//
+String formatArgs (const String& fmtstr, const std::vector<String>& args)
+{
+	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' || fmt[pos + ofs] == 'd')
+		{
+			mod = fmt[pos + ofs];
+			ofs++;
+		}
+
+		if (!isdigit (fmt[pos + ofs]))
+			formatError (fmtstr, "bad format string, expected digit with optional "
+				"modifier after '%%'", pos);
+
+		int i = fmt[pos + ofs]  - '0';
+
+		if (i > static_cast<signed> (args.size()))
+			formatError (fmtstr, String ("Format argument #") + i + " used but not defined.", pos);
+
+		String replacement = args[i - 1];
+
+		switch (mod)
+		{
+			case 's': replacement = (replacement == "1") ? "" : "s";		break;
+			case 'd': replacement.sprintf ("%d", replacement[0]);			break;
+			case 'x': replacement.sprintf ("0x%X", replacement.toLong());	break;
+			default: break;
+		}
+
+		fmt.replace (pos, 1 + ofs, replacement);
+		pos += replacement.length();
+	}
+
+	return fmt;
+}
+
+// =============================================================================
+//
+void error (String msg)
+{
+	Lexer* lx = Lexer::getCurrentLexer();
+	String fileinfo;
+
+	if (lx != null && lx->hasValidToken())
+	{
+		Lexer::TokenInfo* tk = lx->token();
+		fileinfo = format ("%1:%2:%3: ", tk->file, tk->line, tk->column);
+	}
+
+	throw std::runtime_error (fileinfo + msg);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/format.h	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,231 @@
+/*
+	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 "list.h"
+
+class FormatArgument
+{
+	public:
+		FormatArgument (const String& a) : m_text (a) {}
+		FormatArgument (char a) : m_text (a) {}
+		FormatArgument (int a) : m_text (String::fromNumber (a)) {}
+		FormatArgument (long a) : m_text (String::fromNumber (a)) {}
+		FormatArgument (const char* a) : m_text (a) {}
+
+		FormatArgument (void* a)
+		{
+			m_text.sprintf ("%p", a);
+		}
+
+		FormatArgument (const void* a)
+		{
+			m_text.sprintf ("%p", a);
+		}
+
+		template<class T> FormatArgument (const List<T>& list)
+		{
+			if (list.isEmpty())
+			{
+				m_text = "{}";
+				return;
+			}
+
+			m_text = "{ ";
+
+			for (const T& a : list)
+			{
+				if (&a != &list[0])
+					m_text += ", ";
+
+				m_text += FormatArgument (a).text();
+			}
+
+			m_text += " }";
+		}
+
+		inline const String& text() const
+		{
+			return m_text;
+		}
+
+	private:
+		String m_text;
+};
+
+#ifndef IN_IDE_PARSER
+# ifdef DEBUG
+#  define devf(...) PrintTo (stderr, __VA_ARGS__)
+#  define dvalof( A ) PrintTo (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
+
+
+/**
+ * Formats the given string with the given args.
+ *
+ * @param fmtstr Formatter string to process.
+ * @param args Args to format with the string.
+ * @see format()
+ */
+String formatArgs (const String& fmtstr, const std::vector<String>& args);
+
+/**
+ * Expands the given arguments into a vector of strings.
+ *
+ * @param data Where to insert the strings.
+ * @param arg First argument to process
+ * @param rest... Rest of the arguments.
+ */
+template<typename T, typename... RestTypes>
+void expandFormatArguments (std::vector<String>& data, const T& arg, const RestTypes& ... rest)
+{
+	data.push_back (FormatArgument (arg).text());
+	expandFormatArguments (data, rest...);
+}
+
+/**
+ * This is an overload of @c ExpandFormatArguments for end-of-args support.
+ */
+static void expandFormatArguments (std::vector<String>& data) __attribute__ ( (unused));
+static void expandFormatArguments (std::vector<String>& data)
+{
+	(void) data;
+}
+
+/**
+ * Formats the given formatter string and args and returns the string.
+ * This is essentially a modernized sprintf.
+ *
+ * Args in the format string take the form %n where n is a digit. The argument
+ * will be expanded to the nth argument passed. This is essentially Qt's
+ * QString::arg() syntax. Note: %0 is invalid.
+ *
+ * Arguments can be passed a modifier which takes the form of a character
+ * just before the digit. Currently supported modifiers are s, d and x.
+ *
+ * - s: The argument will expand into "s" if it would've not expanded into "1"
+ *      otherwise. If it would have expanded into "1" it will expand into an
+ *      empty string.
+ *
+ * - d: The argument expands into the numeric form of the first character of
+ *      its previous expansion. Use this to get numeric forms of @c char
+ *      arguments.
+ *
+ * - x: The numeric argument will be represented in hexadecimal notation. This
+ *      will work if the argument is a string representing a number. If the
+ *      argument did not expand into a number in the first place, 0 is used
+ *      (and 0x0 is printed).
+ *
+ * @param fmtstr Formatter string to process
+ * @param raw_args Arguments for the formatter string.
+ * @return the formatted string.
+ * @see Print
+ * @see PrintTo
+ */
+template<typename... argtypes>
+String format (const String& fmtstr, const argtypes&... raw_args)
+{
+	std::vector<String> args;
+	expandFormatArguments (args, raw_args...);
+	assert (args.size() == sizeof... (raw_args));
+	return formatArgs (fmtstr, args);
+}
+
+/**
+ * This is an overload of @c format where no arguments are supplied.
+ * @return the formatter string as-is.
+ */
+static String format (const String& fmtstr) __attribute__ ( (unused));
+static String format (const String& fmtstr)
+{
+	return fmtstr;
+}
+
+/**
+ * Processes the given formatter string using @c format and prints it to the
+ * specified file pointer.
+ *
+ * @param fp File pointer to print the formatted string to
+ * @param fmtstr Formatter string for @c format
+ * @param args Arguments for @c fmtstr
+ */
+template<typename... argtypes>
+void printTo (FILE* fp, const String& fmtstr, const argtypes&... args)
+{
+	fprintf (fp, "%s", format (fmtstr, args...).c_str());
+}
+
+/**
+ * Processes the given formatter string using @c format and prints the result to
+ * @c stdout.
+ *
+ * @param fmtstr Formatter string for @c format
+ * @param args Arguments for @c fmtstr
+ */
+template<typename... argtypes>
+void print (const String& fmtstr, const argtypes&... args)
+{
+	printTo (stdout, fmtstr, args...);
+}
+
+/**
+ * Throws an std::runtime_error with the processed formatted string. The program
+ * execution terminates after a call to this function as the exception is first
+ * caught in @c main which prints the error to stderr and then exits.
+ *
+ * @param fmtstr The formatter string of the error.
+ * @param args The args to the formatter string.
+ * @see Format
+ */
+template<typename... argtypes>
+void error (const String& fmtstr, const argtypes&... args)
+{
+	error (format (fmtstr, args...));
+}
+
+/**
+ * An overload of @c Error with no string formatting in between.
+ *
+ * @param msg The error message.
+ */
+void error (String msg);
+
+#endif // BOTC_FORMAT_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lexer.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,344 @@
+/*
+	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() ==TK_Hash)
+		{
+			mustGetFromScanner (sc,TK_Symbol);
+
+			if (sc.getTokenText() == "include")
+			{
+				mustGetFromScanner (sc,TK_String);
+				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
+		{
+			TokenInfo 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),
+			// 	GetTokenTypeString (tok.type));
+
+			m_tokens << tok;
+		}
+	}
+
+	m_tokenPosition = m_tokens.begin() - 1;
+	gFileNameStack.removeOne (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), versionString (false));
+
+	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::next (ETokenType req)
+{
+	Iterator pos = m_tokenPosition;
+
+	if (m_tokens.isEmpty())
+		return false;
+
+	m_tokenPosition++;
+
+	if (isAtEnd() || (req !=TK_Any && tokenType() != req))
+	{
+		m_tokenPosition = pos;
+		return false;
+	}
+
+	return true;
+}
+
+// =============================================================================
+//
+void Lexer::mustGetNext (ETokenType tok)
+{
+	if (!next())
+		error ("unexpected EOF");
+
+	if (tok !=TK_Any)
+		tokenMustBe (tok);
+}
+
+// =============================================================================
+// eugh..
+//
+void Lexer::mustGetFromScanner (LexerScanner& sc, ETokenType tt)
+{
+	if (sc.getNextToken() == false)
+		error ("unexpected EOF");
+
+	if (tt != TK_Any && sc.getTokenType() != tt)
+	{
+		// TODO
+		TokenInfo 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<ETokenType>& toks)
+{
+	if (!next())
+		error ("unexpected EOF");
+
+	for (ETokenType tok : toks)
+		if (tokenType() == tok)
+			return;
+
+	String toknames;
+
+	for (const ETokenType& tokType : toks)
+	{
+		if (&tokType == &toks.last())
+			toknames += " or ";
+		elif (toknames.isEmpty() == false)
+			toknames += ", ";
+
+		toknames += describeTokenType (tokType);
+	}
+
+	error ("expected %1, got %2", toknames, describeToken (token()));
+}
+
+// =============================================================================
+//
+int Lexer::getOneSymbol (const StringList& syms)
+{
+	if (!next())
+		error ("unexpected EOF");
+
+	if (tokenType() ==TK_Symbol)
+	{
+		for (int i = 0; i < syms.size(); ++i)
+		{
+			if (syms[i] == token()->text)
+				return i;
+		}
+	}
+
+	error ("expected one of %1, got %2", syms, describeToken (token()));
+	return -1;
+}
+
+// =============================================================================
+//
+void Lexer::tokenMustBe (ETokenType tok)
+{
+	if (tokenType() != tok)
+		error ("expected %1, got %2", describeTokenType (tok),
+			describeToken (token()));
+}
+
+// =============================================================================
+//
+String Lexer::describeTokenPrivate (ETokenType tokType, Lexer::TokenInfo* tok)
+{
+	if (tokType <gLastNamedToken)
+		return "\"" + LexerScanner::getTokenString (tokType) + "\"";
+
+	switch (tokType)
+	{
+		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::peekNext (Lexer::TokenInfo* tk)
+{
+	Iterator pos = m_tokenPosition;
+	bool r = next();
+
+	if (r && tk != null)
+		*tk = *m_tokenPosition;
+
+	m_tokenPosition = pos;
+	return r;
+}
+
+// =============================================================================
+//
+bool Lexer::peekNextType (ETokenType req)
+{
+	Iterator pos = m_tokenPosition;
+	bool result = false;
+
+	if (next() && tokenType() == req)
+		result = true;
+
+	m_tokenPosition = pos;
+	return result;
+}
+
+// =============================================================================
+//
+Lexer* Lexer::getCurrentLexer()
+{
+	return gMainLexer;
+}
+
+// =============================================================================
+//
+String Lexer::peekNextString (int a)
+{
+	if (m_tokenPosition + a >= m_tokens.end())
+		return "";
+
+	Iterator oldpos = m_tokenPosition;
+	m_tokenPosition += a;
+	String result = token()->text;
+	m_tokenPosition = oldpos;
+	return result;
+}
+
+// =============================================================================
+//
+String Lexer::describeCurrentPosition()
+{
+	return token()->file + ":" + token()->line;
+}
+
+// =============================================================================
+//
+String Lexer::describeTokenPosition()
+{
+	return format ("%1 / %2", m_tokenPosition - m_tokens.begin(), m_tokens.size());
+}
+
+// =============================================================================
+//
+void Lexer::mustGetSymbol (const String& a)
+{
+	mustGetNext (TK_Any);
+	if (token()->text != a)
+		error ("expected \"%1\", got \"%2\"", a, token()->text);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lexer.h	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,127 @@
+/*
+	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
+{
+public:
+	struct TokenInfo
+	{
+		ETokenType	type;
+		String		text;
+		String		file;
+		int			line;
+		int			column;
+	};
+
+	using TokenList	= List<TokenInfo>;
+	using Iterator	= TokenList::Iterator;
+
+public:
+	Lexer();
+	~Lexer();
+
+	void	processFile (String fileName);
+	bool	next (ETokenType req = TK_Any);
+	void	mustGetNext (ETokenType tok);
+	void	mustGetAnyOf (const List<ETokenType>& toks);
+	void	mustGetSymbol (const String& a);
+	int		getOneSymbol (const StringList& syms);
+	void	tokenMustBe (ETokenType tok);
+	bool	peekNext (TokenInfo* tk = null);
+	bool	peekNextType (ETokenType req);
+	String	peekNextString (int a = 1);
+	String	describeCurrentPosition();
+	String	describeTokenPosition();
+
+	static Lexer* getCurrentLexer();
+
+	inline bool hasValidToken() const
+	{
+		return (m_tokenPosition < m_tokens.end() && m_tokenPosition >= m_tokens.begin());
+	}
+
+	inline TokenInfo* token() const
+	{
+		assert (hasValidToken() == true);
+		return &(*m_tokenPosition);
+	}
+
+	inline bool isAtEnd() const
+	{
+		return m_tokenPosition == m_tokens.end();
+	}
+
+	inline ETokenType tokenType() const
+	{
+		return token()->type;
+	}
+
+	inline void skip (int a = 1)
+	{
+		m_tokenPosition += a;
+	}
+
+	inline int position()
+	{
+		return m_tokenPosition - m_tokens.begin();
+	}
+
+	inline void setPosition (int pos)
+	{
+		m_tokenPosition = m_tokens.begin() + pos;
+	}
+
+	// If @tok is given, describes the token. If not, describes @tok_type.
+	static inline String describeTokenType (ETokenType toktype)
+	{
+		return describeTokenPrivate (toktype, null);
+	}
+
+	static inline String describeToken (TokenInfo* tok)
+	{
+		return describeTokenPrivate (tok->type, tok);
+	}
+
+private:
+	TokenList		m_tokens;
+	Iterator		m_tokenPosition;
+
+	// read a mandatory token from scanner
+	void mustGetFromScanner (LexerScanner& sc, ETokenType tt =TK_Any);
+	void checkFileHeader (LexerScanner& sc);
+
+	static String describeTokenPrivate (ETokenType tok_type, TokenInfo* tok);
+};
+
+#endif // BOTC_LEXER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lexerScanner.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,308 @@
+/*
+	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",
+	"constexpr",
+	"default",
+	"do",
+	"else",
+	"event",
+	"eventdef",
+	"for",
+	"funcdef",
+	"if",
+	"int",
+	"mainloop",
+	"onenter",
+	"onexit",
+	"state",
+	"switch",
+	"str",
+	"using",
+	"var",
+	"void",
+	"while",
+	"true",
+	"false",
+	"enum",
+	"func",
+	"return",
+};
+
+static_assert (countof (gTokenStrings) == (int)gLastNamedToken + 1,
+	"Count of gTokenStrings is not the same as the amount of named token identifiers.");
+
+// =============================================================================
+//
+LexerScanner::LexerScanner (FILE* fp) :
+	m_line (1)
+{
+	long fsize, bytes;
+
+	fseek (fp, 0l, SEEK_END);
+	fsize = ftell (fp);
+	rewind (fp);
+	m_data = new char[fsize];
+	m_position = m_lineBreakPosition = &m_data[0];
+	bytes = fread (m_data, 1, fsize, fp);
+	assert (bytes >= fsize);
+}
+
+// =============================================================================
+//
+LexerScanner::~LexerScanner()
+{
+	delete m_data;
+}
+
+// =============================================================================
+//
+bool LexerScanner::checkString (const char* c, int flags)
+{
+	bool r = strncmp (m_position, c, strlen (c)) == 0;
+
+	// There is to be a non-symbol character after words
+	if (r && (flags & FCheckWord) && isSymbolChar (m_position[strlen (c)], true))
+		r = false;
+
+	// Advance the cursor unless we want to just peek
+	if (r && !(flags & FCheckPeek))
+		m_position += strlen (c);
+
+	return r;
+}
+
+// =============================================================================
+//
+bool LexerScanner::getNextToken()
+{
+	m_tokenText = "";
+
+	while (isspace (*m_position))
+		skip();
+
+	// Check for comments
+	if (strncmp (m_position, "//", 2) == 0)
+	{
+		m_position += 2;
+
+		while (*m_position != '\n')
+			skip();
+
+		return getNextToken();
+	}
+	elif (strncmp (m_position, "/*", 2) == 0)
+	{
+		skip (2); // skip the start symbols
+
+		while (strncmp (m_position, "*/", 2) != 0)
+			skip();
+
+		skip (2); // skip the end symbols
+		return getNextToken();
+	}
+
+	if (*m_position == '\0')
+		return false;
+
+	// Check tokens
+	for (int i = 0; i < countof (gTokenStrings); ++i)
+	{
+		int flags = 0;
+
+		if (i >= gFirstNamedToken)
+			flags |= FCheckWord;
+
+		if (checkString (gTokenStrings[i], flags))
+		{
+			m_tokenText = gTokenStrings[i];
+			m_tokenType = (ETokenType) i;
+			return true;
+		}
+	}
+
+	// Check and parse string
+	if (*m_position == '\"')
+	{
+		m_position++;
+
+		while (*m_position != '\"')
+		{
+			if (!*m_position)
+				error ("unterminated string");
+
+			if (checkString ("\\n"))
+			{
+				m_tokenText += '\n';
+				continue;
+			}
+			elif (checkString ("\\t"))
+			{
+				m_tokenText += '\t';
+				continue;
+			}
+			elif (checkString ("\\\""))
+			{
+				m_tokenText += '"';
+				continue;
+			}
+
+			m_tokenText += *m_position++;
+		}
+
+		m_tokenType =TK_String;
+		skip(); // skip the final quote
+		return true;
+	}
+
+	if (isdigit (*m_position))
+	{
+		while (isdigit (*m_position))
+			m_tokenText += *m_position++;
+
+		m_tokenType =TK_Number;
+		return true;
+	}
+
+	if (isSymbolChar (*m_position, false))
+	{
+		m_tokenType =TK_Symbol;
+
+		do
+		{
+			if (!isSymbolChar (*m_position, true))
+				break;
+
+			m_tokenText += *m_position++;
+		} while (*m_position != '\0');
+
+		return true;
+	}
+
+	error ("unknown character \"%1\"", *m_position);
+	return false;
+}
+
+// =============================================================================
+//
+void LexerScanner::skip()
+{
+	if (*m_position == '\n')
+	{
+		m_line++;
+		m_lineBreakPosition = m_position;
+	}
+
+	m_position++;
+}
+
+// =============================================================================
+//
+void LexerScanner::skip (int chars)
+{
+	for (int i = 0; i < chars; ++i)
+		skip();
+}
+
+// =============================================================================
+//
+String LexerScanner::getTokenString (ETokenType a)
+{
+	assert ((int) a <= gLastNamedToken);
+	return gTokenStrings[a];
+}
+
+// =============================================================================
+//
+String LexerScanner::readLine()
+{
+	String line;
+
+	while (*m_position != '\n')
+		line += *(m_position++);
+
+	return line;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lexerScanner.h	Sun Mar 30 21:51:23 2014 +0300
@@ -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 BOTC_LEXER_SCANNER_H
+#define BOTC_LEXER_SCANNER_H
+
+#include <climits>
+#include "main.h"
+
+class LexerScanner
+{
+	public:
+		struct PositionInfo
+		{
+			char*	pos;
+			int		line;
+		};
+
+		// Flags for check_string()
+		enum
+		{
+			FCheckWord = (1 << 0),   // must be followed by whitespace
+			FCheckPeek = (1 << 1),   // don't advance cursor
+		};
+
+		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 m_tokenText;
+		}
+
+		inline int getLine() const
+		{
+			return m_line;
+		}
+
+		inline int getColumn() const
+		{
+			return m_position - m_lineBreakPosition;
+		}
+
+		inline ETokenType getTokenType() const
+		{
+			return m_tokenType;
+		}
+
+		static String getTokenString (ETokenType a);
+
+	private:
+		char*			m_data;
+		char*			m_position;
+		char*			m_lineBreakPosition;
+		String			m_tokenText,
+						m_lastToken;
+		ETokenType		m_tokenType;
+		int				m_line;
+
+		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/list.h	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,358 @@
+/*
+	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<typename T>
+class List
+{
+	public:
+		using WrappedList				= typename std::deque<T>;
+		using Iterator					= typename WrappedList::iterator;
+		using ConstIterator				= typename WrappedList::const_iterator;
+		using ReverseIterator			= typename WrappedList::reverse_iterator;
+		using ConstReverseIterator		= typename WrappedList::const_reverse_iterator;
+		using ValueType					= T;
+		using Self						= List<T>;
+
+		// =====================================================================
+		//
+		List() {}
+
+		// =====================================================================
+		//
+		List (std::initializer_list<ValueType> vals)
+		{
+			m_data = vals;
+		}
+
+		// =====================================================================
+		//
+		List (const WrappedList& 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 Self& other)
+		{
+			resize (size() + other.size());
+			std::copy (other.begin(), other.end(), begin() + other.size());
+		}
+
+		// =====================================================================
+		//
+		bool pop (T& val)
+		{
+			if (isEmpty())
+				return false;
+
+			val = m_data[size() - 1];
+			m_data.erase (m_data.end() - 1);
+			return true;
+		}
+
+		// =====================================================================
+		//
+		Self& operator<< (const T& value)
+		{
+			append (value);
+			return *this;
+		}
+
+		// =====================================================================
+		//
+		void operator<< (const Self& vals)
+		{
+			merge (vals);
+		}
+
+		// =====================================================================
+		//
+		bool operator>> (T& value)
+		{
+			return pop (value);
+		}
+
+		// =====================================================================
+		//
+		Self reverse() const
+		{
+			Self 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 removeOne (const ValueType& it)
+		{
+			int idx;
+
+			if ((idx = find (it)) != -1)
+				removeAt (idx);
+		}
+
+		// =====================================================================
+		//
+		inline bool isEmpty() const
+		{
+			return size() == 0;
+		}
+
+		// =====================================================================
+		//
+		Self splice (int a, int b) const
+		{
+			assert (a >= 0 && b >= 0 && a < size() && b < size() && a <= b);
+			Self result;
+
+			for (int i = a; i <= b; ++i)
+				result << operator[] (i);
+
+			return result;
+		}
+
+		// =====================================================================
+		//
+		inline const WrappedList& deque() 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;
+		}
+
+		// =====================================================================
+		//
+		Self operator+ (const Self& other) const
+		{
+			Self out (*this);
+			out.merge (other);
+			return out;
+		}
+
+	private:
+		WrappedList m_data;
+};
+
+// =============================================================================
+//
+template<typename T>
+List<T>& operator>> (const T& value, List<T>& haystack)
+{
+	haystack.prepend (value);
+	return haystack;
+}
+
+#endif // BOTC_CONTAINERS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/macros.h	Sun Mar 30 21:51:23 2014 +0300
@@ -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.
+*/
+
+#pragma once
+
+#if !defined (__cplusplus) || __cplusplus < 201103L
+# error botc requires a C++11-compliant compiler to be built
+#endif
+
+// 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 named_enum enum
+#define elif else if
+#define types public
+#define countof(A) ((int) (sizeof A / sizeof *A))
+
+#ifndef __GNUC__
+# define __attribute__(X)
+#endif
+
+#define deprecated __attribute__ ((deprecated))
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,193 @@
+/*
+	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 "dataBuffer.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 && 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->signature());
+
+			print ("------------------------------------------------------\n");
+			print ("End of command list\n");
+			exit (0);
+		}
+
+		// Print header
+		String header;
+		String headerline;
+		header = format (APPNAME " version %1", versionString (true));
+
+#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 = parser->getHighestVarIndex (true) + 1;
+		int statelocalcount = parser->getHighestVarIndex (false) + 1;
+		int stringcount = countStringsInTable();
+		print ("%1 / %2 strings\n", stringcount, gMaxStringlistSize);
+		print ("%1 / %2 global variable indices\n", globalcount, gMaxGlobalVars);
+		print ("%1 / %2 state variable indices\n", statelocalcount, gMaxGlobalVars);
+		print ("%1 / %2 events\n", parser->numEvents(), gMaxEvents);
+		print ("%1 state%s1\n", parser->numStates());
+
+		parser->writeToFile (outfile);
+		delete parser;
+		return 0;
+	}
+	catch (std::exception& e)
+	{
+		fprintf (stderr, "error: %s\n", e.what());
+		return 1;
+	}
+}
+
+// ============================================================================
+//
+// 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;
+}
+
+// ============================================================================
+//
+DataType getTypeByName (String token)
+{
+	token = token.toLowercase();
+	return	(token == "int") ? TYPE_Int :
+			(token == "str") ? TYPE_String :
+			(token == "void") ? TYPE_Void :
+			(token == "bool") ? TYPE_Bool :
+			TYPE_Unknown;
+}
+
+
+// ============================================================================
+//
+// Inverse operation - type name by value
+//
+String dataTypeName (DataType type)
+{
+	switch (type)
+	{
+		case TYPE_Int: return "int"; break;
+		case TYPE_String: return "str"; break;
+		case TYPE_Void: return "void"; break;
+		case TYPE_Bool: return "bool"; break;
+		case TYPE_Unknown: 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 versionString (bool longform)
+{
+	String tag (GIT_DESCRIPTION);
+	String version = tag;
+
+	if (longform && tag.endsWith ("-pre"))
+		version += "-" + String (GIT_HASH).mid (0, 8);
+
+	return version;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main.h	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,62 @@
+/*
+	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
+
+#include <cstdio>
+#include <cstdarg>
+#include <cstdint>
+#include "macros.h"
+#include "property.h"
+#include "types.h"
+#include "list.h"
+#include "string.h"
+#include "format.h"
+#include "botStuff.h"
+#include "tokens.h"
+
+String makeObjectFileName (String s);
+DataType getTypeByName (String token);
+String dataTypeName (DataType type);
+String versionString (bool longform);
+String makeVersionString (int major, int minor, int patch);
+
+template<typename T>
+inline T max (T a, T b)
+{
+	return a > b ? a : b;
+}
+
+template<typename T>
+inline T min (T a, T b)
+{
+	return a < b ? a : b;
+}
+
+#endif // BOTC_MAIN_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/parser.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,1417 @@
+/*
+	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 "list.h"
+#include "lexer.h"
+#include "dataBuffer.h"
+#include "expression.h"
+
+#define SCOPE(n) (m_scopeStack[m_scopeCursor - n])
+
+static const StringList g_validZandronumVersions = {"1.2", "1.3", "2.0"};
+
+// ============================================================================
+//
+BotscriptParser::BotscriptParser() :
+	m_isReadOnly (false),
+	m_mainBuffer (new DataBuffer),
+	m_onenterBuffer (new DataBuffer),
+	m_mainLoopBuffer (new DataBuffer),
+	m_lexer (new Lexer),
+	m_numStates (0),
+	m_numEvents (0),
+	m_currentMode (PARSERMODE_TopLevel),
+	m_isStateSpawnDefined (false),
+	m_gotMainLoop (false),
+	m_scopeCursor (-1),
+	m_isElseAllowed (false),
+	m_highestGlobalVarIndex (0),
+	m_highestStateVarIndex (0),
+	m_zandronumVersion (10200), // 1.2
+	m_defaultZandronumVersion (true) {}
+
+// ============================================================================
+//
+BotscriptParser::~BotscriptParser()
+{
+	delete m_lexer;
+}
+
+// ============================================================================
+//
+void BotscriptParser::checkToplevel()
+{
+	if (m_currentMode != PARSERMODE_TopLevel)
+		error ("%1-statements may only be defined at top level!", getTokenString());
+}
+
+// ============================================================================
+//
+void BotscriptParser::checkNotToplevel()
+{
+	if (m_currentMode == PARSERMODE_TopLevel)
+		error ("%1-statements must not be defined at top level!", getTokenString());
+}
+
+// ============================================================================
+//
+// Main compiler 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
+	m_lexer->processFile (fileName);
+	pushScope();
+
+	while (m_lexer->next())
+	{
+		// Check if else is potentically valid
+		if (tokenIs (TK_Else) && m_isElseAllowed == false)
+			error ("else without preceding if");
+
+		if (tokenIs (TK_Else) == false)
+			m_isElseAllowed = false;
+
+		switch (m_lexer->token()->type)
+		{
+			case TK_State:
+				parseStateBlock();
+				break;
+
+			case TK_Event:
+				parseEventBlock();
+				break;
+
+			case TK_Mainloop:
+				parseMainloop();
+				break;
+
+			case TK_Onenter:
+			case TK_Onexit:
+				parseOnEnterExit();
+				break;
+
+			case TK_Var:
+				parseVar();
+				break;
+
+			case TK_If:
+				parseIf();
+				break;
+
+			case TK_Else:
+				parseElse();
+				break;
+
+			case TK_While:
+				parseWhileBlock();
+				break;
+
+			case TK_For:
+				parseForBlock();
+				break;
+
+			case TK_Do:
+				parseDoBlock();
+				break;
+
+			case TK_Switch:
+				parseSwitchBlock();
+				break;
+
+			case TK_Case:
+				parseSwitchCase();
+				break;
+
+			case TK_Default:
+				parseSwitchDefault();
+				break;
+
+			case TK_Break:
+				parseBreak();
+				break;
+
+			case TK_Continue:
+				parseContinue();
+				break;
+
+			case TK_BraceEnd:
+				parseBlockEnd();
+				break;
+
+			case TK_Eventdef:
+				parseEventdef();
+				break;
+
+			case TK_Funcdef:
+				parseFuncdef();
+				break;
+
+			case TK_Semicolon:
+				break;
+
+			case TK_Using:
+				parseUsing();
+				break;
+
+			default:
+			{
+				// Check if it's a command
+				CommandInfo* comm = findCommandByName (getTokenString());
+
+				if (comm)
+				{
+					currentBuffer()->mergeAndDestroy (parseCommand (comm));
+					m_lexer->mustGetNext (TK_Semicolon);
+					continue;
+				}
+
+				// If nothing else, parse it as a statement
+				m_lexer->skip (-1);
+				DataBuffer* b = parseStatement();
+
+				if (b == false)
+				{
+					m_lexer->next();
+					error ("unknown token `%1`", getTokenString());
+				}
+
+				currentBuffer()->mergeAndDestroy (b);
+				m_lexer->mustGetNext (TK_Semicolon);
+				break;
+			}
+		}
+	}
+
+	// ===============================================================================
+	// Script file ended. Do some last checks and write the last things to main buffer
+	if (m_currentMode != PARSERMODE_TopLevel)
+		error ("script did not end at top level; a `}` is missing somewhere");
+
+	if (isReadOnly() == false)
+	{
+		// stateSpawn must be defined!
+		if (m_isStateSpawnDefined == false)
+			error ("script must have a state named `stateSpawn`!");
+
+		if (m_defaultZandronumVersion)
+		{
+			print ("\n");
+			print ("note: use the 'using' directive to define a target Zandronum version\n");
+			print ("usage: using zandronum <version>, possible versions: %1\n", g_validZandronumVersions);
+			print ("\n");
+		}
+
+		// Dump the last state's onenter and mainloop
+		writeMemberBuffers();
+
+		// String table
+		writeStringTable();
+	}
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseStateBlock()
+{
+	checkToplevel();
+	m_lexer->mustGetNext (TK_String);
+	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.toLowercase() == "statespawn")
+		m_isStateSpawnDefined = true;
+
+	// Must end in a colon
+	m_lexer->mustGetNext (TK_Colon);
+
+	// write the previous state's onenter and
+	// mainloop buffers to file now
+	if (m_currentState.isEmpty() == false)
+		writeMemberBuffers();
+
+	currentBuffer()->writeDWord (DH_StateName);
+	currentBuffer()->writeString (statename);
+	currentBuffer()->writeDWord (DH_StateIndex);
+	currentBuffer()->writeDWord (m_numStates);
+
+	m_numStates++;
+	m_currentState = statename;
+	m_gotMainLoop = false;
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseEventBlock()
+{
+	checkToplevel();
+	m_lexer->mustGetNext (TK_String);
+
+	EventDefinition* e = findEventByName (getTokenString());
+
+	if (e == null)
+		error ("bad event, got `%1`\n", getTokenString());
+
+	m_lexer->mustGetNext (TK_BraceStart);
+	m_currentMode = PARSERMODE_Event;
+	currentBuffer()->writeDWord (DH_Event);
+	currentBuffer()->writeDWord (e->number);
+	m_numEvents++;
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseMainloop()
+{
+	checkToplevel();
+	m_lexer->mustGetNext (TK_BraceStart);
+
+	m_currentMode = PARSERMODE_MainLoop;
+	m_mainLoopBuffer->writeDWord (DH_MainLoop);
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseOnEnterExit()
+{
+	checkToplevel();
+	bool onenter = (tokenIs (TK_Onenter));
+	m_lexer->mustGetNext (TK_BraceStart);
+
+	m_currentMode = onenter ? PARSERMODE_Onenter : PARSERMODE_Onexit;
+	currentBuffer()->writeDWord (onenter ? DH_OnEnter : DH_OnExit);
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseVar()
+{
+	Variable* var = new Variable;
+	var->origin = m_lexer->describeCurrentPosition();
+	var->isarray = false;
+	const bool isconst = m_lexer->next (TK_Const);
+	m_lexer->mustGetAnyOf ({TK_Int,TK_Str,TK_Void});
+
+	DataType vartype =	(tokenIs (TK_Int)) ? TYPE_Int :
+					(tokenIs (TK_Str)) ? TYPE_String :
+					TYPE_Bool;
+
+	m_lexer->mustGetNext (TK_DollarSign);
+	m_lexer->mustGetNext (TK_Symbol);
+	String name = getTokenString();
+
+	if (m_lexer->next (TK_BracketStart))
+	{
+		m_lexer->mustGetNext (TK_BracketEnd);
+		var->isarray = true;
+
+		if (isconst)
+			error ("arrays cannot be const");
+	}
+
+	for (Variable* var : SCOPE(0).globalVariables + SCOPE(0).localVariables)
+	{
+		if (var->name == name)
+			error ("Variable $%1 is already declared on this scope; declared at %2",
+				var->name, var->origin);
+	}
+
+	var->name = name;
+	var->statename = "";
+	var->type = vartype;
+
+	if (isconst == false)
+	{
+		var->writelevel = WRITE_Mutable;
+	}
+	else
+	{
+		m_lexer->mustGetNext (TK_Assign);
+		Expression expr (this, m_lexer, vartype);
+
+		// If the expression was constexpr, we know its value and thus
+		// can store it in the variable.
+		if (expr.getResult()->isConstexpr())
+		{
+			var->writelevel = WRITE_Constexpr;
+			var->value = expr.getResult()->value();
+		}
+		else
+		{
+			// TODO: might need a VM-wise oninit for this...
+			error ("const variables must be constexpr");
+		}
+	}
+
+	// Assign an index for the variable if it is not constexpr. Constexpr
+	// variables can simply be substituted out for their value when used
+	// so they need no index.
+	if (var->writelevel != WRITE_Constexpr)
+	{
+		bool isglobal = isInGlobalState();
+		var->index = isglobal ? SCOPE(0).globalVarIndexBase++ : SCOPE(0).localVarIndexBase++;
+
+		if ((isglobal == true && var->index >= gMaxGlobalVars) ||
+			(isglobal == false && var->index >= gMaxStateVars))
+		{
+			error ("too many %1 variables", isglobal ? "global" : "state-local");
+		}
+	}
+
+	if (isInGlobalState())
+		SCOPE(0).globalVariables << var;
+	else
+		SCOPE(0).localVariables << var;
+
+	suggestHighestVarIndex (isInGlobalState(), var->index);
+	m_lexer->mustGetNext (TK_Semicolon);
+	print ("Declared %3 variable #%1 $%2\n", var->index, var->name, isInGlobalState() ? "global" : "state-local");
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseIf()
+{
+	checkNotToplevel();
+	pushScope();
+
+	// Condition
+	m_lexer->mustGetNext (TK_ParenStart);
+
+	// Read the expression and write it.
+	DataBuffer* c = parseExpression (TYPE_Int);
+	currentBuffer()->mergeAndDestroy (c);
+
+	m_lexer->mustGetNext (TK_ParenEnd);
+	m_lexer->mustGetNext (TK_BraceStart);
+
+	// Add a mark - to here temporarily - and add a reference to it.
+	// Upon a closing brace, the mark will be adjusted.
+	ByteMark* mark = currentBuffer()->addMark ("");
+
+	// Use DH_IfNotGoto - if the expression is not true, we goto the mark
+	// we just defined - and this mark will be at the end of the scope block.
+	currentBuffer()->writeDWord (DH_IfNotGoto);
+	currentBuffer()->addReference (mark);
+
+	// Store it
+	SCOPE (0).mark1 = mark;
+	SCOPE (0).type = SCOPE_If;
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseElse()
+{
+	checkNotToplevel();
+	m_lexer->mustGetNext (TK_BraceStart);
+	pushScope (eNoReset);
+
+	if (SCOPE (0).type != SCOPE_If)
+		error ("else without preceding if");
+
+	// write down to jump to the end of the else statement
+	// Otherwise we have fall-throughs
+	SCOPE (0).mark2 = currentBuffer()->addMark ("");
+
+	// Instruction to jump to the end after if block is complete
+	currentBuffer()->writeDWord (DH_Goto);
+	currentBuffer()->addReference (SCOPE (0).mark2);
+
+	// Move the ifnot mark here and set type to else
+	currentBuffer()->adjustMark (SCOPE (0).mark1);
+	SCOPE (0).type = SCOPE_Else;
+}
+
+// ============================================================================
+//
+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 = currentBuffer()->addMark (""); // start
+	ByteMark* mark2 = currentBuffer()->addMark (""); // end
+
+	// Condition
+	m_lexer->mustGetNext (TK_ParenStart);
+	DataBuffer* expr = parseExpression (TYPE_Int);
+	m_lexer->mustGetNext (TK_ParenEnd);
+	m_lexer->mustGetNext (TK_BraceStart);
+
+	// write condition
+	currentBuffer()->mergeAndDestroy (expr);
+
+	// Instruction to go to the end if it fails
+	currentBuffer()->writeDWord (DH_IfNotGoto);
+	currentBuffer()->addReference (mark2);
+
+	// Store the needed stuff
+	SCOPE (0).mark1 = mark1;
+	SCOPE (0).mark2 = mark2;
+	SCOPE (0).type = SCOPE_While;
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseForBlock()
+{
+	checkNotToplevel();
+	pushScope();
+
+	// Initializer
+	m_lexer->mustGetNext (TK_ParenStart);
+	DataBuffer* init = parseStatement();
+
+	if (init == null)
+		error ("bad statement for initializer of for");
+
+	m_lexer->mustGetNext (TK_Semicolon);
+
+	// Condition
+	DataBuffer* cond = parseExpression (TYPE_Int);
+
+	if (cond == null)
+		error ("bad statement for condition of for");
+
+	m_lexer->mustGetNext (TK_Semicolon);
+
+	// Incrementor
+	DataBuffer* incr = parseStatement();
+
+	if (incr == null)
+		error ("bad statement for incrementor of for");
+
+	m_lexer->mustGetNext (TK_ParenEnd);
+	m_lexer->mustGetNext (TK_BraceStart);
+
+	// First, write out the initializer
+	currentBuffer()->mergeAndDestroy (init);
+
+	// Init two marks
+	ByteMark* mark1 = currentBuffer()->addMark ("");
+	ByteMark* mark2 = currentBuffer()->addMark ("");
+
+	// Add the condition
+	currentBuffer()->mergeAndDestroy (cond);
+	currentBuffer()->writeDWord (DH_IfNotGoto);
+	currentBuffer()->addReference (mark2);
+
+	// Store the marks and incrementor
+	SCOPE (0).mark1 = mark1;
+	SCOPE (0).mark2 = mark2;
+	SCOPE (0).buffer1 = incr;
+	SCOPE (0).type = SCOPE_For;
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseDoBlock()
+{
+	checkNotToplevel();
+	pushScope();
+	m_lexer->mustGetNext (TK_BraceStart);
+	SCOPE (0).mark1 = currentBuffer()->addMark ("");
+	SCOPE (0).type = SCOPE_Do;
+}
+
+// ============================================================================
+//
+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();
+	m_lexer->mustGetNext (TK_ParenStart);
+	currentBuffer()->mergeAndDestroy (parseExpression (TYPE_Int));
+	m_lexer->mustGetNext (TK_ParenEnd);
+	m_lexer->mustGetNext (TK_BraceStart);
+	SCOPE (0).type = SCOPE_Switch;
+	SCOPE (0).mark1 = currentBuffer()->addMark (""); // end mark
+	SCOPE (0).buffer1 = null; // default header
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseSwitchCase()
+{
+	// case is only allowed inside switch
+	if (SCOPE (0).type != SCOPE_Switch)
+		error ("case label outside switch");
+
+	// Get a literal value for the case block. Zandronum does not support
+	// expressions here.
+	m_lexer->mustGetNext (TK_Number);
+	int num = m_lexer->token()->text.toLong();
+	m_lexer->mustGetNext (TK_Colon);
+
+	for (const CaseInfo& info : SCOPE(0).cases)
+		if (info.number == num)
+			error ("multiple case %1 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_switchBuffer = null;
+	currentBuffer()->writeDWord (DH_CaseGoto);
+	currentBuffer()->writeDWord (num);
+	addSwitchCase (null);
+	SCOPE (0).casecursor->number = num;
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseSwitchDefault()
+{
+	if (SCOPE (0).type != SCOPE_Switch)
+		error ("default label outside switch");
+
+	if (SCOPE (0).buffer1 != null)
+		error ("multiple default labels in one switch");
+
+	m_lexer->mustGetNext (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.
+	DataBuffer* buf = new DataBuffer;
+	SCOPE (0).buffer1 = buf;
+	buf->writeDWord (DH_Drop);
+	buf->writeDWord (DH_Goto);
+	addSwitchCase (buf);
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseBreak()
+{
+	if (m_scopeCursor == 0)
+		error ("unexpected `break`");
+
+	currentBuffer()->writeDWord (DH_Goto);
+
+	// switch and if use mark1 for the closing point,
+	// for and while use mark2.
+	switch (SCOPE (0).type)
+	{
+		case SCOPE_If:
+		case SCOPE_Switch:
+		{
+			currentBuffer()->addReference (SCOPE (0).mark1);
+		} break;
+
+		case SCOPE_For:
+		case SCOPE_While:
+		{
+			currentBuffer()->addReference (SCOPE (0).mark2);
+		} break;
+
+		default:
+		{
+			error ("unexpected `break`");
+		} break;
+	}
+
+	m_lexer->mustGetNext (TK_Semicolon);
+}
+
+// ============================================================================
+//
+void BotscriptParser::parseContinue()
+{
+	m_lexer->mustGetNext (TK_Semicolon);
+
+	int curs;
+	bool found = false;
+
+	// Fall through the scope until we find a loop block
+	for (curs = m_scopeCursor; curs > 0 && !found; curs--)
+	{
+		switch (m_scopeStack[curs].type)
+		{
+			case SCOPE_For:
+			case SCOPE_While:
+			case SCOPE_Do:
+			{
+				currentBuffer()->writeDWord (DH_Goto);
+				currentBuffer()->addReference (m_scopeStack[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 (m_scopeCursor > 0)
+	{
+		switch (SCOPE (0).type)
+		{
+			case SCOPE_If:
+			{
+				// Adjust the closing mark.
+				currentBuffer()->adjustMark (SCOPE (0).mark1);
+
+				// We're returning from `if`, thus `else` follow
+				m_isElseAllowed = true;
+				break;
+			}
+
+			case SCOPE_Else:
+			{
+				// else instead uses mark1 for itself (so if expression
+				// fails, jump to else), mark2 means end of else
+				currentBuffer()->adjustMark (SCOPE (0).mark2);
+				break;
+			}
+
+			case SCOPE_For:
+			{	// write the incrementor at the end of the loop block
+				currentBuffer()->mergeAndDestroy (SCOPE (0).buffer1);
+			}
+			case SCOPE_While:
+			{	// write down the instruction to go back to the start of the loop
+				currentBuffer()->writeDWord (DH_Goto);
+				currentBuffer()->addReference (SCOPE (0).mark1);
+
+				// Move the closing mark here since we're at the end of the while loop
+				currentBuffer()->adjustMark (SCOPE (0).mark2);
+				break;
+			}
+
+			case SCOPE_Do:
+			{
+				m_lexer->mustGetNext (TK_While);
+				m_lexer->mustGetNext (TK_ParenStart);
+				DataBuffer* expr = parseExpression (TYPE_Int);
+				m_lexer->mustGetNext (TK_ParenEnd);
+				m_lexer->mustGetNext (TK_Semicolon);
+
+				// If the condition runs true, go back to the start.
+				currentBuffer()->mergeAndDestroy (expr);
+				currentBuffer()->writeDWord (DH_IfGoto);
+				currentBuffer()->addReference (SCOPE (0).mark1);
+				break;
+			}
+
+			case SCOPE_Switch:
+			{
+				// Switch closes. Move down to the record buffer of
+				// the lower block.
+				if (SCOPE (1).casecursor != SCOPE (1).cases.begin() - 1)
+					m_switchBuffer = SCOPE (1).casecursor->data;
+				else
+					m_switchBuffer = null;
+
+				// If there was a default in the switch, write its header down now.
+				// If not, write instruction to jump to the end of switch after
+				// the headers (thus won't fall-through if no case matched)
+				if (SCOPE (0).buffer1)
+					currentBuffer()->mergeAndDestroy (SCOPE (0).buffer1);
+				else
+				{
+					currentBuffer()->writeDWord (DH_Drop);
+					currentBuffer()->writeDWord (DH_Goto);
+					currentBuffer()->addReference (SCOPE (0).mark1);
+				}
+
+				// Go through all of the buffers we
+				// recorded down and write them.
+				for (CaseInfo& info : SCOPE (0).cases)
+				{
+					currentBuffer()->adjustMark (info.mark);
+					currentBuffer()->mergeAndDestroy (info.data);
+				}
+
+				// Move the closing mark here
+				currentBuffer()->adjustMark (SCOPE (0).mark1);
+				break;
+			}
+
+			case SCOPE_Unknown:
+				break;
+		}
+
+		// Descend down the stack
+		m_scopeCursor--;
+		return;
+	}
+
+	int dataheader =	(m_currentMode == PARSERMODE_Event) ? DH_EndEvent :
+						(m_currentMode == PARSERMODE_MainLoop) ? DH_EndMainLoop :
+						(m_currentMode == PARSERMODE_Onenter) ? DH_EndOnEnter :
+						(m_currentMode == PARSERMODE_Onexit) ? DH_EndOnExit : -1;
+
+	if (dataheader == -1)
+		error ("unexpected `}`");
+
+	// Data header must be written before mode is changed because
+	// onenter and mainloop go into special buffers, and we want
+	// the closing data headers into said buffers too.
+	currentBuffer()->writeDWord (dataheader);
+	m_currentMode = PARSERMODE_TopLevel;
+	m_lexer->next (TK_Semicolon);
+}
+
+// =============================================================================
+//
+void BotscriptParser::parseEventdef()
+{
+	EventDefinition* e = new EventDefinition;
+
+	m_lexer->mustGetNext (TK_Number);
+	e->number = getTokenString().toLong();
+	m_lexer->mustGetNext (TK_Colon);
+	m_lexer->mustGetNext (TK_Symbol);
+	e->name = m_lexer->token()->text;
+	m_lexer->mustGetNext (TK_ParenStart);
+	m_lexer->mustGetNext (TK_ParenEnd);
+	m_lexer->mustGetNext (TK_Semicolon);
+	addEvent (e);
+}
+
+// =============================================================================
+//
+void BotscriptParser::parseFuncdef()
+{
+	CommandInfo* comm = new CommandInfo;
+	comm->origin = m_lexer->describeCurrentPosition();
+
+	// Return value
+	m_lexer->mustGetAnyOf ({TK_Int,TK_Void,TK_Bool,TK_Str});
+	comm->returnvalue = getTypeByName (m_lexer->token()->text); // TODO
+	assert (comm->returnvalue != -1);
+
+	// Number
+	m_lexer->mustGetNext (TK_Number);
+	comm->number = m_lexer->token()->text.toLong();
+	m_lexer->mustGetNext (TK_Colon);
+
+	// Name
+	m_lexer->mustGetNext (TK_Symbol);
+	comm->name = m_lexer->token()->text;
+
+	// Arguments
+	m_lexer->mustGetNext (TK_ParenStart);
+	comm->minargs = 0;
+
+	while (m_lexer->peekNextType (TK_ParenEnd) == false)
+	{
+		if (comm->args.isEmpty() == false)
+			m_lexer->mustGetNext (TK_Comma);
+
+		CommandArgument arg;
+		m_lexer->mustGetAnyOf ({TK_Int,TK_Bool,TK_Str});
+		DataType type = getTypeByName (m_lexer->token()->text); // TODO
+		assert (type != -1 && type != TYPE_Void);
+		arg.type = type;
+
+		m_lexer->mustGetNext (TK_Symbol);
+		arg.name = m_lexer->token()->text;
+
+		// If this is an optional parameter, we need the default value.
+		if (comm->minargs < comm->args.size() || m_lexer->peekNextType (TK_Assign))
+		{
+			m_lexer->mustGetNext (TK_Assign);
+
+			switch (type)
+			{
+				case TYPE_Int:
+				case TYPE_Bool:
+					m_lexer->mustGetNext (TK_Number);
+					break;
+
+				case TYPE_String:
+					error ("string arguments cannot have default values");
+
+				case TYPE_Unknown:
+				case TYPE_Void:
+					break;
+			}
+
+			arg.defvalue = m_lexer->token()->text.toLong();
+		}
+		else
+			comm->minargs++;
+
+		comm->args << arg;
+	}
+
+	m_lexer->mustGetNext (TK_ParenEnd);
+	m_lexer->mustGetNext (TK_Semicolon);
+	addCommandDefinition (comm);
+}
+
+// ============================================================================
+//
+// Parses a using statement
+//
+void BotscriptParser::parseUsing()
+{
+	checkToplevel();
+	m_lexer->mustGetSymbol ("zandronum");
+	String versionText;
+
+	while (m_lexer->next() && (m_lexer->tokenType() == TK_Number || m_lexer->tokenType() == TK_Dot))
+		versionText += getTokenString();
+
+	// Note: at this point the lexer's pointing at the token after the version.
+	if (versionText.isEmpty())
+		error ("expected version string, got `%1`", getTokenString());
+	if (g_validZandronumVersions.contains (versionText) == false)
+		error ("unknown version string `%2`: valid versions: `%1`\n", g_validZandronumVersions, versionText);
+
+	StringList versionTokens = versionText.split (".");
+	m_zandronumVersion = versionTokens[0].toLong() * 10000 + versionTokens[1].toLong() * 100;
+	m_defaultZandronumVersion = false;
+	m_lexer->tokenMustBe (TK_Semicolon);
+}
+
+// ============================================================================/
+//
+// Parses a command call
+//
+DataBuffer* BotscriptParser::parseCommand (CommandInfo* comm)
+{
+	DataBuffer* r = new DataBuffer (64);
+
+	if (m_currentMode == PARSERMODE_TopLevel && comm->returnvalue == TYPE_Void)
+		error ("command call at top level");
+
+	m_lexer->mustGetNext (TK_ParenStart);
+	m_lexer->mustGetNext (TK_Any);
+
+	int curarg = 0;
+
+	for (;;)
+	{
+		if (tokenIs (TK_ParenEnd))
+		{
+			if (curarg < comm->minargs)
+				error ("too few arguments passed to %1\n\tusage is: %2",
+					comm->name, comm->signature());
+
+			break;
+		}
+
+		if (curarg >= comm->args.size())
+			error ("too many arguments (%3) passed to %1\n\tusage is: %2",
+				comm->name, comm->signature());
+
+		r->mergeAndDestroy (parseExpression (comm->args[curarg].type, true));
+		m_lexer->mustGetNext (TK_Any);
+
+		if (curarg < comm->minargs - 1)
+		{
+			m_lexer->tokenMustBe (TK_Comma);
+			m_lexer->mustGetNext (TK_Any);
+		}
+		else if (curarg < comm->args.size() - 1)
+		{
+			// Can continue, but can terminate as well.
+			if (tokenIs (TK_ParenEnd))
+			{
+				curarg++;
+				break;
+			}
+			else
+			{
+				m_lexer->tokenMustBe (TK_Comma);
+				m_lexer->mustGetNext (TK_Any);
+			}
+		}
+
+		curarg++;
+	}
+
+	// If the script skipped any optional arguments, fill in defaults.
+	while (curarg < comm->args.size())
+	{
+		r->writeDWord (DH_PushNumber);
+		r->writeDWord (comm->args[curarg].defvalue);
+		curarg++;
+	}
+
+	r->writeDWord (DH_Command);
+	r->writeDWord (comm->number);
+	r->writeDWord (comm->args.size());
+
+	return r;
+}
+
+// ============================================================================
+//
+String BotscriptParser::parseFloat()
+{
+	m_lexer->tokenMustBe (TK_Number);
+	String floatstring = getTokenString();
+	Lexer::TokenInfo tok;
+
+	// Go after the decimal point
+	if (m_lexer->peekNext (&tok) && tok.type ==TK_Dot)
+	{
+		m_lexer->skip();
+		m_lexer->mustGetNext (TK_Number);
+		floatstring += ".";
+		floatstring += getTokenString();
+	}
+
+	return floatstring;
+}
+
+// ============================================================================
+//
+// Parses an assignment operator.
+//
+AssignmentOperator BotscriptParser::parseAssignmentOperator()
+{
+	const List<ETokenType> tokens =
+	{
+		TK_Assign,
+		TK_AddAssign,
+		TK_SubAssign,
+		TK_MultiplyAssign,
+		TK_DivideAssign,
+		TK_ModulusAssign,
+		TK_DoublePlus,
+		TK_DoubleMinus,
+	};
+
+	m_lexer->mustGetAnyOf (tokens);
+
+	switch (m_lexer->tokenType())
+	{
+		case TK_Assign:			return ASSIGNOP_Assign;
+		case TK_AddAssign:		return ASSIGNOP_Add;
+		case TK_SubAssign:		return ASSIGNOP_Subtract;
+		case TK_MultiplyAssign:	return ASSIGNOP_Multiply;
+		case TK_DivideAssign:	return ASSIGNOP_Divide;
+		case TK_ModulusAssign:	return ASSIGNOP_Modulus;
+		case TK_DoublePlus:		return ASSIGNOP_Increase;
+		case TK_DoubleMinus:	return ASSIGNOP_Decrease;
+		default: break;
+	}
+
+	assert (false);
+	return (AssignmentOperator) 0;
+}
+
+// ============================================================================
+//
+struct AssignmentDataHeaderInfo
+{
+	AssignmentOperator	op;
+	DataHeader			local;
+	DataHeader			global;
+	DataHeader			array;
+};
+
+const AssignmentDataHeaderInfo gAssignmentDataHeaders[] =
+{
+	{ ASSIGNOP_Assign,		DH_AssignLocalVar,		DH_AssignGlobalVar,		DH_AssignGlobalArray },
+	{ ASSIGNOP_Add,			DH_AddLocalVar,			DH_AddGlobalVar,		DH_AddGlobalArray },
+	{ ASSIGNOP_Subtract,	DH_SubtractLocalVar,	DH_SubtractGlobalVar,	DH_SubtractGlobalArray },
+	{ ASSIGNOP_Multiply,	DH_MultiplyLocalVar,	DH_MultiplyGlobalVar,	DH_MultiplyGlobalArray },
+	{ ASSIGNOP_Divide,		DH_DivideLocalVar,		DH_DivideGlobalVar,		DH_DivideGlobalArray },
+	{ ASSIGNOP_Modulus,		DH_ModLocalVar,			DH_ModGlobalVar,		DH_ModGlobalArray },
+	{ ASSIGNOP_Increase,	DH_IncreaseLocalVar,	DH_IncreaseGlobalVar,	DH_IncreaseGlobalArray },
+	{ ASSIGNOP_Decrease,	DH_DecreaseLocalVar,	DH_DecreaseGlobalVar,	DH_DecreaseGlobalArray },
+};
+
+DataHeader BotscriptParser::getAssigmentDataHeader (AssignmentOperator op, Variable* var)
+{
+	for (const auto& a : gAssignmentDataHeaders)
+	{
+		if (a.op != op)
+			continue;
+
+		if (var->isarray)
+			return a.array;
+
+		if (var->IsGlobal())
+			return a.global;
+
+		return a.local;
+	}
+
+	error ("WTF: couldn't find data header for operator #%1", op);
+	return (DataHeader) 0;
+}
+
+// ============================================================================
+//
+// 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 (Variable* var)
+{
+	DataBuffer* retbuf = new DataBuffer;
+	DataBuffer* arrayindex = null;
+
+	if (var->writelevel != WRITE_Mutable)
+		error ("cannot alter read-only variable $%1", var->name);
+
+	if (var->isarray)
+	{
+		m_lexer->mustGetNext (TK_BracketStart);
+		Expression expr (this, m_lexer, TYPE_Int);
+		expr.getResult()->convertToBuffer();
+		arrayindex = expr.getResult()->buffer()->clone();
+		m_lexer->mustGetNext (TK_BracketEnd);
+	}
+
+	// Get an operator
+	AssignmentOperator oper = parseAssignmentOperator();
+
+	if (m_currentMode == PARSERMODE_TopLevel)
+		error ("can't alter variables at top level");
+
+	if (var->isarray)
+		retbuf->mergeAndDestroy (arrayindex);
+
+	// Parse the right operand
+	if (oper != ASSIGNOP_Increase && oper != ASSIGNOP_Decrease)
+	{
+		DataBuffer* expr = parseExpression (var->type);
+		retbuf->mergeAndDestroy (expr);
+	}
+
+#if 0
+	// <<= and >>= do not have data headers. Solution: expand them.
+	// a <<= b -> a = a << b
+	// a >>= b -> a = a >> b
+	retbuf->WriteDWord (var->IsGlobal() ? DH_PushGlobalVar : DH_PushLocalVar);
+	retbuf->WriteDWord (var->index);
+	retbuf->MergeAndDestroy (expr);
+	retbuf->WriteDWord ((oper == OPER_ASSIGNLEFTSHIFT) ? DH_LeftShift : DH_RightShift);
+	retbuf->WriteDWord (var->IsGlobal() ? DH_AssignGlobalVar : DH_AssignLocalVar);
+	retbuf->WriteDWord (var->index);
+#endif
+
+	DataHeader dh = getAssigmentDataHeader (oper, var);
+	retbuf->writeDWord (dh);
+	retbuf->writeDWord (var->index);
+	return retbuf;
+}
+
+// ============================================================================
+//
+void BotscriptParser::pushScope (EReset reset)
+{
+	m_scopeCursor++;
+
+	if (m_scopeStack.size() < m_scopeCursor + 1)
+	{
+		ScopeInfo newscope;
+		m_scopeStack << newscope;
+		reset = SCOPE_Reset;
+	}
+
+	if (reset == SCOPE_Reset)
+	{
+		ScopeInfo* info = &SCOPE (0);
+		info->type = SCOPE_Unknown;
+		info->mark1 = null;
+		info->mark2 = null;
+		info->buffer1 = null;
+		info->cases.clear();
+		info->casecursor = info->cases.begin() - 1;
+	}
+
+	// Reset variable stuff in any case
+	SCOPE(0).globalVarIndexBase = (m_scopeCursor == 0) ? 0 : SCOPE(1).globalVarIndexBase;
+	SCOPE(0).localVarIndexBase = (m_scopeCursor == 0) ? 0 : SCOPE(1).localVarIndexBase;
+
+	for (Variable* var : SCOPE(0).globalVariables + SCOPE(0).localVariables)
+		delete var;
+
+	SCOPE(0).localVariables.clear();
+	SCOPE(0).globalVariables.clear();
+}
+
+// ============================================================================
+//
+DataBuffer* BotscriptParser::parseExpression (DataType reqtype, bool fromhere)
+{
+	// hehe
+	if (fromhere == true)
+		m_lexer->skip (-1);
+
+	Expression expr (this, m_lexer, reqtype);
+	expr.getResult()->convertToBuffer();
+
+	// The buffer will be destroyed once the function ends so we need to
+	// clone it now.
+	return expr.getResult()->buffer()->clone();
+}
+
+// ============================================================================
+//
+DataBuffer* BotscriptParser::parseStatement()
+{
+	// If it's a variable, expect assignment.
+	if (m_lexer->next (TK_DollarSign))
+	{
+		m_lexer->mustGetNext (TK_Symbol);
+		Variable* var = findVariable (getTokenString());
+
+		if (var == null)
+			error ("unknown variable $%1", var->name);
+
+		return parseAssignment (var);
+	}
+
+	return null;
+}
+
+// ============================================================================
+//
+void BotscriptParser::addSwitchCase (DataBuffer* casebuffer)
+{
+	ScopeInfo* info = &SCOPE (0);
+	CaseInfo casedata;
+
+	// Init a mark for the case buffer
+	ByteMark* casemark = currentBuffer()->addMark ("");
+	casedata.mark = casemark;
+
+	// Add a reference to the mark. "case" and "default" both
+	// add the necessary bytecode before the reference.
+	if (casebuffer != null)
+		casebuffer->addReference (casemark);
+	else
+		currentBuffer()->addReference (casemark);
+
+	// Init a buffer for the case block and tell the object
+	// writer to record all written data to it.
+	casedata.data = m_switchBuffer = new DataBuffer;
+	SCOPE(0).cases << casedata;
+	info->casecursor++;
+}
+
+// ============================================================================
+//
+bool BotscriptParser::tokenIs (ETokenType a)
+{
+	return (m_lexer->tokenType() == a);
+}
+
+// ============================================================================
+//
+String BotscriptParser::getTokenString()
+{
+	return m_lexer->token()->text;
+}
+
+// ============================================================================
+//
+String BotscriptParser::describePosition() const
+{
+	Lexer::TokenInfo* tok = m_lexer->token();
+	return tok->file + ":" + String (tok->line) + ":" + String (tok->column);
+}
+
+// ============================================================================
+//
+DataBuffer* BotscriptParser::currentBuffer()
+{
+	if (m_switchBuffer != null)
+		return m_switchBuffer;
+
+	if (m_currentMode == PARSERMODE_MainLoop)
+		return m_mainLoopBuffer;
+
+	if (m_currentMode == PARSERMODE_Onenter)
+		return m_onenterBuffer;
+
+	return m_mainBuffer;
+}
+
+// ============================================================================
+//
+void BotscriptParser::writeMemberBuffers()
+{
+	// If there was no mainloop defined, write a dummy one now.
+	if (m_gotMainLoop == false)
+	{
+		m_mainLoopBuffer->writeDWord (DH_MainLoop);
+		m_mainLoopBuffer->writeDWord (DH_EndMainLoop);
+	}
+
+	// Write the onenter and mainloop buffers, in that order in particular.
+	for (DataBuffer** bufp : List<DataBuffer**> ({&m_onenterBuffer, &m_mainLoopBuffer}))
+	{
+		currentBuffer()->mergeAndDestroy (*bufp);
+
+		// Clear the buffer afterwards for potential next state
+		*bufp = new DataBuffer;
+	}
+
+	// Next state definitely has no mainloop yet
+	m_gotMainLoop = false;
+}
+
+// ============================================================================
+//
+// Write string table
+//
+void BotscriptParser::writeStringTable()
+{
+	int stringcount = countStringsInTable();
+
+	if (stringcount == 0)
+		return;
+
+	// Write header
+	m_mainBuffer->writeDWord (DH_StringList);
+	m_mainBuffer->writeDWord (stringcount);
+
+	// Write all strings
+	for (int i = 0; i < stringcount; i++)
+		m_mainBuffer->writeString (getStringTable()[i]);
+}
+
+// ============================================================================
+//
+// Write the compiled bytecode to a file
+//
+void BotscriptParser::writeToFile (String outfile)
+{
+	FILE* fp = fopen (outfile, "wb");
+
+	if (fp == null)
+		error ("couldn't open %1 for writing: %2", outfile, strerror (errno));
+
+	// First, resolve references
+	for (MarkReference* ref : m_mainBuffer->references())
+		for (int i = 0; i < 4; ++i)
+			m_mainBuffer->buffer()[ref->pos + i] = (ref->target->pos >> (8 * i)) & 0xFF;
+
+	// Then, dump the main buffer to the file
+	fwrite (m_mainBuffer->buffer(), 1, m_mainBuffer->writtenSize(), fp);
+	print ("-- %1 byte%s1 written to %2\n", m_mainBuffer->writtenSize(), outfile);
+	fclose (fp);
+}
+
+// ============================================================================
+//
+// Attempt to find the variable by the given name. Looks from current scope
+// downwards.
+//
+Variable* BotscriptParser::findVariable (const String& name)
+{
+	for (int i = m_scopeCursor; i >= 0; --i)
+	{
+		for (Variable* var : m_scopeStack[i].globalVariables + m_scopeStack[i].localVariables)
+		{
+			if (var->name == name)
+				return var;
+		}
+	}
+
+	return null;
+}
+
+// ============================================================================
+//
+// Is the parser currently in global state (i.e. not in any specific state)?
+//
+bool BotscriptParser::isInGlobalState() const
+{
+	return m_currentState.isEmpty();
+}
+
+// ============================================================================
+//
+void BotscriptParser::suggestHighestVarIndex (bool global, int index)
+{
+	if (global)
+		m_highestGlobalVarIndex = max (m_highestGlobalVarIndex, index);
+	else
+		m_highestStateVarIndex = max (m_highestStateVarIndex, index);
+}
+
+// ============================================================================
+//
+int BotscriptParser::getHighestVarIndex (bool global)
+{
+	if (global)
+		return m_highestGlobalVarIndex;
+
+	return m_highestStateVarIndex;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/parser.h	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,258 @@
+/*
+	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"
+
+class DataBuffer;
+class Lexer;
+class Variable;
+
+// ============================================================================
+// Mark types
+//
+named_enum MarkType
+{
+	MARK_Label,
+	MARK_If,
+	MARK_Internal, // internal structures
+};
+
+// ============================================================================
+// Scope types
+//
+named_enum ScopeType
+{
+	SCOPE_Unknown,
+	SCOPE_If,
+	SCOPE_While,
+	SCOPE_For,
+	SCOPE_Do,
+	SCOPE_Switch,
+	SCOPE_Else,
+};
+
+named_enum AssignmentOperator
+{
+	ASSIGNOP_Assign,
+	ASSIGNOP_Add,
+	ASSIGNOP_Subtract,
+	ASSIGNOP_Multiply,
+	ASSIGNOP_Divide,
+	ASSIGNOP_Modulus,
+	ASSIGNOP_Increase,
+	ASSIGNOP_Decrease,
+};
+
+named_enum Writability
+{
+	WRITE_Mutable,		// normal read-many-write-many variable
+	WRITE_Const,		// write-once const variable
+	WRITE_Constexpr,	// const variable whose value is known to compiler
+};
+
+// =============================================================================
+//
+// Parser mode: where is the parser at?
+//
+named_enum ParserMode
+{
+	PARSERMODE_TopLevel,	// at top level
+	PARSERMODE_Event,		// inside event definition
+	PARSERMODE_MainLoop,	// inside mainloop
+	PARSERMODE_Onenter,		// inside onenter
+	PARSERMODE_Onexit,		// inside onexit
+};
+
+// ============================================================================
+//
+struct Variable
+{
+	String			name;
+	String			statename;
+	DataType		type;
+	int				index;
+	Writability		writelevel;
+	int				value;
+	String			origin;
+	bool			isarray;
+
+	inline bool IsGlobal() const
+	{
+		return statename.isEmpty();
+	}
+};
+
+// ============================================================================
+//
+struct CaseInfo
+{
+	ByteMark*		mark;
+	int				number;
+	DataBuffer*		data;
+};
+
+// ============================================================================
+//
+// Meta-data about scopes
+//
+struct ScopeInfo
+{
+	ByteMark*					mark1;
+	ByteMark*					mark2;
+	ScopeType					type;
+	DataBuffer*					buffer1;
+	int							globalVarIndexBase;
+	int							globalArrayIndexBase;
+	int							localVarIndexBase;
+
+	// switch-related stuff
+	List<CaseInfo>::Iterator	casecursor;
+	List<CaseInfo>				cases;
+	List<Variable*>				localVariables;
+	List<Variable*>				globalVariables;
+	List<Variable*>				globalArrays;
+};
+
+// ============================================================================
+//
+class BotscriptParser
+{
+	PROPERTY (public, bool, isReadOnly, setReadOnly, STOCK_WRITE)
+
+	public:
+		enum EReset
+		{
+			eNoReset,
+			SCOPE_Reset,
+		};
+
+		BotscriptParser();
+		~BotscriptParser();
+		void					parseBotscript (String fileName);
+		DataBuffer*				parseCommand (CommandInfo* comm);
+		DataBuffer*				parseAssignment (Variable* var);
+		AssignmentOperator		parseAssignmentOperator();
+		String					parseFloat();
+		void					pushScope (EReset reset = SCOPE_Reset);
+		DataBuffer*				parseStatement();
+		void					addSwitchCase (DataBuffer* b);
+		void					checkToplevel();
+		void					checkNotToplevel();
+		bool					tokenIs (ETokenType a);
+		String					getTokenString();
+		String					describePosition() const;
+		void					writeToFile (String outfile);
+		Variable*				findVariable (const String& name);
+		bool					isInGlobalState() const;
+		void					suggestHighestVarIndex (bool global, int index);
+		int						getHighestVarIndex (bool global);
+
+		inline ScopeInfo& scope (int offset)
+		{
+			return m_scopeStack[m_scopeCursor - offset];
+		}
+
+		inline int numEvents() const
+		{
+			return m_numEvents;
+		}
+
+		inline int numStates() const
+		{
+			return m_numStates;
+		}
+
+	private:
+		// The main buffer - the contents of this is what we
+		// write to file after parsing is complete
+		DataBuffer*		m_mainBuffer;
+
+		// onenter buffer - the contents of the onenter {} block
+		// is buffered here and is merged further at the end of state
+		DataBuffer*		m_onenterBuffer;
+
+		// Mainloop buffer - the contents of the mainloop {} block
+		// is buffered here and is merged further at the end of state
+		DataBuffer*		m_mainLoopBuffer;
+
+		// Switch buffer - switch case data is recorded to this
+		// buffer initially, instead of into main buffer.
+		DataBuffer*		m_switchBuffer;
+
+		Lexer*			m_lexer;
+		int				m_numStates;
+		int				m_numEvents;
+		ParserMode		m_currentMode;
+		String			m_currentState;
+		bool			m_isStateSpawnDefined;
+		bool			m_gotMainLoop;
+		int				m_scopeCursor;
+		bool			m_isElseAllowed;
+		int				m_highestGlobalVarIndex;
+		int				m_highestStateVarIndex;
+		int				m_numWrittenBytes;
+		List<ScopeInfo>	m_scopeStack;
+		int				m_zandronumVersion;
+		bool			m_defaultZandronumVersion;
+
+		DataBuffer*		currentBuffer();
+		void			parseStateBlock();
+		void			parseEventBlock();
+		void			parseMainloop();
+		void			parseOnEnterExit();
+		void			parseVar();
+		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			parseLabel();
+		void			parseEventdef();
+		void			parseFuncdef();
+		void			parseUsing();
+		void			writeMemberBuffers();
+		void			writeStringTable();
+		DataBuffer*		parseExpression (DataType reqtype, bool fromhere = false);
+		DataHeader		getAssigmentDataHeader (AssignmentOperator op, Variable* var);
+};
+
+#endif // BOTC_PARSER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/property.h	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,49 @@
+/*
+	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.
+*/
+
+#pragma once
+
+#define PROPERTY( ACCESS, TYPE, READ, WRITE, WRITETYPE )			\
+	private:														\
+		TYPE m_##READ;												\
+																	\
+	public:															\
+		inline TYPE const& READ() const								\
+		{															\
+			return m_##READ; 										\
+		}															\
+																	\
+	ACCESS:															\
+		void WRITE( TYPE const& a ) PROPERTY_##WRITETYPE( READ )	\
+
+#define PROPERTY_STOCK_WRITE( READ )	\
+		{								\
+			m_##READ = a;				\
+		}
+
+#define PROPERTY_CUSTOM_WRITE( READ ) ;
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/string.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,451 @@
+/*
+	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 m_string.compare (other.stdString());
+}
+
+// =============================================================================
+//
+void String::trim (int n)
+{
+	if (n > 0)
+		m_string = mid (0, length() - n).stdString();
+	else
+		m_string = mid (n, -1).stdString();
+}
+
+// =============================================================================
+//
+String String::strip (const List< char >& unwanted)
+{
+	String copy (m_string);
+
+	for (char c : unwanted)
+		for (int i = 0; i < copy.length(); ++i)
+			if (copy[i] == c)
+				copy.removeAt (i);
+
+	/*
+	int pos = 0;
+	while ((pos = copy.First (c)) != -1)
+		copy.RemoveAt (pos--);
+	*/
+
+	return copy;
+}
+
+// =============================================================================
+//
+String String::toUppercase() const
+{
+	String newstr = m_string;
+
+	for (char& c : newstr)
+		if (c >= 'a' && c <= 'z')
+			c -= 'a' - 'A';
+
+	return newstr;
+}
+
+// =============================================================================
+//
+String String::toLowercase() const
+{
+	String newstr = m_string;
+
+	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 (const 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)
+		m_string = m_string.replace (pos, strlen (a), b);
+}
+
+// =============================================================================
+//
+int String::count (char needle) const
+{
+	int needles = 0;
+
+	for (const char & c : m_string)
+		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, m_string.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 (m_string[i] != ' ')
+			continue;
+
+		if (++count < n)
+			continue;
+
+		return i;
+	}
+
+	return -1;
+}
+
+// =============================================================================
+//
+int String::firstIndexOf (const char* c, int 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::lastIndexOf (const char* c, int 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::toLong (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::toFloat (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::toDouble (bool* ok) const
+{
+	errno = 0;
+	char* endptr;
+	double i = strtod (m_string.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 : 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::endsWith (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::startsWith (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;
+}
+
+// =============================================================================
+//
+String StringList::join (const String& delim)
+{
+	String result;
+
+	for (const String& it : deque())
+	{
+		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.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::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 Mar 30 21:51:23 2014 +0300
@@ -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 "list.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) :
+			m_string (&a) {}
+
+		String (const char* data) :
+			m_string (data) {}
+
+		String (const StringType& data) :
+			m_string (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 (const 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 m_string[0] == '\0';
+		}
+
+		inline void append (const char* data)
+		{
+			m_string.append (data);
+		}
+
+		inline void append (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 ConstIterator begin() const
+		{
+			return m_string.cbegin();
+		}
+
+		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 ConstIterator end() const
+		{
+			return m_string.end();
+		}
+
+		inline void clear()
+		{
+			m_string.clear();
+		}
+
+		inline void removeAt (int pos)
+		{
+			m_string.erase (m_string.begin() + pos);
+		}
+
+		inline void insert (int pos, char c)
+		{
+			m_string.insert (m_string.begin() + pos, c);
+		}
+
+		inline int length() const
+		{
+			return m_string.length();
+		}
+
+		inline void remove (int pos, int len)
+		{
+			m_string.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)
+		{
+			m_string.replace (pos, n, a.chars());
+		}
+
+		inline void shrinkToFit()
+		{
+			m_string.shrink_to_fit();
+		}
+
+		inline const StringType& stdString() const
+		{
+			return m_string;
+		}
+
+		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)
+		{
+			m_string = (a + m_string).stdString();
+		}
+
+		// =============================================================================
+		//
+		inline String& operator+= (const char data)
+		{
+			append (data);
+			return *this;
+		}
+
+		// =============================================================================
+		//
+		inline String operator- (int n) const
+		{
+			String newString = m_string;
+			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 chars();
+		}
+
+		// =============================================================================
+		//
+		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 m_string;
+};
+
+// =============================================================================
+//
+class StringList : public List<String>
+{
+	public:
+		StringList() {}
+		StringList (std::initializer_list<String> vals) :
+			List<String> (vals) {}
+		StringList (const List<String>& a) : List<String> (a.deque()) {}
+		StringList (const WrappedList& 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.cpp	Sun Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,82 @@
+/*
+	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.
+*/
+
+// TODO: Another freeloader...
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "stringTable.h"
+
+static StringList g_StringTable;
+
+// ============================================================================
+//
+const StringList& getStringTable()
+{
+	return g_StringTable;
+}
+
+// ============================================================================
+//
+// 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 < g_StringTable.size(); idx++)
+	{
+		// String is already in the table, thus return it.
+		if (g_StringTable[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 (g_StringTable.size() == gMaxStringlistSize - 1)
+		error ("too many strings!\n");
+
+	// Now, dump the string into the slot
+	g_StringTable.append (a);
+	return (g_StringTable.size() - 1);
+}
+
+// ============================================================================
+//
+// Counts the amount of strings in the table.
+//
+int countStringsInTable()
+{
+	return g_StringTable.size();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/stringTable.h	Sun Mar 30 21:51:23 2014 +0300
@@ -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 Mar 30 21:51:23 2014 +0300
@@ -0,0 +1,131 @@
+/*
+	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>
+#include "macros.h"
+
+// =======================================================
+named_enum ETokenType
+{
+	// Non-word tokens
+	TK_LeftShiftAssign,
+	TK_RightShiftAssign,
+	TK_Equals,
+	TK_NotEquals,
+	TK_AddAssign,
+	TK_SubAssign,
+	TK_MultiplyAssign,
+	TK_DivideAssign,
+	TK_ModulusAssign,
+	TK_LeftShift,
+	TK_RightShift,
+	TK_AtLeast,
+	TK_AtMost,
+	TK_DoubleAmperstand,
+	TK_DoubleBar,
+	TK_DoublePlus,
+	TK_DoubleMinus,
+	TK_SingleQuote,
+	TK_DollarSign,
+	TK_ParenStart,
+	TK_ParenEnd,
+	TK_BracketStart,
+	TK_BracketEnd,
+	TK_BraceStart,
+	TK_BraceEnd,
+	TK_Assign,
+	TK_Plus,
+	TK_Minus,
+	TK_Multiply,
+	TK_Divide,
+	TK_Modulus,
+	TK_Comma,
+	TK_Lesser,
+	TK_Greater,
+	TK_Dot,
+	TK_Colon,
+	TK_Semicolon,
+	TK_Hash,
+	TK_ExclamationMark,
+	TK_Amperstand,
+	TK_Bar,
+	TK_Caret,
+	TK_QuestionMark,
+	TK_Arrow,
+
+	// --------------
+	// Named tokens
+	TK_Bool,
+	TK_Break,
+	TK_Case,
+	TK_Continue,
+	TK_Const,
+	TK_Constexpr,
+	TK_Default,
+	TK_Do,
+	TK_Else,
+	TK_Event,
+	TK_Eventdef,
+	TK_For,
+	TK_Funcdef,
+	TK_If,
+	TK_Int,
+	TK_Mainloop,
+	TK_Onenter,
+	TK_Onexit,
+	TK_State,
+	TK_Switch,
+	TK_Str,
+	TK_Using,
+	TK_Var,
+	TK_Void,
+	TK_While,
+	TK_True,
+	TK_False,
+
+	// 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,
+	TK_Func,
+	TK_Return,
+
+	// --------------
+	// Generic tokens
+	TK_Symbol,
+	TK_Number,
+	TK_String,
+	TK_Any,
+};
+
+static const int gFirstNamedToken		= TK_Bool;
+static const int gLastNamedToken		= (int) TK_Symbol - 1;
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/types.h	Sun Mar 30 21:51:23 2014 +0300
@@ -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.
+*/
+
+#ifndef BOTC_TYPES_H
+#define BOTC_TYPES_H
+
+#include <cstdlib>
+#include <stdexcept>
+#include "macros.h"
+#include "string.h"
+
+static const std::nullptr_t null = nullptr;
+
+// =============================================================================
+//
+named_enum DataType
+{
+	TYPE_Unknown,
+	TYPE_Void,
+	TYPE_Int,
+	TYPE_String,
+	TYPE_Bool,
+};
+
+// =============================================================================
+//
+struct ByteMark
+{
+	String		name;
+	int			pos;
+};
+
+// =============================================================================
+//
+struct MarkReference
+{
+	ByteMark*	target;
+	int			pos;
+};
+
+// =============================================================================
+//
+// 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

mercurial