src/parser.cc

changeset 86
43fe4be38a58
parent 85
264a61e9eba0
child 87
8f65914e7046
--- a/src/parser.cc	Sun Jan 19 20:39:30 2014 +0200
+++ b/src/parser.cc	Sun Jan 26 23:18:48 2014 +0200
@@ -26,7 +26,6 @@
 	THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-#include "object_writer.h"
 #include "parser.h"
 #include "events.h"
 #include "commands.h"
@@ -34,21 +33,9 @@
 #include "variables.h"
 #include "containers.h"
 #include "lexer.h"
-
-#define SCOPE(n) scopestack[g_ScopeCursor - n]
+#include "data_buffer.h"
 
-// TODO: make these static
-int g_NumStates = 0;
-int g_NumEvents = 0;
-parsermode_e g_current_mode = MODE_TOPLEVEL;
-string g_CurState = "";
-bool g_stateSpawnDefined = false;
-bool g_GotMainLoop = false;
-int g_ScopeCursor = 0;
-data_buffer* g_IfExpression = null;
-bool g_CanElse = false;
-static string* g_undefined_labels[MAX_MARKS]; // TODO: make a list
-list<constant_info> g_ConstInfo;
+#define SCOPE(n) (m_scope_stack[m_scope_cursor - n])
 
 // ============================================================================
 //
@@ -66,7 +53,7 @@
 //
 void botscript_parser::check_toplevel()
 {
-	if (g_current_mode != MODE_TOPLEVEL)
+	if (m_current_mode != MODE_TOPLEVEL)
 		error ("%1-statements may only be defined at top level!", token_string());
 }
 
@@ -74,7 +61,7 @@
 //
 void botscript_parser::check_not_toplevel()
 {
-	if (g_current_mode == MODE_TOPLEVEL)
+	if (m_current_mode == MODE_TOPLEVEL)
 		error ("%1-statements must not be defined at top level!", token_string());
 }
 
@@ -82,29 +69,33 @@
 // Main parser code. Begins read of the script file, checks the syntax of it
 // and writes the data to the object file via Objwriter - which also takes care
 // of necessary buffering so stuff is written in the correct order.
-void botscript_parser::parse_botscript (string file_name, object_writer* w)
+void botscript_parser::parse_botscript (string file_name)
 {
-	m_writer = w;
-
 	// Lex and preprocess the file
 	m_lx->process_file (file_name);
 
+	m_current_mode = MODE_TOPLEVEL;
+	m_num_states = 0;
+	m_num_events = 0;
+	m_scope_cursor = 0;
+	m_state_spawn_defined = false;
+	m_got_main_loop = false;
+	m_if_expression = null;
+	m_can_else = false;
+
 	// Zero the entire block stack first
 	// TODO: this shouldn't be necessary
 	for (int i = 0; i < MAX_SCOPE; i++)
-		ZERO (scopestack[i]);
-
-	for (int i = 0; i < MAX_MARKS; i++)
-		g_undefined_labels[i] = null;
+		ZERO (m_scope_stack[i]);
 
 	while (m_lx->get_next())
 	{
 		// Check if else is potentically valid
-		if (token_is (tk_else) && !g_CanElse)
+		if (token_is (tk_else) && !m_can_else)
 			error ("else without preceding if");
 
 		if (!token_is (tk_else))
-			g_CanElse = false;
+			m_can_else = false;
 
 		switch (m_lx->get_token()->type)
 		{
@@ -209,7 +200,7 @@
 
 				if (comm)
 				{
-					m_writer->get_current_buffer()->merge (ParseCommand (comm));
+					buffer()->merge_and_destroy (parse_command (comm));
 					m_lx->must_get_next (tk_semicolon);
 					continue;
 				}
@@ -220,7 +211,7 @@
 				if (!b)
 					error ("unknown token `%1`", token_string());
 
-				m_writer->write_buffer (b);
+				buffer()->merge_and_destroy (b);
 				m_lx->must_get_next (tk_semicolon);
 			}
 			break;
@@ -229,22 +220,29 @@
 
 	// ===============================================================================
 	// Script file ended. Do some last checks and write the last things to main buffer
-	if (g_current_mode != MODE_TOPLEVEL)
+	if (m_current_mode != MODE_TOPLEVEL)
 		error ("script did not end at top level; a `}` is missing somewhere");
 
 	// stateSpawn must be defined!
-	if (!g_stateSpawnDefined)
+	if (!m_state_spawn_defined)
 		error ("script must have a state named `stateSpawn`!");
 
-	for (int i = 0; i < MAX_MARKS; i++)
-		if (g_undefined_labels[i])
-			error ("label `%1` is referenced via `goto` but isn't defined\n", g_undefined_labels[i]);
+	// Ensure no goto target is left undefined
+	if (m_undefined_labels.is_empty() == false)
+	{
+		string_list names;
+
+		for (undefined_label& undf : m_undefined_labels)
+			names << undf.name;
+
+		error ("labels `%1` are referenced via `goto` but are not defined\n", names);
+	}
 
 	// Dump the last state's onenter and mainloop
-	m_writer->write_member_buffers();
+	write_member_buffers();
 
 	// String table
-	m_writer->write_string_table();
+	write_string_table();
 }
 
 // ============================================================================
@@ -262,24 +260,24 @@
 	// stateSpawn is special - it *must* be defined. If we
 	// encountered it, then mark down that we have it.
 	if (-statename == "statespawn")
-		g_stateSpawnDefined = true;
+		m_state_spawn_defined = true;
 
 	// Must end in a colon
 	m_lx->must_get_next (tk_colon);
 
 	// write the previous state's onenter and
 	// mainloop buffers to file now
-	if (g_CurState.is_empty() == false)
-		m_writer->write_member_buffers();
+	if (m_current_state.is_empty() == false)
+		write_member_buffers();
 
-	m_writer->write (dh_state_name);
-	m_writer->write_string (statename);
-	m_writer->write (dh_state_index);
-	m_writer->write (g_NumStates);
+	buffer()->write_dword (dh_state_name);
+	buffer()->write_string (statename);
+	buffer()->write_dword (dh_state_index);
+	buffer()->write_dword (m_num_states);
 
-	g_NumStates++;
-	g_CurState = statename;
-	g_GotMainLoop = false;
+	m_num_states++;
+	m_current_state = statename;
+	m_got_main_loop = false;
 }
 
 // ============================================================================
