Added mark/reference system to be able to refer to positions in the buffered bytecode. Labels and go-to support.

Sun, 29 Jul 2012 16:55:32 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sun, 29 Jul 2012 16:55:32 +0300
changeset 37
c349dca807f9
parent 36
a8838b5f1213
child 38
e4bbd540663b

Added mark/reference system to be able to refer to positions in the buffered bytecode. Labels and go-to support.

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.h file | annotate | diff | comparison | revisions
--- a/common.h	Sun Jul 29 04:02:07 2012 +0300
+++ b/common.h	Sun Jul 29 16:55:32 2012 +0300
@@ -126,4 +126,34 @@
 #endif
 bool IsKeyword (str s);
 
+// Script mark -- also serves as reference type
+struct ScriptMark {
+	int type;
+	str name;
+	size_t pos;
+};
+
+struct ScriptMarkReference {
+	unsigned int num;
+	size_t pos;
+};
+
+// ====================================================================
+// Finds a byte in the given value.
+template <class T> unsigned char GetByteIndex (T a, unsigned int b) {
+	if (b >= sizeof (T))
+		error ("CharByte: tried to get byte %u out of a %u-byte %s\n",
+			b, sizeof (T), typeid (T).name());
+	
+	unsigned long p1 = pow<unsigned long> (256, b);
+	unsigned long p2 = pow<unsigned long> (256, b+1);
+	unsigned long r = (a % p2) / p1;
+	
+	if (r > 256)
+		error ("DataBuffer::CharByte: result %lu too big!", r);
+	
+	unsigned char ur = static_cast<unsigned char> (r);
+	return ur;
+}
+
 #endif // __COMMON_H__
\ No newline at end of file
--- a/databuffer.h	Sun Jul 29 04:02:07 2012 +0300
+++ b/databuffer.h	Sun Jul 29 16:55:32 2012 +0300
@@ -44,6 +44,8 @@
 #include <string.h>
 #include "common.h"
 
+#define MAX_MARKS 256
+
 // ============================================================================
 // DataBuffer: A dynamic data buffer.
 class DataBuffer {
@@ -57,18 +59,44 @@
 	// Written size of the buffer
 	unsigned int writesize;
 	
+	// Marks and references
+	ScriptMark* marks[MAX_MARKS];
+	ScriptMarkReference* refs[MAX_MARKS];
+	
+	// ====================================================================
 	// METHODS
+	
+	// ====================================================================
+	// Constructor
 	DataBuffer (unsigned int size=128) {
 		writesize = 0;
 		
 		buffer = new unsigned char[size];
 		allocsize = size;
+		
+		// Clear the marks table out
+		for (unsigned int u = 0; u < MAX_MARKS; u++) {
+			marks[u] = NULL;
+			refs[u] = NULL;
+		}
 	}
 	
+	// ====================================================================
 	~DataBuffer () {
 		delete buffer;
+		
+		// Delete any marks and references
+		for (unsigned int u = 0; u < MAX_MARKS; u++) {
+			if (marks[u])
+				delete marks[u];
+			
+			if (refs[u])
+				delete refs[u];
+		}
 	}
 	
+	// ====================================================================
+	// Write stuff to the buffer
 	template<class T> void Write(T stuff) {
 		if (sizeof (char) != 1) {
 			error ("DataBuffer: sizeof(char) must be 1!\n");
@@ -99,39 +127,100 @@
 		for (unsigned int x = 0; x < sizeof (T); x++) {
 			if (writesize >= allocsize)
 				error ("DataBuffer: written size exceeds allocated size!\n");
-			buffer[writesize] = CharByte<T> (stuff, x);
+			buffer[writesize] = GetByteIndex<T> (stuff, x);
 			writesize++;
 		}
 	}
 	
+	// ====================================================================
 	// Merge another data buffer into this one.
 	void Merge (DataBuffer* other) {
 		if (!other)
-				return;
+			return;
 		
 		for (unsigned int x = 0; x < other->writesize; x++) {
 			unsigned char c = *(other->buffer+x);
 			Write<unsigned char> (c);
 		}
 		
+		// 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]->type, other->marks[u]->name);
+				marks[u]->pos += other->writesize;
+			}
+			
+			if (other->refs[u]) {
+				// Same for references
+				unsigned int r = AddMarkReference (other->refs[u]->num);
+				refs[r]->pos += other->writesize;
+			}
+		}
+		
 		delete other;
 	}
 	
