Added switch support... fixed more problems with marks in the process

Mon, 13 Aug 2012 19:04:29 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Mon, 13 Aug 2012 19:04:29 +0300
changeset 48
976c57f153b3
parent 47
d84d82213137
child 49
8e2f7a031410

Added switch support... fixed more problems with marks in the process

databuffer.h file | annotate | diff | comparison | revisions
main.cxx file | annotate | diff | comparison | revisions
objwriter.cxx file | annotate | diff | comparison | revisions
objwriter.h file | annotate | diff | comparison | revisions
parser.cxx file | annotate | diff | comparison | revisions
preprocessor.cxx file | annotate | diff | comparison | revisions
scriptreader.h file | annotate | diff | comparison | revisions
--- a/databuffer.h	Sun Aug 12 04:45:27 2012 +0300
+++ b/databuffer.h	Mon Aug 13 19:04:29 2012 +0300
@@ -46,6 +46,8 @@
 
 #define MAX_MARKS 256
 
+extern int g_NextMark;
+
 // ============================================================================
 // DataBuffer: A dynamic data buffer.
 class DataBuffer {
@@ -136,23 +138,38 @@
 	void Merge (DataBuffer* other) {
 		if (!other)
 			return;
+		int oldsize = writesize;
 		
 		for (unsigned int x = 0; x < other->writesize; x++)
-			Write<unsigned char> (*(other->buffer+x));
+			Write<byte> (*(other->buffer+x));
 		
 		// Merge its marks and references
 		unsigned int u = 0;
 		for (u = 0; u < MAX_MARKS; u++) {
 			if (other->marks[u]) {
 				// Add the mark and offset its position.
-				unsigned int u = AddMark (other->marks[u]->name);
-				marks[u]->pos += other->writesize;
+				/* str name = other->marks[u]->name;
+				unsigned int x = AddMark (name);
+				marks[x]->pos = other->marks[u]->pos + oldsize;
+				*/
+				
+				if (marks[u])
+					error ("DataBuffer: duplicate mark %d!\n");
+				
+				marks[u] = other->marks[u];
+				marks[u]->pos += oldsize;
+				
+				// The original mark becomes NULL so that the deconstructor
+				// will not delete it prematurely. (should it delete any
+				// marks in the first place since there is no such thing
+				// as at temporary mark?)
+				other->marks[u] = NULL;
 			}
 			
 			if (other->refs[u]) {
 				// Same for references
-				unsigned int r = AddMarkReference (other->refs[u]->num);
-				refs[r]->pos += other->writesize;
+				unsigned int r = AddMarkReference (other->refs[u]->num, false);
+				refs[r]->pos = other->refs[u]->pos + oldsize;
 			}
 		}
 		
@@ -174,10 +191,10 @@
 	// to "mark" a position like that for future use.
 	unsigned int AddMark (str name) {
 		// Find a free slot for the mark
-		unsigned int u;
-		for (u = 0; u < MAX_MARKS; u++)
-			if (!marks[u])
-				break;
+		unsigned int u = g_NextMark++;
+		
+		if (marks[u])
+			error ("DataBuffer: attempted to re-create mark %u!\n", u);
 		
 		if (u >= MAX_MARKS)
 			error ("mark quota exceeded, all labels, if-structs and loops add marks\n");
@@ -193,7 +210,7 @@
 	// A ref is another "mark" that references a mark. When the bytecode
 	// is written to file, they are changed into their marks' current
 	// positions. Marks themselves are never written to files, only refs are
-	unsigned int AddMarkReference (unsigned int marknum) {
+	unsigned int AddMarkReference (unsigned int marknum, bool placeholder = true) {
 		unsigned int u;
 		for (u = 0; u < MAX_MARKS; u++)
 			if (!refs[u])
@@ -212,7 +229,8 @@
 		refs[u] = r;
 		
 		// Write a dummy placeholder for the reference
-		Write<word> (1234);
+		if (placeholder)
+			Write<word> (1234);
 		
 		return u;
 	}
--- a/main.cxx	Sun Aug 12 04:45:27 2012 +0300
+++ b/main.cxx	Mon Aug 13 19:04:29 2012 +0300
@@ -57,6 +57,8 @@
 #include "botcommands.h"
 
 const char* g_Keywords[] = {
+	"break",
+	"case",
 	"do",
 	"event",
 	"for",
@@ -66,22 +68,23 @@
 	"onenter",
 	"onexit",
 	"state",
+	"switch",
 	"var"
 	"while",
 	
 	// 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
-	"break",
-	"case",
 	"continue",
 	"default",
 	"else",
 	"enum", // Would enum actually be useful? I think so.
 	"func", // Would function support need external support from zandronum?
 	"return",
-	"switch",
 };
 
+// databuffer global variable
+int g_NextMark = 0;
+
 int main (int argc, char** argv) {
 	// Intepret command-line parameters:
 	// -l: list commands
@@ -164,18 +167,22 @@
 	ObjWriter *w = new ObjWriter (outfile);
 	
 	// We're set, begin parsing :)
-	printf ("Parsing script..\n");
+	printf ("Parsing script...\n");
 	r->BeginParse (w);
+	printf ("Script parsed successfully.\n");
 	
 	// Parse done, print statistics and write to file
 	unsigned int globalcount = CountGlobalVars ();
+	unsigned int stringcount = CountStringTable ();
 	int NumMarks = w->MainBuffer->CountMarks ();
 	int NumRefs = w->MainBuffer->CountReferences ();
-	printf ("%u / %u global variable%s\n", globalcount, MAX_SCRIPT_VARIABLES, PLURAL (globalcount));
-	printf ("%d / %d mark%s used\n", NumMarks, MAX_MARKS, PLURAL (NumMarks));
-	printf ("%d / %d ref%s used\n", NumRefs, MAX_MARKS, PLURAL (NumRefs));
-	printf ("%d state%s written\n", g_NumStates, PLURAL (g_NumStates));
-	printf ("%d event%s written\n", g_NumEvents, PLURAL (g_NumEvents));
+	printf ("%u / %u strings written\n", stringcount, MAX_LIST_STRINGS);
+	printf ("%u / %u global variables\n", globalcount, MAX_SCRIPT_VARIABLES);
+	printf ("%d / %d bytecode marks\n", NumMarks, MAX_MARKS);
+	printf ("%d / %d bytecode references\n", NumRefs, MAX_MARKS);
+	printf ("%d / %d events\n", g_NumEvents, MAX_NUM_EVENTS);
+	printf ("%d state%s\n", g_NumStates, PLURAL (g_NumStates));
+	
 	w->WriteToFile ();
 	
 	// Clear out the junk
--- a/objwriter.cxx	Sun Aug 12 04:45:27 2012 +0300
+++ b/objwriter.cxx	Mon Aug 13 19:04:29 2012 +0300
@@ -55,6 +55,7 @@
 	MainBuffer = new DataBuffer;
 	MainLoopBuffer = new DataBuffer;
 	OnEnterBuffer = new DataBuffer;
+	RecordBuffer = NULL; // created on demand
 	numWrittenBytes = 0;
 	numWrittenReferences = 0;
 	filepath = path;
@@ -109,8 +110,6 @@
 		for (unsigned int a = 0; a < stringcount; a++)
 			WriteString (g_StringTable[a]);
 	}
-	
-	printf ("%u / %u string%s written\n", stringcount, MAX_LIST_STRINGS, PLURAL (stringcount));
 }
 
 // Write main buffer to file
@@ -118,9 +117,6 @@
 	fp = fopen (filepath, "w");
 	CHECK_FILE (fp, filepath, "writing");
 	
-	if (sizeof (unsigned char) != 1)
-		error ("size of unsigned char isn't 1, but %d!\n", sizeof (unsigned char));
-	
 	// First, resolve references
 	numWrittenReferences = 0;
 	for (unsigned int u = 0; u < MAX_MARKS; u++) {
@@ -134,6 +130,10 @@
 			memset (MainBuffer->buffer + ref->pos + v, uni.b[v], 1);
 		}
 		
+		/*
+		printf ("reference %u at %d resolved to %u at %d\n",
+			u, ref->pos, ref->num, MainBuffer->marks[ref->num]->pos);
+		*/
 		numWrittenReferences++;
 	}
 	
@@ -146,7 +146,8 @@
 }
 
 DataBuffer* ObjWriter::GetCurrentBuffer() {
-	return	(g_CurMode == MODE_MAINLOOP) ? MainLoopBuffer :
+	return	RecordBuffer ? RecordBuffer :
+		(g_CurMode == MODE_MAINLOOP) ? MainLoopBuffer :
 		(g_CurMode == MODE_ONENTER) ? OnEnterBuffer :
 		MainBuffer;
 }
--- a/objwriter.h	Sun Aug 12 04:45:27 2012 +0300
+++ b/objwriter.h	Mon Aug 13 19:04:29 2012 +0300
@@ -59,6 +59,7 @@
 	DataBuffer* MainBuffer;
 	DataBuffer* OnEnterBuffer;
 	DataBuffer* MainLoopBuffer;
+	DataBuffer* RecordBuffer;
 	unsigned int numWrittenBytes;
 	unsigned int numWrittenReferences;
 	
--- a/parser.cxx	Sun Aug 12 04:45:27 2012 +0300
+++ b/parser.cxx	Mon Aug 13 19:04:29 2012 +0300
@@ -201,6 +201,8 @@
 			MustNext ();
 			
 			// Find the mark this goto statement points to
+			// TODO: This should define the mark instead of bombing
+			// out if the mark isn't found!
 			unsigned int m = w->FindMark (token);
 			if (m == MAX_MARKS)
 				ParserError ("unknown label `%s`!", token.chars());
@@ -221,7 +223,9 @@
 			// Condition
 			MustNext ("(");
 			
-			// Read the expression and write it. Store it to memory too for else statements.
+			// Read the expression and write it.
+			// TODO: This should be storing it into a variable first, so
+			// that else statements would be possible!
 			MustNext ();
 			DataBuffer* c = ParseExpression (TYPE_INT);
 			w->WriteBuffer (c);
@@ -334,6 +338,99 @@
 		}
 		
 		// ============================================================