@@ -295,10 +293,10 @@
 		error ("bad event, got `%1`\n", token_string());
 
 	m_lx->must_get_next (tk_brace_start);
-	g_current_mode = MODE_EVENT;
-	m_writer->write (dh_event);
-	m_writer->write (e->number);
-	g_NumEvents++;
+	m_current_mode = MODE_EVENT;
+	buffer()->write_dword (dh_event);
+	buffer()->write_dword (e->number);
+	m_num_events++;
 }
 
 // ============================================================================
@@ -308,9 +306,8 @@
 	check_toplevel();
 	m_lx->must_get_next (tk_brace_start);
 
-	// Mode must be set before dataheader is written here!
-	g_current_mode = MODE_MAINLOOP;
-	m_writer->write (dh_main_loop);
+	m_current_mode = MODE_MAINLOOP;
+	m_main_loop_buffer->write_dword (dh_main_loop);
 }
 
 // ============================================================================
@@ -321,10 +318,8 @@
 	bool onenter = (token_is (tk_onenter));
 	m_lx->must_get_next (tk_brace_start);
 
-	// Mode must be set before dataheader is written here,
-	// because onenter goes to a separate buffer.
-	g_current_mode = onenter ? MODE_ONENTER : MODE_ONEXIT;
-	m_writer->write (onenter ? dh_on_enter : dh_on_exit);
+	m_current_mode = onenter ? MODE_ONENTER : MODE_ONEXIT;
+	buffer()->write_dword (onenter ? dh_on_enter : dh_on_exit);
 }
 
 // ============================================================================
@@ -332,7 +327,7 @@
 void botscript_parser::parse_variable_declaration()
 {
 	// For now, only globals are supported
-	if (g_current_mode != MODE_TOPLEVEL || g_CurState.is_empty() == false)
+	if (m_current_mode != MODE_TOPLEVEL || m_current_state.is_empty() == false)
 		error ("variables must only be global for now");
 
 	type_e type =	(token_is (tk_int)) ? TYPE_INT :
@@ -362,18 +357,20 @@
 
 	// Find the mark this goto statement points to
 	string target = token_string();
-	int m = m_writer->find_byte_mark (target);
+	byte_mark* mark = buffer()->find_mark_by_name (target);
 
 	// If not set, define it
-	if (m == MAX_MARKS)
+	if (!mark)
 	{
-		m = m_writer->add_mark (target);
-		g_undefined_labels[m] = new string (target);
+		undefined_label undf;
+		undf.name = target;
+		undf.target = buffer()->add_mark (target);
+		m_undefined_labels << undf;
 	}
 
 	// Add a reference to the mark.
-	m_writer->write (dh_goto);
-	m_writer->add_reference (m);
+	buffer()->write_dword (dh_goto);
+	buffer()->add_reference (mark);
 	m_lx->must_get_next (tk_semicolon);
 }
 
@@ -390,22 +387,22 @@
 	// Read the expression and write it.
 	m_lx->must_get_next();
 	data_buffer* c = parse_expression (TYPE_INT);
-	m_writer->write_buffer (c);
+	buffer()->merge_and_destroy (c);
 
 	m_lx->must_get_next (tk_paren_end);
 	m_lx->must_get_next (tk_brace_start);
 
 	// Add a mark - to here temporarily - and add a reference to it.
 	// Upon a closing brace, the mark will be adjusted.