-private:
-	template <class T> unsigned char CharByte (T a, unsigned int b) {
-		if (b >= sizeof (T))
-			error ("CharByte: tried to get byte %u out of a %u-byte %s\n",
-				b, sizeof (T), typeid (T).name());
+	// ====================================================================
+	// Adds a mark to the buffer. A mark is a reference to a particular
+	// position in the bytecode. The actual permanent position cannot
+	// be predicted in any way or form, thus these things will be used
+	// to "mark" a position like that for future use.
+	unsigned int AddMark (int type, str name) {
+		// Find a free slot for the mark
+		unsigned int u;
+		for (u = 0; u < MAX_MARKS; u++)
+			if (!marks[u])
+				break;
+		
+		if (u >= MAX_MARKS)
+			error ("mark quota exceeded, all labels, if-structs and loops add marks\n");
+		
+		ScriptMark* m = new ScriptMark;
+		m->name = name;
+		m->type = type;
+		m->pos = writesize;
+		marks[u] = m;
+		return u;
+	}
+	
+	unsigned int AddMarkReference (unsigned int marknum) {
+		unsigned int u;
+		for (u = 0; u < MAX_MARKS; u++)
+			if (!refs[u])
+				break;
 		
-		unsigned long p1 = pow<unsigned long> (256, b);
-		unsigned long p2 = pow<unsigned long> (256, b+1);
-		unsigned long r = (a % p2) / p1;
+		if (u == MAX_MARKS)
+			error ("mark reference quota exceeded, all goto-statements, if-structs and loops add refs\n");
+		
+		// NOTE: Do not check if the mark actually exists here since a
+		// reference may come in the code earlier than the actual mark
+		// and the new mark number can be predicted.
+		ScriptMarkReference* r = new ScriptMarkReference;
+		r->num = marknum;
+		r->pos = writesize;
+		refs[u] = r;
 		
-		if (r > 256)
-			error ("DataBuffer::CharByte: result %lu too big!", r);
+		return u;
+	}
+	
+	// Delete a mark and all references to it.
+	void DeleteMark (unsigned int marknum) {
+		if (!marks[marknum])
+			return;
 		
-		unsigned char ur = static_cast<unsigned char> (r);
-		return ur;
+		// Delete the mark
+		delete marks[marknum];
+		marks[marknum] = NULL;
+		
+		// Delete its references
+		for (unsigned int u = 0; u < MAX_MARKS; u++) {
+			if (refs[u]->num == marknum) {
+				delete refs[u];
+				refs[u] = NULL;
+			}
+		}
 	}
 };
 
--- a/main.cxx	Sun Jul 29 04:02:07 2012 +0300
+++ b/main.cxx	Sun Jul 29 16:55:32 2012 +0300
@@ -63,6 +63,7 @@
 	"onenter",
 	"onexit",
 	"var",
+	"goto",
 	
 	// 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
@@ -75,7 +76,6 @@
 	"enum", // Would enum actually be useful? I think so.
 	"for",
 	"func", // Would function support need external support from zandronum?
-	"goto", // Labels and goto must be done before if or any loops...
 	"if",
 	"return",
 	"switch",