+		// Switch
+		if (!token.icompare ("switch")) {
+			/* This goes a bit tricky. switch is structured in the
+			 * bytecode followingly:
+			 * (expression)
+			 * case a: goto casemark1
+			 * case b: goto casemark2
+			 * case c: goto casemark3
+			 * goto mark1 // jump to end if no matches
+			 * casemark1: ...
+			 * casemark2: ...
+			 * casemark3: ...
+			 * mark1: // end mark
+			 */
+			
+			MUST_NOT_TOPLEVEL
+			PushBlockStack ();
+			MustNext ("(");
+			MustNext ();
+			w->WriteBuffer (ParseExpression (TYPE_INT));
+			MustNext (")");
+			MustNext ("{");
+			blockstack[g_BlockStackCursor].type = BLOCKTYPE_SWITCH;
+			blockstack[g_BlockStackCursor].mark1 = w->AddMark (""); // end mark
+			continue;
+		}
+		
+		// ============================================================
+		if (!token.icompare ("case")) {
+			// case is only allowed inside switch
+			if (g_BlockStackCursor <= 0 || blockstack[g_BlockStackCursor].type != BLOCKTYPE_SWITCH)
+				ParserError ("`case` outside switch");
+			
+			BlockInformation* info = &blockstack[g_BlockStackCursor];
+			info->casecursor++;
+			if (info->casecursor >= MAX_CASE)
+				ParserError ("too many `case` statements in one switch");
+			
+			// Get the literal (Zandronum does not support expressions here)
+			MustNumber ();
+			int num = atoi (token.chars ());
+			
+			MustNext (":");
+			
+			// Init a mark for the case buffer
+			int m = w->AddMark ("");
+			info->casemarks[info->casecursor] = m;
+			
+			// 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.
+			info->casebuffers[info->casecursor] = w->RecordBuffer = NULL;
+			w->Write<word> (DH_CASEGOTO);
+			w->Write<word> (num);
+			w->AddReference (m);
+			
+			// Init a buffer for the case block, tell the object
+			// writer to record all written data to it.
+			info->casebuffers[info->casecursor] = w->RecordBuffer = new DataBuffer;
+			continue;
+		}
+		
+		// ============================================================
+		// Break statement.
+		if (!token.icompare ("break")) {
+			if (!g_BlockStackCursor)
+				ParserError ("unexpected `break`");
+			
+			BlockInformation* info = &blockstack[g_BlockStackCursor];
+			
+			w->Write<word> (DH_GOTO);
+			
+			// switch and if use mark1 for the closing point,
+			// for and while use mark2.
+			switch (info->type) {
+			case BLOCKTYPE_IF:
+			case BLOCKTYPE_SWITCH:
+				w->AddReference (info->mark1);
+				break;
+			case BLOCKTYPE_FOR:
+			case BLOCKTYPE_WHILE:
+				w->AddReference (info->mark2);
+				break;
+			default:
+				ParserError ("unexpected `break`");
+				break;
+			}
+			
+			MustNext (";");
+			continue;
+		}
+		
+		// ============================================================
 		if (!token.compare ("}")) {
 			// Closing brace
 			
@@ -357,7 +454,7 @@
 					// Move the closing mark here since we're at the end of the while loop
 					w->MoveMark (info->mark2);
 					break;
-				case BLOCKTYPE_DO:
+				case BLOCKTYPE_DO: { 
 					MustNext ("while");
 					MustNext ("(");
 					MustNext ();
@@ -372,6 +469,30 @@
 					break;
 				}
 				
+				case BLOCKTYPE_SWITCH: {
+					// Switch closes. Move down to the record buffer of
+					// the lower block.
+					BlockInformation* previnfo = &blockstack[g_BlockStackCursor - 1];
+					if (previnfo->casecursor != -1)
+						w->RecordBuffer = previnfo->casebuffers[previnfo->casecursor];
+					else
+						w->RecordBuffer = NULL;
+					
+					// Go through all of the buffers we
+					// recorded down and write them.
+					for (unsigned int u = 0; u < MAX_CASE; u++) {
+						if (!info->casebuffers[u])
+							continue;
+						
+						w->MoveMark (info->casemarks[u]);
+						w->WriteBuffer (info->casebuffers[u]);
+					}
+					
+					// Move the closing mark here
+					w->MoveMark (info->mark1);
+				}
+				}
+				
 				// Descend down the stack
 				g_BlockStackCursor--;
 				continue;