-	int marknum = m_writer->add_mark ("");
+	byte_mark* mark = buffer()->add_mark ("");
 
 	// Use dh_if_not_goto - if the expression is not true, we goto the mark
 	// we just defined - and this mark will be at the end of the scope block.
-	m_writer->write (dh_if_not_goto);
-	m_writer->add_reference (marknum);
+	buffer()->write_dword (dh_if_not_goto);
+	buffer()->add_reference (mark);
 
 	// Store it
-	SCOPE (0).mark1 = marknum;
+	SCOPE (0).mark1 = mark;
 	SCOPE (0).type = e_if_scope;
 }
 
@@ -417,9 +414,9 @@
 	m_lx->must_get_next (tk_brace_start);
 
 	// Don't use PushScope as it resets the scope
-	g_ScopeCursor++;
+	m_scope_cursor++;
 
-	if (g_ScopeCursor >= MAX_SCOPE)
+	if (m_scope_cursor >= MAX_SCOPE)
 		error ("too deep scope");
 
 	if (SCOPE (0).type != e_if_scope)
@@ -427,14 +424,14 @@
 
 	// write down to jump to the end of the else statement
 	// Otherwise we have fall-throughs
-	SCOPE (0).mark2 = m_writer->add_mark ("");
+	SCOPE (0).mark2 = buffer()->add_mark ("");
 
 	// Instruction to jump to the end after if block is complete
-	m_writer->write (dh_goto);
-	m_writer->add_reference (SCOPE (0).mark2);
+	buffer()->write_dword (dh_goto);
+	buffer()->add_reference (SCOPE (0).mark2);
 
 	// Move the ifnot mark here and set type to else
-	m_writer->move_mark (SCOPE (0).mark1);
+	buffer()->adjust_mark (SCOPE (0).mark1);
 	SCOPE (0).type = e_else_scope;
 }
 
@@ -449,8 +446,8 @@
 	// end. The condition is checked at the very start of the loop, if it fails,
 	// we use goto to skip to the end of the loop. At the end, we loop back to
 	// the beginning with a go-to statement.
-	int mark1 = m_writer->add_mark (""); // start
-	int mark2 = m_writer->add_mark (""); // end
+	byte_mark* mark1 = buffer()->add_mark (""); // start
+	byte_mark* mark2 = buffer()->add_mark (""); // end
 
 	// Condition
 	m_lx->must_get_next (tk_paren_start);
@@ -460,11 +457,11 @@
 	m_lx->must_get_next (tk_brace_start);
 
 	// write condition
-	m_writer->write_buffer (expr);
+	buffer()->merge_and_destroy (expr);
 
 	// Instruction to go to the end if it fails
-	m_writer->write (dh_if_not_goto);
-	m_writer->add_reference (mark2);
+	buffer()->write_dword (dh_if_not_goto);
+	buffer()->add_reference (mark2);
 
 	// Store the needed stuff
 	SCOPE (0).mark1 = mark1;
@@ -509,16 +506,16 @@
 	m_lx->must_get_next (tk_brace_start);
 
 	// First, write out the initializer
-	m_writer->write_buffer (init);
+	buffer()->merge_and_destroy (init);
 
 	// Init two marks
-	int mark1 = m_writer->add_mark ("");
-	int mark2 = m_writer->add_mark ("");
+	byte_mark* mark1 = buffer()->add_mark ("");
+	byte_mark* mark2 = buffer()->add_mark ("");
 
 	// Add the condition
-	m_writer->write_buffer (cond);
-	m_writer->write (dh_if_not_goto);
-	m_writer->add_reference (mark2);
+	buffer()->merge_and_destroy (cond);
+	buffer()->write_dword (dh_if_not_goto);
+	buffer()->add_reference (mark2);
 
 	// Store the marks and incrementor
 	SCOPE (0).mark1 = mark1;
@@ -534,7 +531,7 @@
 	check_not_toplevel();
 	push_scope();
 	m_lx->must_get_next (tk_brace_start);
-	SCOPE (0).mark1 = m_writer->add_mark ("");
+	SCOPE (0).mark1 = buffer()->add_mark ("");
 	SCOPE (0).type = e_do_scope;
 }
 
@@ -559,11 +556,11 @@
 	push_scope();
 	m_lx->must_get_next (tk_paren_start);
 	m_lx->must_get_next();
-	m_writer->write_buffer (parse_expression (TYPE_INT));
+	buffer()->merge_and_destroy (parse_expression (TYPE_INT));
 	m_lx->must_get_next (tk_paren_end);
 	m_lx->must_get_next (tk_brace_start);
 	SCOPE (0).type = e_switch_scope;