--- a/objwriter.cxx	Sun Jul 29 04:02:07 2012 +0300
+++ b/objwriter.cxx	Sun Jul 29 16:55:32 2012 +0300
@@ -135,11 +135,46 @@
 		error ("size of unsigned char isn't 1, but %d!\n", sizeof (unsigned char));
 	
 	for (unsigned int x = 0; x < MainBuffer->writesize; x++) {
-		unsigned char c = *(MainBuffer->buffer+x);
-		fwrite (&c, 1, 1, fp);
-		numWrittenBytes++;
+		// Check if this position is a reference
+		for (unsigned int r = 0; r < MAX_MARKS; r++) {
+			if (MainBuffer->refs[r] && MainBuffer->refs[r]->pos == x) {
+				word ref = static_cast<word> (MainBuffer->marks[MainBuffer->refs[r]->num]->pos);
+				WriteDataToFile<word> (ref);
+			}
+		}
+		
+		WriteDataToFile<unsigned char> (*(MainBuffer->buffer+x));
 	}
 	
 	printf ("-- %u byte%s written to %s\n", numWrittenBytes, PLURAL (numWrittenBytes), filepath.chars());
 	fclose (fp);
+}
+
+DataBuffer* ObjWriter::GetCurrentBuffer() {
+	return	(g_CurMode == MODE_MAINLOOP) ? MainLoopBuffer :
+		(g_CurMode == MODE_ONENTER) ? OnEnterBuffer :
+		MainBuffer;
+}
+
+ScriptMark* g_ScriptMark = NULL;
+
+// Adds a mark
+unsigned int ObjWriter::AddMark (int type, str name) {
+	return GetCurrentBuffer()->AddMark (type, name);
+}
+
+// Adds a reference
+unsigned int ObjWriter::AddReference (unsigned int mark) {
+	DataBuffer* b = GetCurrentBuffer();
+	return b->AddMarkReference (mark);
+}
+
+// Finds a mark
+unsigned int ObjWriter::FindMark (int type, str name) {
+	DataBuffer* b = GetCurrentBuffer();
+	for (unsigned int u = 0; u < MAX_MARKS; u++) {
+		if (b->marks[u] && b->marks[u]->type == type && !b->marks[u]->name.icompare (name))
+			return u;
+	}
+	return MAX_MARKS;
 }
\ No newline at end of file
--- a/objwriter.h	Sun Jul 29 04:02:07 2012 +0300
+++ b/objwriter.h	Sun Jul 29 16:55:32 2012 +0300
@@ -72,17 +72,29 @@
 	void WriteBuffers ();
 	void WriteStringTable ();
 	void WriteToFile ();
+	DataBuffer* GetCurrentBuffer ();
+	
+	unsigned int AddMark (int type, str name);
+	unsigned int FindMark (int type, str name);
+	unsigned int AddReference (unsigned int mark);
 	
 	template <class T> void Write (T stuff) {
-		DataBuffer* buffer =	(g_CurMode == MODE_MAINLOOP) ? MainLoopBuffer :
-					(g_CurMode == MODE_ONENTER) ? OnEnterBuffer :
-					MainBuffer;
+		DataBuffer* buffer = GetCurrentBuffer ();
 		buffer->Write<T> (stuff);
 		return;
 	}
 	
 	// Cannot use default arguments in function templates..
 	void Write (word stuff) {Write<word> (stuff);}
+	
+	template <class T> void WriteDataToFile (T stuff) {
+		// One byte at a time
+		for (unsigned int x = 0; x < sizeof (T); x++) {
+			unsigned char c = GetByteIndex<T> (stuff, x);
+			fwrite (&c, 1, 1, fp);
+			numWrittenBytes++;
+		}
+	}
 };
 
 #endif // __OBJWRITER_H__
\ No newline at end of file
--- a/parser.cxx	Sun Jul 29 04:02:07 2012 +0300
+++ b/parser.cxx	Sun Jul 29 16:55:32 2012 +0300
@@ -52,7 +52,10 @@
 #include "variables.h"
 
 #define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \
-	ParserError ("%ss may only be defined at top level!", token.chars());
+	ParserError ("%s-statements may only be defined at top level!", token.chars());
+
+#define MUST_NOT_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \
+	ParserError ("%s-statements may not be defined at top level!", token.chars());
 
 int g_NumStates = 0;
 int g_NumEvents = 0;
