Sun, 29 Jul 2012 16:55:32 +0300
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