-	SCOPE (0).mark1 = m_writer->add_mark (""); // end mark
+	SCOPE (0).mark1 = buffer()->add_mark (""); // end mark
 	SCOPE (0).buffer1 = null; // default header
 }
 
@@ -584,17 +581,19 @@
 		if (SCOPE (0).casenumbers[i] == num)
 			error ("multiple case %d labels in one switch", num);
 
-	// write down the expression and case-go-to. This builds
+	// 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
+	//
+	// AddSwitchCase will add the reference to the mark
 	// for the case block that this heralds, and takes care
 	// of buffering setup and stuff like that.
-	//	 null the switch buffer for the case-go-to statement,
+	//
+	// We null the switch buffer for the case-go-to statement as
 	// we want it all under the switch, not into the case-buffers.
-	m_writer->SwitchBuffer = null;
-	m_writer->write (dh_case_goto);
-	m_writer->write (num);
+	m_switch_buffer = null;
+	buffer()->write_dword (dh_case_goto);
+	buffer()->write_dword (num);
 	add_switch_case (null);
 	SCOPE (0).casenumbers[SCOPE (0).casecursor] = num;
 }
@@ -620,8 +619,8 @@
 	// a default.
 	data_buffer* b = new data_buffer;
 	SCOPE (0).buffer1 = b;
-	b->write (dh_drop);
-	b->write (dh_goto);
+	b->write_dword (dh_drop);
+	b->write_dword (dh_goto);
 	add_switch_case (b);
 }
 
@@ -629,10 +628,10 @@
 //
 void botscript_parser::parse_break()
 {
-	if (!g_ScopeCursor)
+	if (!m_scope_cursor)
 		error ("unexpected `break`");
 
-	m_writer->write (dh_goto);
+	buffer()->write_dword (dh_goto);
 
 	// switch and if use mark1 for the closing point,
 	// for and while use mark2.
@@ -641,13 +640,13 @@
 		case e_if_scope:
 		case e_switch_scope:
 		{
-			m_writer->add_reference (SCOPE (0).mark1);
+			buffer()->add_reference (SCOPE (0).mark1);
 		} break;
 
 		case e_for_scope:
 		case e_while_scope:
 		{
-			m_writer->add_reference (SCOPE (0).mark2);
+			buffer()->add_reference (SCOPE (0).mark2);
 		} break;
 
 		default:
@@ -669,16 +668,16 @@
 	bool found = false;
 
 	// Fall through the scope until we find a loop block
-	for (curs = g_ScopeCursor; curs > 0 && !found; curs--)
+	for (curs = m_scope_cursor; curs > 0 && !found; curs--)
 	{
-		switch (scopestack[curs].type)
+		switch (m_scope_stack[curs].type)
 		{
 			case e_for_scope:
 			case e_while_scope:
 			case e_do_scope:
 			{
-				m_writer->write (dh_goto);
-				m_writer->add_reference (scopestack[curs].mark1);
+				buffer()->write_dword (dh_goto);
+				buffer()->add_reference (m_scope_stack[curs].mark1);
 				found = true;
 			} break;
 
@@ -698,36 +697,36 @@
 {
 	// Closing brace
 	// If we're in the block stack, we're descending down from it now
-	if (g_ScopeCursor > 0)
+	if (m_scope_cursor > 0)
 	{
 		switch (SCOPE (0).type)
 		{
 			case e_if_scope:
 				// Adjust the closing mark.
-				m_writer->move_mark (SCOPE (0).mark1);
+				buffer()->adjust_mark (SCOPE (0).mark1);
 
 				// We're returning from if, thus else can be next
-				g_CanElse = true;
+				m_can_else = true;
 				break;
 
 			case e_else_scope:
 				// else instead uses mark1 for itself (so if expression
 				// fails, jump to else), mark2 means end of else
-				m_writer->move_mark (SCOPE (0).mark2);
+				buffer()->adjust_mark (SCOPE (0).mark2);
 				break;
 
 			case e_for_scope:
 				// write the incrementor at the end of the loop block
-				m_writer->write_buffer (SCOPE (0).buffer1);
+				buffer()->merge_and_destroy (SCOPE (0).buffer1);
 
 				// fall-thru
 			case e_while_scope:
 				// write down the instruction to go back to the start of the loop
-				m_writer->write (dh_goto);
-				m_writer->add_reference (SCOPE (0).mark1);
+				buffer()->write_dword (dh_goto);
+				buffer()->add_reference (SCOPE (0).mark1);
 
 				// Move the closing mark here since we're at the end of the while loop
-				m_writer->move_mark (SCOPE (0).mark2);
+				buffer()->adjust_mark (SCOPE (0).mark2);
 				break;
 
 			case e_do_scope:
@@ -740,9 +739,9 @@
 				m_lx->must_get_next (tk_semicolon);
 
 				// If the condition runs true, go back to the start.
-				m_writer->write_buffer (expr);
-				m_writer->write (dh_if_goto);
-				m_writer->add_reference (SCOPE (0).mark1);
+				buffer()->merge_and_destroy (expr);
+				buffer()->write_dword (dh_if_goto);
+				buffer()->add_reference (SCOPE (0).mark1);
 				break;
 			}
 
@@ -751,21 +750,21 @@
 				// Switch closes. Move down to the record buffer of
 				// the lower block.
 				if (SCOPE (1).casecursor != -1)
-					m_writer->SwitchBuffer = SCOPE (1).casebuffers[SCOPE (1).casecursor];
+					m_switch_buffer = SCOPE (1).casebuffers[SCOPE (1).casecursor];
 				else
-					m_writer->SwitchBuffer = null;
+					m_switch_buffer = null;
 
 				// If there was a default in the switch, write its header down now.
 				// If not, write instruction to jump to the end of switch after
 				// the headers (thus won't fall-through if no case matched)
 				if (SCOPE (0).buffer1)
-					m_writer->write_buffer (SCOPE (0).buffer1);
-							else
-					{
-						m_writer->write (dh_drop);
-						m_writer->write (dh_goto);
-						m_writer->add_reference (SCOPE (0).mark1);
-					}
+					buffer()->merge_and_destroy (SCOPE (0).buffer1);
+				else
+				{
+					buffer()->write_dword (dh_drop);
+					buffer()->write_dword (dh_goto);
+					buffer()->add_reference (SCOPE (0).mark1);
+				}
 
 				// Go through all of the buffers we
 				// recorded down and write them.
@@ -774,12 +773,12 @@
 					if (!SCOPE (0).casebuffers[u])
 						continue;
 
-					m_writer->move_mark (SCOPE (0).casemarks[u]);
-					m_writer->write_buffer (SCOPE (0).casebuffers[u]);
+					buffer()->adjust_mark (SCOPE (0).casemarks[u]);
+					buffer()->merge_and_destroy (SCOPE (0).casebuffers[u]);
 				}
 
 				// Move the closing mark here
-				m_writer->move_mark (SCOPE (0).mark1);
+				buffer()->adjust_mark (SCOPE (0).mark1);
 				break;
 			}
 
@@ -788,14 +787,14 @@
 		}
 
 		// Descend down the stack
-		g_ScopeCursor--;
+		m_scope_cursor--;
 		return;
 	}
 
-	int dataheader =	(g_current_mode == MODE_EVENT) ? dh_end_event :
-						(g_current_mode == MODE_MAINLOOP) ? dh_end_main_loop :
-						(g_current_mode == MODE_ONENTER) ? dh_end_on_enter :
-						(g_current_mode == MODE_ONEXIT) ? dh_end_on_exit : -1;
+	int dataheader =	(m_current_mode == MODE_EVENT) ? dh_end_event :
+						(m_current_mode == MODE_MAINLOOP) ? dh_end_main_loop :
+						(m_current_mode == MODE_ONENTER) ? dh_end_on_enter :
+						(m_current_mode == MODE_ONEXIT) ? dh_end_on_exit : -1;
 
 	if (dataheader == -1)
 		error ("unexpected `}`");
@@ -803,8 +802,8 @@
 	// 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.
-	m_writer->write (dataheader);
-	g_current_mode = MODE_TOPLEVEL;
+	buffer()->write_dword (dataheader);
+	m_current_mode = MODE_TOPLEVEL;
 	m_lx->get_next (tk_semicolon);
 }
 