@@ -60,12 +63,14 @@
 str g_CurState = "";
 bool g_stateSpawnDefined = false;
 bool g_GotMainLoop = false;
+int g_StructStack = 0;
 
 // ============================================================================
 // 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) {
+	g_StructStack = 0;
 	while (Next()) {
 		printf ("BeginParse: token: `%s`\n", token.chars());
 		if (!token.icompare ("state")) {
@@ -166,8 +171,45 @@
 			continue;
 		}
 		
+		// Label
+		if (!PeekNext().compare (":")) {
+			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 (MARKTYPE_LABEL, token);
+			MustNext (":");
+			continue;
+		}
+		
+		// Goto
+		if (!token.icompare ("goto")) {
+			// Get the name of the label
+			MustNext ();
+			
+			// Find the mark this goto statement points to
+			unsigned int m = w->FindMark (MARKTYPE_LABEL, token);
+			if (m == MAX_MARKS)
+				ParserError ("unknown label `%s`!", token.chars());
+			
+			// Add a reference to the mark.
+			w->Write<word> (DH_GOTO);
+			w->AddReference (m);
+			MustNext (";");
+			continue;
+		}
+		
 		if (!token.compare ("}")) {
-			printf ("parse closing brace\n");
+			// If this was done inside the struct stack (i.e.
+			// inside "if" for instance), it does not end the mode.
+			if (g_StructStack > 0) {
+				g_StructStack--;
+				continue;
+			}
+			
 			// Closing brace
 			int dataheader =	(g_CurMode == MODE_EVENT) ? DH_ENDEVENT :
 						(g_CurMode == MODE_MAINLOOP) ? DH_ENDMAINLOOP :
@@ -340,9 +382,6 @@
 	
 	DataBuffer* lb = NULL;
 	
-	// If it's a variable, note it down - we need to do special checks with it later.
-	ScriptVar* var = FindGlobalVariable (token);
-	
 	lb = ParseExprValue (reqtype);
 	printf ("done\n");
 	
@@ -375,7 +414,7 @@
 	retbuf->Merge (rb);
 	retbuf->Merge (lb);
 	
-	long dh = DataHeaderByOperator (var, oper);
+	long dh = DataHeaderByOperator (NULL, oper);
 	retbuf->Write<word> (dh);
 	
 	printf ("expression complete\n");
@@ -490,23 +529,16 @@
 // 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* ScriptReader::ParseAssignment (ScriptVar* var) {
-	printf ("ASSIGNMENT: this token is `%s`, next token is `%s`\n",
-		token.chars(), PeekNext().chars());
-	
 	// Get an operator
-	printf ("parse assignment operator at token %s\n", token.chars());
-	
 	MustNext ();
 	int oper = ParseOperator ();
 	if (!IsAssignmentOperator (oper))
 		ParserError ("expected assignment operator");
-	printf ("got %d\n", oper);
 	
 	if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction
 		ParserError ("can't alter variables at top level");
 	
 	// Parse the right operand,
-	printf ("parse right operand\n");
 	MustNext ();
 	DataBuffer* retbuf = ParseExprValue (TYPE_INT);
 	
@@ -514,6 +546,5 @@
 	retbuf->Write<word> (dh);
 	retbuf->Write<word> (var->index);
 	
-	printf ("assignment complete\n");
 	return retbuf;
 }
\ No newline at end of file
--- a/scriptreader.h	Sun Jul 29 04:02:07 2012 +0300
+++ b/scriptreader.h	Sun Jul 29 16:55:32 2012 +0300
@@ -136,4 +136,11 @@
 	OPER_ASSIGNMOD
 };
 
+// Mark types
+enum {
+	MARKTYPE_LABEL,
+	MARKTYPE_IF,
+	MARKTYPE_INTERNAL, // internal structures
+};
+
 #endif // __SCRIPTREADER_H__
\ No newline at end of file

mercurial