Added default label for switch

Mon, 13 Aug 2012 23:10:39 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Mon, 13 Aug 2012 23:10:39 +0300
changeset 50
2e333a3ca49a
parent 49
8e2f7a031410
child 51
2cfa6edbf928

Added default label for switch

common.h file | annotate | diff | comparison | revisions
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
scriptreader.cxx file | annotate | diff | comparison | revisions
scriptreader.h file | annotate | diff | comparison | revisions
stringtable.h file | annotate | diff | comparison | revisions
--- a/common.h	Mon Aug 13 19:12:21 2012 +0300
+++ b/common.h	Mon Aug 13 23:10:39 2012 +0300
@@ -120,7 +120,7 @@
 // Byte datatype
 // typedef unsigned long int word;
 typedef int32_t word;
-typedef uint8_t byte;
+typedef unsigned char byte;
 
 // Keywords
 #ifndef __MAIN_CXX__
--- a/databuffer.h	Mon Aug 13 19:12:21 2012 +0300
+++ b/databuffer.h	Mon Aug 13 23:10:39 2012 +0300
@@ -44,7 +44,7 @@
 #include <string.h>
 #include "common.h"
 
-#define MAX_MARKS 256
+#define MAX_MARKS 512
 
 extern int g_NextMark;
 
--- a/main.cxx	Mon Aug 13 19:12:21 2012 +0300
+++ b/main.cxx	Mon Aug 13 23:10:39 2012 +0300
@@ -59,6 +59,7 @@
 const char* g_Keywords[] = {
 	"break",
 	"case",
+	"default",
 	"do",
 	"event",
 	"for",
@@ -75,7 +76,6 @@
 	// 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
 	"continue",
-	"default",
 	"else",
 	"enum", // Would enum actually be useful? I think so.
 	"func", // Would function support need external support from zandronum?
@@ -163,12 +163,12 @@
 	InitVariables ();
 	
 	// Prepare reader and writer
-	ScriptReader *r = new ScriptReader (argv[1]);
-	ObjWriter *w = new ObjWriter (outfile);
+	ScriptReader* r = new ScriptReader (argv[1]);
+	ObjWriter* w = new ObjWriter (outfile);
 	
 	// We're set, begin parsing :)
 	printf ("Parsing script...\n");
-	r->BeginParse (w);
+	r->ParseBotScript (w);
 	printf ("Script parsed successfully.\n");
 	
 	// Parse done, print statistics and write to file
--- a/objwriter.cxx	Mon Aug 13 19:12:21 2012 +0300
+++ b/objwriter.cxx	Mon Aug 13 23:10:39 2012 +0300
@@ -101,15 +101,17 @@
 
 // Write string table
 void ObjWriter::WriteStringTable () {
-	// If we added strings here, we need to write a list of them.
 	unsigned int stringcount = CountStringTable ();
-	if (stringcount) {
-		Write<long> (DH_STRINGLIST);
-		Write<long> (stringcount);
-		
-		for (unsigned int a = 0; a < stringcount; a++)
-			WriteString (g_StringTable[a]);
-	}
+	if (!stringcount)
+		return;
+	
+	// Write header
+	Write<long> (DH_STRINGLIST);
+	Write<long> (stringcount);
+	
+	// Write all strings
+	for (unsigned int a = 0; a < stringcount; a++)
+		WriteString (g_StringTable[a]);
 }
 
 // Write main buffer to file
@@ -124,11 +126,11 @@
 		if (!ref)
 			continue;
 		
-		for (unsigned int v = 0; v < sizeof (word); v++) {
-			union_t<word> uni;
-			uni.val = static_cast<word> (MainBuffer->marks[ref->num]->pos);
+		// Substitute the placeholder with the mark position
+		union_t<word> uni;
+		uni.val = static_cast<word> (MainBuffer->marks[ref->num]->pos);
+		for (unsigned int v = 0; v < sizeof (word); v++)
 			memset (MainBuffer->buffer + ref->pos + v, uni.b[v], 1);
-		}
 		
 		/*
 		printf ("reference %u at %d resolved to %u at %d\n",
@@ -139,7 +141,7 @@
 	
 	// Then, dump the main buffer to the file
 	for (unsigned int x = 0; x < MainBuffer->writesize; x++)
-		WriteDataToFile<unsigned char> (*(MainBuffer->buffer+x));
+		WriteDataToFile<byte> (*(MainBuffer->buffer+x));
 	
 	printf ("-- %u byte%s written to %s\n", numWrittenBytes, PLURAL (numWrittenBytes), filepath.chars());
 	fclose (fp);
--- a/objwriter.h	Mon Aug 13 19:12:21 2012 +0300
+++ b/objwriter.h	Mon Aug 13 23:10:39 2012 +0300
@@ -54,13 +54,33 @@
 public:
 	// ====================================================================
 	// MEMBERS
+	
+	// Pointer to the file we're writing to
 	FILE* fp;
+	
+	// Path to the file we're writing to
 	str filepath;
+	
+	// The main buffer - the contents of this is what we
+	// write to file after parsing is complete
 	DataBuffer* MainBuffer;
+	
+	// onenter buffer - the contents of the onenter{} block
+	// is buffered here and is merged further at the end of state
 	DataBuffer* OnEnterBuffer;
+	
+	// Mainloop buffer - the contents of the mainloop{} block
+	// is buffered here and is merged further at the end of state
 	DataBuffer* MainLoopBuffer;
+	
+	// Switch buffer - switch case data is recorded to this
+	// buffer initially, instead of into main buffer.
 	DataBuffer* SwitchBuffer;
+	
+	// How many bytes have we written to file?
 	unsigned int numWrittenBytes;
+	
+	// How many references did we resolve in the main buffer?
 	unsigned int numWrittenReferences;
 	
 	// ====================================================================
@@ -83,9 +103,7 @@
 	void DeleteMark (unsigned int mark);
 	
 	template <class T> void Write (T stuff) {
-		DataBuffer* buffer = GetCurrentBuffer ();
-		buffer->Write<T> (stuff);
-		return;
+		GetCurrentBuffer ()->Write<T> (stuff);
 	}
 	
 	// Default to word
@@ -93,11 +111,13 @@
 		Write<word> (stuff);
 	}
 	
+private:
+	// Write given data to file.
 	template <class T> void WriteDataToFile (T stuff) {
 		// One byte at a time
+		union_t<T> uni;
+		uni.val = stuff;
 		for (unsigned int x = 0; x < sizeof (T); x++) {
-			union_t<T> uni;
-			uni.val = stuff;
 			fwrite (&uni.b[x], 1, 1, fp);
 			numWrittenBytes++;
 		}
--- a/parser.cxx	Mon Aug 13 19:12:21 2012 +0300
+++ b/parser.cxx	Mon Aug 13 23:10:39 2012 +0300
@@ -70,10 +70,10 @@
 // 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 ScriptReader::BeginParse (ObjWriter* w) {
+void ScriptReader::ParseBotScript (ObjWriter* w) {
 	while (Next()) {
 		// ============================================================
-		if (!token.icompare ("state")) {
+		if (!token.compare ("state")) {
 			MUST_TOPLEVEL
 			
 			MustString ();
@@ -108,7 +108,7 @@
 		}
 		
 		// ============================================================
-		if (!token.icompare ("event")) {
+		if (!token.compare ("event")) {
 			MUST_TOPLEVEL
 			
 			// Event definition
@@ -129,7 +129,7 @@
 		}
 		
 		// ============================================================
-		if (!token.icompare ("mainloop")) {
+		if (!token.compare ("mainloop")) {
 			MUST_TOPLEVEL
 			MustNext ("{");
 			
@@ -140,7 +140,7 @@
 		}
 		
 		// ============================================================
-		if (!token.icompare ("onenter") || !token.icompare ("onexit")) {
+		if (!token.compare ("onenter") || !token.compare ("onexit")) {
 			MUST_TOPLEVEL
 			bool onenter = !token.compare ("onenter");
 			MustNext ("{");
@@ -176,25 +176,8 @@
 		}
 		
 		// ============================================================
-		// Label
-		if (!PeekNext().compare (":")) {
-			MUST_NOT_TOPLEVEL
-			
-			if (IsKeyword (token))
-				ParserError ("label name `%s` conflicts with keyword\n", token.chars());
-			if (FindCommand (token))
-				ParserError ("label name `%s` conflicts with command name\n", token.chars());
-			if (FindGlobalVariable (token))
-				ParserError ("label name `%s` conflicts with variable\n", token.chars());
-			
-			w->AddMark (token);
-			MustNext (":");
-			continue;
-		}
-		
-		// ============================================================
 		// Goto
-		if (!token.icompare ("goto")) {
+		if (!token.compare ("goto")) {
 			MUST_NOT_TOPLEVEL
 			
 			// Get the name of the label
@@ -216,7 +199,7 @@
 		
 		// ============================================================
 		// If
-		if (!token.icompare ("if")) {
+		if (!token.compare ("if")) {
 			MUST_NOT_TOPLEVEL
 			PushBlockStack ();
 			
@@ -266,7 +249,6 @@
 			MustNext ();
 			DataBuffer* expr = ParseExpression (TYPE_INT);
 			MustNext (")");
-			
 			MustNext ("{");
 			
 			// Write condition
@@ -285,7 +267,7 @@
 		
 		// ============================================================
 		// For loop
-		if (!token.icompare ("for")) {
+		if (!token.compare ("for")) {
 			MUST_NOT_TOPLEVEL
 			PushBlockStack ();
 			
@@ -328,7 +310,7 @@
 		
 		// ============================================================
 		// Do/while loop
-		if (!token.icompare ("do")) {
+		if (!token.compare ("do")) {
 			MUST_NOT_TOPLEVEL
 			PushBlockStack ();
 			MustNext ("{");
@@ -339,7 +321,7 @@
 		
 		// ============================================================
 		// Switch
-		if (!token.icompare ("switch")) {
+		if (!token.compare ("switch")) {
 			/* This goes a bit tricky. switch is structured in the
 			 * bytecode followingly:
 			 * (expression)
@@ -362,50 +344,70 @@
 			MustNext ("{");
 			blockstack[g_BlockStackCursor].type = BLOCKTYPE_SWITCH;
 			blockstack[g_BlockStackCursor].mark1 = w->AddMark (""); // end mark
+			blockstack[g_BlockStackCursor].buffer1 = NULL; // default header
 			continue;
 		}
 		
 		// ============================================================
-		if (!token.icompare ("case")) {
+		if (!token.compare ("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");
+			if (info->type != BLOCKTYPE_SWITCH)
+				ParserError ("case label outside 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;
+			for (int i = 0; i < MAX_CASE; i++)
+				if (info->casenumbers[i] == num)
+					ParserError ("multiple case %d labels in one switch", num);
 			
 			// Write down the expression and case-go-to. This builds
 			// the case tree. The closing event will write the actual
 			// blocks and move the marks appropriately.
-			//
-			// NULL the switch buffer for the case-go-to statement,
+			//	AddSwitchCase will add the reference to the mark
+			// for the case block that this heralds, and takes care
+			// of buffering setup and stuff like that.
+			//	NULL the switch buffer for the case-go-to statement,
 			// we want it all under the switch, not into the case-buffers.
 			w->SwitchBuffer = NULL;
 			w->Write<word> (DH_CASEGOTO);
 			w->Write<word> (num);
-			w->AddReference (m);
+			AddSwitchCase (w, NULL);
+			info->casenumbers[info->casecursor] = num;
+			continue;
+		}
+		
+		if (!token.compare ("default")) {
+			BlockInformation* info = &blockstack[g_BlockStackCursor];
+			if (info->type != BLOCKTYPE_SWITCH)
+				ParserError ("default label outside switch");
+			
+			if (info->buffer1)
+				ParserError ("multiple default labels in one switch");
 			
-			// Init a buffer for the case block, tell the object
-			// writer to record all written data to it.
-			info->casebuffers[info->casecursor] = w->SwitchBuffer = new DataBuffer;
+			MustNext (":");
+			
+			// 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* b = new DataBuffer;
+			info->buffer1 = b;
+			b->Write<word> (DH_DROP);
+			b->Write<word> (DH_GOTO);
+			AddSwitchCase (w, b);
 			continue;
 		}
 		
 		// ============================================================
 		// Break statement.
-		if (!token.icompare ("break")) {
+		if (!token.compare ("break")) {
 			if (!g_BlockStackCursor)
 				ParserError ("unexpected `break`");
 			
@@ -434,6 +436,23 @@
 		}
 		
 		// ============================================================
+		// Label
+		if (!PeekNext().compare (":")) {
+			MUST_NOT_TOPLEVEL
+			
+			if (IsKeyword (token))
+				ParserError ("label name `%s` conflicts with keyword\n", token.chars());
+			if (FindCommand (token))
+				ParserError ("label name `%s` conflicts with command name\n", token.chars());
+			if (FindGlobalVariable (token))
+				ParserError ("label name `%s` conflicts with variable\n", token.chars());
+			
+			w->AddMark (token);
+			MustNext (":");
+			continue;
+		}
+		
+		// ============================================================
 		if (!token.compare ("}")) {
 			// Closing brace
 			
@@ -471,7 +490,6 @@
 					w->AddReference (info->mark1);
 					break;
 				}
-				
 				case BLOCKTYPE_SWITCH: {
 					// Switch closes. Move down to the record buffer of
 					// the lower block.
@@ -481,6 +499,17 @@
 					else
 						w->SwitchBuffer = NULL;
 					
+					// If there was a default in the switch, write its header down now.
+					// If not, write instruction to jump to the end of switch after
+					// the headers (thus won't fall-through if no case matched)
+					if (info->buffer1)
+						w->WriteBuffer (info->buffer1);
+					else {
+						w->Write<word> (DH_DROP);
+						w->Write<word> (DH_GOTO);
+						w->AddReference (info->mark1);
+					}
+					
 					// Go through all of the buffers we
 					// recorded down and write them.
 					for (unsigned int u = 0; u < MAX_CASE; u++) {
@@ -821,6 +850,7 @@
 	for (int i = 0; i < MAX_CASE; i++) {
 		info->casemarks[i] = MAX_MARKS;
 		info->casebuffers[i] = NULL;
+		info->casenumbers[i] = -1;
 	}
 }
 
@@ -834,4 +864,27 @@
 	// If it's not a keyword, parse it as an expression.
 	DataBuffer* b = ParseExpression (TYPE_VOID);
 	return b;
+}
+
+void ScriptReader::AddSwitchCase (ObjWriter* w, DataBuffer* b) {
+	BlockInformation* info = &blockstack[g_BlockStackCursor];
+	
+	info->casecursor++;
+	if (info->casecursor >= MAX_CASE)
+		ParserError ("too many cases in one switch");
+	
+	// Init a mark for the case buffer
+	int m = w->AddMark ("");
+	info->casemarks[info->casecursor] = m;
+	
+	// Add a reference to the mark. "case" and "default" both
+	// add the necessary bytecode before the reference.
+	if (b)
+		b->AddMarkReference (m);
+	else
+		w->AddReference (m);
+	
+	// Init a buffer for the case block and tell the object
+	// writer to record all written data to it.
+	info->casebuffers[info->casecursor] = w->SwitchBuffer = new DataBuffer;
 }
\ No newline at end of file
--- a/scriptreader.cxx	Mon Aug 13 19:12:21 2012 +0300
+++ b/scriptreader.cxx	Mon Aug 13 23:10:39 2012 +0300
@@ -125,8 +125,8 @@
 	if (feof (fp[fc]))
 		return 0;
 	
-	char* c = (char*)malloc (sizeof (char));
-	if (!fread (c, sizeof (char), 1, fp[fc]))
+	char c;
+	if (!fread (&c, 1, 1, fp[fc]))
 		return 0;
 	
 	// We're at a newline, thus next char read will begin the next line
@@ -136,7 +136,7 @@
 		curchar[fc] = 0; // gets incremented to 1
 	}
 	
-	if (c[0] == '\n') {
+	if (c == '\n') {
 		atnewline = true;
 		
 		// Check for pre-processor directives
@@ -144,7 +144,7 @@
 	}
 	
 	curchar[fc]++;
-	return c[0];
+	return c;
 }
 
 // ============================================================================
@@ -375,14 +375,6 @@
 		MustNext ();
 	num += token;
 	
-	// The number can possibly start off with a minus sign, which
-	// also breaks the token. If we encounter it, read another token
-	// and merge it to this one.
-	if (!token.compare ("-")) {
-		MustNext ();
-		num += token;
-	}
-	
 	// "true" and "false" are valid numbers
 	if (!token.icompare ("true"))
 		token = "1";
@@ -392,5 +384,11 @@
 		if (!num.isnumber())
 			ParserError ("expected a number, got `%s`", num.chars());
 		token = num;
+		
+		// Overflow check
+		str check;
+		check.appendformat ("%d", atoi (num));
+		if (token.compare (check) != 0)
+			ParserWarning ("integer too large: %s -> %s", token.chars(), check.chars());
 	}
 }
\ No newline at end of file
--- a/scriptreader.h	Mon Aug 13 19:12:21 2012 +0300
+++ b/scriptreader.h	Mon Aug 13 23:10:39 2012 +0300
@@ -67,6 +67,9 @@
 	// Marks to case-blocks
 	int casemarks[MAX_CASE];
 	
+	// Numbers of the case labels
+	int casenumbers[MAX_CASE];
+	
 	// actual case blocks
 	DataBuffer* casebuffers[MAX_CASE];
 	
@@ -119,7 +122,7 @@
 	void ParserWarning (const char* message, ...);
 	
 	// parser.cxx:
-	void BeginParse (ObjWriter* w);
+	void ParseBotScript (ObjWriter* w);
 	DataBuffer* ParseCommand (CommandDef* comm);
 	DataBuffer* ParseExpression (int reqtype);
 	DataBuffer* ParseAssignment (ScriptVar* var);
@@ -131,6 +134,7 @@
 	void PreprocessDirectives ();
 	void PreprocessMacros ();
 	DataBuffer* ParseStatement (ObjWriter* w);
+	void AddSwitchCase (ObjWriter* w, DataBuffer* b);
 	
 private:
 	bool atnewline;
--- a/stringtable.h	Mon Aug 13 19:12:21 2012 +0300
+++ b/stringtable.h	Mon Aug 13 23:10:39 2012 +0300
@@ -39,7 +39,6 @@
  */
 
 #include "bots.h"
-#include "str.h"
 
 void InitStringTable();
 unsigned int PushToStringTable (char* s);

mercurial