@@ -846,7 +845,7 @@
 	}
 
 	info.val = m_lx->get_token()->text;
-	g_ConstInfo << info;
+	m_constants << info;
 
 	m_lx->must_get_next (tk_semicolon);
 }
@@ -857,6 +856,7 @@
 {
 	check_not_toplevel();
 	string label_name = token_string();
+	byte_mark* mark = null;
 
 	// want no conflicts..
 	if (find_command_by_name (label_name))
@@ -866,24 +866,22 @@
 		error ("label name `%1` conflicts with variable\n", label_name);
 
 	// See if a mark already exists for this label
-	int mark = -1;
-
-	for (int i = 0; i < MAX_MARKS; i++)
+	for (undefined_label& undf : m_undefined_labels)
 	{
-		if (g_undefined_labels[i] && *g_undefined_labels[i] == label_name)
-		{
-			mark = i;
-			m_writer->move_mark (i);
+		if (undf.name != label_name)
+			continue;
 
-			// No longer undefinde
-			delete g_undefined_labels[i];
-			g_undefined_labels[i] = null;
-		}
+		mark = undf.target;
+		buffer()->adjust_mark (mark);
+
+		// No longer undefined
+		m_undefined_labels.remove (undf);
+		break;
 	}
 
 	// Not found in unmarked lists, define it now
-	if (mark == -1)
-		m_writer->add_mark (label_name);
+	if (mark == null)
+		buffer()->add_mark (label_name);
 
 	m_lx->must_get_next (tk_colon);
 }
