Mon, 13 Aug 2012 23:10:39 +0300
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;