@@ -693,6 +814,11 @@
 	info->mark1 = 0;
 	info->mark2 = 0;
 	info->buffer1 = NULL;
+	info->casecursor = -1;
+	for (int i = 0; i < MAX_CASE; i++) {
+		info->casemarks[i] = MAX_MARKS;
+		info->casebuffers[i] = NULL;
+	}
 }
 
 DataBuffer* ScriptReader::ParseStatement (ObjWriter* w) {
--- a/preprocessor.cxx	Sun Aug 12 04:45:27 2012 +0300
+++ b/preprocessor.cxx	Mon Aug 13 19:04:29 2012 +0300
@@ -51,18 +51,17 @@
  * own bare-bones variant of the function for file reading.
  */
 char ScriptReader::PPReadChar () {
-	char* c = (char*)malloc (sizeof (char));
-	if (!fread (c, sizeof (char), 1, fp[fc]))
+	char c;
+	if (!fread (&c, sizeof (char), 1, fp[fc]))
 		return 0;
 	curchar[fc]++;
-	return c[0];
+	return c;
 }
 
 void ScriptReader::PPMustChar (char c) {
 	char d = PPReadChar ();
-	if (c != d) {
+	if (c != d)
 		ParserError ("expected `%c`, got `%d`", c, d);
-	}
 }
 
 // ============================================================================
--- a/scriptreader.h	Sun Aug 12 04:45:27 2012 +0300
+++ b/scriptreader.h	Mon Aug 13 19:04:29 2012 +0300
@@ -48,6 +48,7 @@
 
 #define MAX_FILESTACK 8
 #define MAX_STRUCTSTACK 32
+#define MAX_CASE 64
 
 class ScriptVar;
 
@@ -58,6 +59,19 @@
 	unsigned int mark2;
 	unsigned int type;
 	DataBuffer* buffer1;
+	
+	// switch-related stuff
+	// Which case are we at?
+	short casecursor;
+	
+	// Marks to case-blocks
+	int casemarks[MAX_CASE];
+	
+	// actual case blocks
+	DataBuffer* casebuffers[MAX_CASE];
+	
+	// What is the current buffer of the block?
+	DataBuffer* recordbuffer;
 };
 
 // ============================================================================
@@ -170,6 +184,7 @@
 	BLOCKTYPE_WHILE,
 	BLOCKTYPE_FOR,
 	BLOCKTYPE_DO,
+	BLOCKTYPE_SWITCH,
 };
 
 #endif // __SCRIPTREADER_H__
\ No newline at end of file

mercurial