@@ -921,9 +919,6 @@
 	m_lx->must_get_next (tk_symbol);
 	comm->name = m_lx->get_token()->text;
 
-	if (IsKeyword (comm->name))
-		error ("function name `%1` conflicts with keyword", comm->name);
-
 	m_lx->must_get_next (tk_colon);
 
 	// Return value
@@ -994,11 +989,11 @@
 
 // ============================================================================
 // Parses a command call
-data_buffer* botscript_parser::ParseCommand (command_info* comm)
+data_buffer* botscript_parser::parse_command (command_info* comm)
 {
 	data_buffer* r = new data_buffer (64);
 
-	if (g_current_mode == MODE_TOPLEVEL)
+	if (m_current_mode == MODE_TOPLEVEL)
 		error ("command call at top level");
 
 	m_lx->must_get_next (tk_paren_start);
@@ -1022,7 +1017,7 @@
 			error ("too many arguments passed to %1\n\tprototype: %2",
 				comm->name, get_command_signature (comm));
 
-		r->merge (parse_expression (comm->args[curarg].type));
+		r->merge_and_destroy (parse_expression (comm->args[curarg].type));
 		m_lx->must_get_next();
 
 		if (curarg < comm->numargs - 1)
@@ -1051,14 +1046,14 @@
 	// If the script skipped any optional arguments, fill in defaults.
 	while (curarg < comm->maxargs)
 	{
-		r->write (dh_push_number);
-		r->write (comm->args[curarg].defvalue);
+		r->write_dword (dh_push_number);
+		r->write_dword (comm->args[curarg].defvalue);
 		curarg++;
 	}
 
-	r->write (dh_command);
-	r->write (comm->number);
-	r->write (comm->maxargs);
+	r->write_dword (dh_command);
+	r->write_dword (comm->number);
+	r->write_dword (comm->maxargs);
 
 	return r;
 }
@@ -1145,7 +1140,7 @@
 	data_buffer* retbuf = new data_buffer (64);
 
 	// Parse first operand
-	retbuf->merge (parse_expr_value (reqtype));
+	retbuf->merge_and_destroy (parse_expr_value (reqtype));
 
 	// Parse any and all operators we get
 	int oper;
@@ -1173,22 +1168,22 @@
 			// It also is handled differently: there isn't a dataheader for ternary
 			// operator. Instead, we abuse PUSHNUMBER and IFNOTGOTO for this.
 			// Behold, big block of writing madness! :P
-			int mark1 = retbuf->add_mark (""); // start of "else" case
-			int mark2 = retbuf->add_mark (""); // end of expression
-			retbuf->write (dh_if_not_goto); // if the first operand (condition)
+			byte_mark* mark1 = retbuf->add_mark (""); // start of "else" case
+			byte_mark* mark2 = retbuf->add_mark (""); // end of expression
+			retbuf->write_dword (dh_if_not_goto); // if the first operand (condition)
 			retbuf->add_reference (mark1); // didn't eval true, jump into mark1
-			retbuf->merge (rb); // otherwise, perform second operand (true case)
-			retbuf->write (dh_goto); // afterwards, jump to the end, which is
+			retbuf->merge_and_destroy (rb); // otherwise, perform second operand (true case)
+			retbuf->write_dword (dh_goto); // afterwards, jump to the end, which is
 			retbuf->add_reference (mark2); // marked by mark2.
-			retbuf->move_mark (mark1); // move mark1 at the end of the true case
-			retbuf->merge (tb); // perform third operand (false case)
-			retbuf->move_mark (mark2); // move the ending mark2 here
+			retbuf->adjust_mark (mark1); // move mark1 at the end of the true case
+			retbuf->merge_and_destroy (tb); // perform third operand (false case)
+			retbuf->adjust_mark (mark2); // move the ending mark2 here
 		}
 		else
 		{
 			// write to buffer
-			retbuf->merge (rb);
-			retbuf->write (get_data_header_by_operator (null, oper));
+			retbuf->merge_and_destroy (rb);
+			retbuf->write_dword (get_data_header_by_operator (null, oper));
 		}
 	}
 
@@ -1325,8 +1320,8 @@
 		if (reqtype != TYPE_INT)
 			error ("strlen returns int but %1 is expected\n", GetTypeName (reqtype));
 
-		b->write (dh_push_number);
-		b->write (constant->val.len());
+		b->write_dword (dh_push_number);
+		b->write_dword (constant->val.len());
 
 		m_lx->must_get_next (tk_paren_end);
 	}
@@ -1336,7 +1331,7 @@
 		// Expression
 		m_lx->must_get_next();
 		data_buffer* c = parse_expression (reqtype);
-		b->merge (c);
+		b->merge_and_destroy (c);
 		m_lx->must_get_next (tk_paren_end);
 	}
 	else if (command_info* comm = find_command_by_name (token_string()))
