Mon, 13 Aug 2012 19:04:29 +0300
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