@@ -1347,7 +1342,7 @@
 		if (reqtype && comm->returnvalue != reqtype)
 			error ("%1 returns an incompatible data type", comm->name);
 
-		b = ParseCommand (comm);
+		b = parse_command (comm);
 	}
 	else if (constant_info* constant = find_constant (token_string()))
 	{
@@ -1361,12 +1356,12 @@
 		{
 			case TYPE_BOOL:
 			case TYPE_INT:
-				b->write (dh_push_number);
-				b->write (atoi (constant->val));
+				b->write_dword (dh_push_number);
+				b->write_dword (atoi (constant->val));
 				break;
 
 			case TYPE_STRING:
-				b->write_string (constant->val);
+				b->write_string_index (constant->val);
 				break;
 
 			case TYPE_VOID:
@@ -1377,8 +1372,8 @@
 	else if ((g = find_global_variable (token_string())))
 	{
 		// Global variable
-		b->write (dh_push_global_var);
-		b->write (g->index);
+		b->write_dword (dh_push_global_var);
+		b->write_dword (g->index);
 	}
 	else
 	{
@@ -1397,13 +1392,13 @@
 
 			// All values are written unsigned - thus we need to write the value's
 			// absolute value, followed by an unary minus for negatives.
-			b->write (dh_push_number);
+			b->write_dword (dh_push_number);
 
 			long v = token_string().to_long();
-			b->write (static_cast<word> (abs (v)));
+			b->write_dword (static_cast<word> (abs (v)));
 
 			if (v < 0)
-				b->write (dh_unary_minus);
+				b->write_dword (dh_unary_minus);
 
 			break;
 		}
@@ -1413,14 +1408,14 @@
 			// string if it finds it in the table, or writes it to the
 			// table and returns it index if it doesn't find it there.
 			m_lx->must_be (tk_string);
-			b->write_string (token_string());
+			b->write_string_index (token_string());
 			break;
 		}
 	}
 
 	// Negate it now if desired
 	if (negate)
-		b->write (dh_negate_logical);
+		b->write_dword (dh_negate_logical);
 
 	return b;
 }
@@ -1432,8 +1427,6 @@
 //
 data_buffer* botscript_parser::parse_assignment (script_variable* var)
 {
-	bool global = !var->statename.len();
-
 	// Get an operator
 	m_lx->must_get_next();
 	int oper = parse_operator();
@@ -1441,7 +1434,7 @@
 	if (!is_assignment_operator (oper))
 		error ("expected assignment operator");
 
-	if (g_current_mode == MODE_TOPLEVEL)
+	if (m_current_mode == MODE_TOPLEVEL)
 		error ("can't alter variables at top level");
 
 	// Parse the right operand
@@ -1454,19 +1447,19 @@
 	// a >>= b -> a = a >> b
 	if (oper == OPER_ASSIGNLEFTSHIFT || oper == OPER_ASSIGNRIGHTSHIFT)
 	{
-		retbuf->write (global ? dh_push_global_var : dh_push_local_var);
-		retbuf->write (var->index);
-		retbuf->merge (expr);
-		retbuf->write ((oper == OPER_ASSIGNLEFTSHIFT) ? dh_left_shift : dh_right_shift);
-		retbuf->write (global ? dh_assign_global_var : dh_assign_local_var);
-		retbuf->write (var->index);
+		retbuf->write_dword (var->is_global() ? dh_push_global_var : dh_push_local_var);
+		retbuf->write_dword (var->index);
+		retbuf->merge_and_destroy (expr);
+		retbuf->write_dword ((oper == OPER_ASSIGNLEFTSHIFT) ? dh_left_shift : dh_right_shift);
+		retbuf->write_dword (var->is_global() ? dh_assign_global_var : dh_assign_local_var);
+		retbuf->write_dword (var->index);
 	}
 	else
 	{
-		retbuf->merge (expr);
+		retbuf->merge_and_destroy (expr);
 		long dh = get_data_header_by_operator (var, oper);
-		retbuf->write (dh);
-		retbuf->write (var->index);
+		retbuf->write_dword (dh);
+		retbuf->write_dword (var->index);
 	}
 
 	return retbuf;
@@ -1476,21 +1469,21 @@
 //
 void botscript_parser::push_scope()
 {
-	g_ScopeCursor++;
+	m_scope_cursor++;
 
-	if (g_ScopeCursor >= MAX_SCOPE)
+	if (m_scope_cursor >= MAX_SCOPE)
 		error ("too deep scope");
 
 	ScopeInfo* info = &SCOPE (0);
 	info->type = e_unknown_scope;
-	info->mark1 = 0;
-	info->mark2 = 0;
+	info->mark1 = null;
+	info->mark2 = null;
 	info->buffer1 = null;
 	info->casecursor = -1;
 
 	for (int i = 0; i < MAX_CASE; i++)
 	{
-		info->casemarks[i] = MAX_MARKS;
+		info->casemarks[i] = null;
 		info->casebuffers[i] = null;
 		info->casenumbers[i] = -1;
 	}
@@ -1522,28 +1515,28 @@
 		error ("too many cases in one switch");
 
 	// Init a mark for the case buffer
-	int m = m_writer->add_mark ("");
-	info->casemarks[info->casecursor] = m;
+	byte_mark* casemark = buffer()->add_mark ("");
+	info->casemarks[info->casecursor] = casemark;
 
 	// Add a reference to the mark. "case" and "default" both
 	// add the necessary bytecode before the reference.
 	if (b)
-		b->add_reference (m);
+		b->add_reference (casemark);
 	else
-		m_writer->add_reference (m);
+		buffer()->add_reference (casemark);
 
 	// Init a buffer for the case block and tell the object
 	// writer to record all written data to it.
-	info->casebuffers[info->casecursor] = m_writer->SwitchBuffer = new data_buffer;
+	info->casebuffers[info->casecursor] = m_switch_buffer = new data_buffer;
 }
 
 // ============================================================================
 //
-constant_info* find_constant (const string& tok)
+constant_info* botscript_parser::find_constant (const string& tok)
 {
-	for (int i = 0; i < g_ConstInfo.size(); i++)
-		if (g_ConstInfo[i].name == tok)
-			return &g_ConstInfo[i];
+	for (int i = 0; i < m_constants.size(); i++)
+		if (m_constants[i].name == tok)
+			return &m_constants[i];
 
 	return null;
 }
@@ -1569,3 +1562,99 @@
 	lexer::token* tok = m_lx->get_token();
 	return tok->file + ":" + string (tok->line) + ":" + string (tok->column);
 }
+
+// ============================================================================
+//
+data_buffer* botscript_parser::buffer()
+{
+	if (m_switch_buffer != null)
+		return m_switch_buffer;
+
+	if (m_current_mode == MODE_MAINLOOP)
+		return m_main_loop_buffer;
+
+	if (m_current_mode == MODE_ONENTER)
+		return m_on_enter_buffer;
+
+	return m_main_buffer;
+}
+
+// ============================================================================
+//
+void botscript_parser::write_member_buffers()
+{
+	// If there was no mainloop defined, write a dummy one now.
+	if (!m_got_main_loop)
+	{
+		m_main_loop_buffer->write_dword (dh_main_loop);
+		m_main_loop_buffer->write_dword (dh_end_main_loop);
+	}
+
+	// Write the onenter and mainloop buffers, in that order in particular.
+	for (data_buffer** bufp : list<data_buffer**> ({&m_on_enter_buffer, &m_main_loop_buffer}))
+	{
+		buffer()->merge_and_destroy (*bufp);
+
+		// Clear the buffer afterwards for potential next state
+		*bufp = new data_buffer;
+	}
+
+	// Next state definitely has no mainloop yet
+	m_got_main_loop = false;
+}
+
+// ============================================================================
+//
+// Write string table
+//
+void botscript_parser::write_string_table()
+{
+	int stringcount = num_strings_in_table();
+
+	if (!stringcount)
+		return;
+
+	// Write header
+	m_main_buffer->write_dword (dh_string_list);
+	m_main_buffer->write_dword (stringcount);
+
+	// Write all strings
+	for (int i = 0; i < stringcount; i++)
+		m_main_buffer->write_string (get_string_table()[i]);
+}
+
+// ============================================================================
+//
+// Write the compiled bytecode to a file
+//
+void botscript_parser::write_to_file (string outfile)
+{
+	FILE* fp = fopen (outfile, "w");
+	CHECK_FILE (fp, outfile, "writing");
+
+	// First, resolve references
+	for (int u = 0; u < MAX_MARKS; u++)
+	{
+		mark_reference* ref = m_main_buffer->get_refs()[u];
+
+		if (!ref)
+			continue;
+
+		// Substitute the placeholder with the mark position
+		generic_union<word> uni;
+		uni.as_word = static_cast<word> (ref->target->pos);
+
+		for (int v = 0; v < (int) sizeof (word); v++)
+			memset (m_main_buffer->get_buffer() + ref->pos + v, uni.as_bytes[v], 1);
+
+		/*
+		printf ("reference %u at %d resolved to %u at %d\n",
+			u, ref->pos, ref->num, MainBuffer->marks[ref->num]->pos);
+		*/
+	}
+
+	// Then, dump the main buffer to the file
+	fwrite (m_main_buffer->get_buffer(), 1, m_main_buffer->get_write_size(), fp);
+	print ("-- %1 byte%s1 written to %2\n", m_main_buffer->get_write_size(), outfile);
+	fclose (fp);
+}
\ No newline at end of file

mercurial