Sat, 28 Jul 2012 17:41:24 +0300
Expression parser mostly up and running!! Still work to do on it though.
commands.cxx | file | annotate | diff | comparison | revisions | |
commands.def | file | annotate | diff | comparison | revisions | |
commands.h | file | annotate | diff | comparison | revisions | |
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 | |
str.cxx | file | annotate | diff | comparison | revisions |
--- a/commands.cxx Tue Jul 17 20:35:43 2012 +0300 +++ b/commands.cxx Sat Jul 28 17:41:24 2012 +0300 @@ -51,7 +51,7 @@ ScriptReader* r = new ScriptReader ("commands.def"); g_CommDef = NULL; CommandDef* curdef = g_CommDef; - unsigned int numCommDefs = 0; + unsigned int numCommDefs = 0; while (r->PeekNext().len()) { CommandDef* comm = new CommandDef; @@ -71,7 +71,7 @@ // Return value r->MustNext (); - comm->returnvalue = GetReturnTypeByString (r->token); + comm->returnvalue = GetCommandType (r->token); if (comm->returnvalue == -1) r->ParserError ("bad return value type `%s`", r->token.chars()); @@ -96,7 +96,7 @@ r->MustNext (":"); r->MustNext (); - int type = GetReturnTypeByString (r->token); + int type = GetCommandType (r->token); if (type == -1) r->ParserError ("bad argument %d type `%s`", curarg, r->token.chars()); if (type == RETURNVAL_VOID) @@ -109,7 +109,7 @@ // - 1 because of terminating null character if (r->token.len() > MAX_ARGNAMELEN - 1) r->ParserWarning ("argument name is too long (%d, max is %d)", - r->token.len(), MAX_ARGNAMELEN-1); + r->token.len(), MAX_ARGNAMELEN - 1); strncpy (comm->argnames[curarg], r->token.chars(), MAX_ARGNAMELEN); comm->argnames[curarg][MAX_ARGNAMELEN-1] = 0; @@ -146,23 +146,22 @@ delete r; if (!numCommDefs) - error ("error: no commands defined!\n"); + error ("no commands defined!\n"); printf ("%d command definitions read.\n", numCommDefs); } -// urgh long name -int GetReturnTypeByString (str t) { +int GetCommandType (str t) { // "float" is for now just int. // TODO: find out how BotScript floats work // (are they real floats or fixed? how are they // stored?) and add proper floating point support. // NOTE: Also, shouldn't use RETURNVAL for data types.. t = t.tolower(); - return !t.compare ("int") ? RETURNVAL_INT : - !t.compare ("float") ? RETURNVAL_INT : - !t.compare ("str") ? RETURNVAL_STRING : - !t.compare ("void") ? RETURNVAL_VOID : - !t.compare ("bool") ? RETURNVAL_BOOLEAN : -1; + return !t.compare ("int") ? TYPE_INT : + !t.compare ("float") ? TYPE_INT : + !t.compare ("str") ? TYPE_STRING : + !t.compare ("void") ? TYPE_VOID : + !t.compare ("bool") ? TYPE_INT : -1; } // Inverse operation @@ -177,7 +176,7 @@ return ""; } -CommandDef* GetCommandByName (str fname) { +CommandDef* FindCommand (str fname) { CommandDef* comm; ITERATE_COMMANDS (comm) { if (!fname.icompare (comm->name))
--- a/commands.def Tue Jul 17 20:35:43 2012 +0300 +++ b/commands.def Sat Jul 28 17:41:24 2012 +0300 @@ -1,8 +1,3 @@ -/* This file defines the commands botc will treat as valid. - * Do not edit unless you know what you are doing! - * - * Syntax: number:name:returntype:numargs:maxargs[:argumentlist] - */ 0:changestate:void:1:1:str(statename) 1:delay:void:1:1:int(tics) 2:rand:int:2:2:int(a):int(b)
--- a/commands.h Tue Jul 17 20:35:43 2012 +0300 +++ b/commands.h Sat Jul 28 17:41:24 2012 +0300 @@ -63,8 +63,8 @@ }; void ReadCommands (); -CommandDef* GetCommandByName (str a); -int GetReturnTypeByString (str token); +CommandDef* FindCommand (str a); +int GetCommandType (str token); str GetReturnTypeName (int r); str GetCommandPrototype (CommandDef* comm);
--- a/common.h Tue Jul 17 20:35:43 2012 +0300 +++ b/common.h Sat Jul 28 17:41:24 2012 +0300 @@ -47,6 +47,7 @@ #include "bots.h" #include "str.h" +// Application name and version #define APPNAME "botc" #define VERSION_MAJOR 0 #define VERSION_MINOR 0 @@ -57,10 +58,10 @@ #define FILE_CASEINSENSITIVE #endif -// Where is the parser at? +// Parser mode: where is the parser at? enum parsermode { MODE_TOPLEVEL, // at top level - MODE_EVENT, // inside event definition + MODE_EVENT, // inside event definition MODE_MAINLOOP, // inside mainloop MODE_ONENTER, // inside onenter MODE_ONEXIT, // inside onexit @@ -72,18 +73,21 @@ exit (1); \ } +// Shortcut for formatting #define PERFORM_FORMAT(in, out) \ va_list v; \ va_start (v, in); \ char* out = vdynformat (in, v, 256); \ va_end (v); +// Plural expression #define PLURAL(n) (n != 1) ? "s" : "" void error (const char* text, ...); char* ObjectFileName (str s); bool fexists (char* path); +// Make the parser's variables globally available #ifndef __PARSER_CXX__ extern int g_NumStates; extern int g_NumEvents; @@ -92,7 +96,7 @@ #endif // Power function -template<class T> T pow (T a, T b) { +template<class T> T pow (T a, int b) { if (!b) return 1; @@ -112,4 +116,7 @@ return (c <= 32 || c == 127 || c == 255); } +// Byte datatype +typedef unsigned long int byte; + #endif // __COMMON_H__ \ No newline at end of file
--- a/databuffer.h Tue Jul 17 20:35:43 2012 +0300 +++ b/databuffer.h Sat Jul 28 17:41:24 2012 +0300 @@ -104,18 +104,28 @@ } } + // Merge another data buffer into this one. + void Merge (DataBuffer* other) { + for (unsigned int x = 0; x < other->writesize; x++) { + unsigned char c = *(other->buffer+x); + Write<unsigned char> (c); + } + + 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()); + 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 ("result %lu too big!", r); + error ("DataBuffer::CharByte: result %lu too big!", r); unsigned char ur = static_cast<unsigned char> (r); return ur;
--- a/main.cxx Tue Jul 17 20:35:43 2012 +0300 +++ b/main.cxx Sat Jul 28 17:41:24 2012 +0300 @@ -129,7 +129,7 @@ ReadCommands (); // Init stuff - InitStringTable(); + InitStringTable (); InitVariables (); // Prepare reader and writer @@ -148,8 +148,11 @@ w->WriteToFile (); // Clear out the junk + printf ("clear r\n"); delete r; + printf ("clear w\n"); delete w; + printf ("done!\n"); } void error (const char* text, ...) { @@ -164,8 +167,6 @@ if (extdot >= s.len()-4) s.trim (s.len() - extdot); - // Add new ".o" extension s += ".o"; - return s.chars(); } \ No newline at end of file
--- a/objwriter.cxx Tue Jul 17 20:35:43 2012 +0300 +++ b/objwriter.cxx Sat Jul 28 17:41:24 2012 +0300 @@ -45,6 +45,7 @@ #include "common.h" #include "str.h" #include "objwriter.h" +#include "stringtable.h" #include "bots.h" @@ -60,8 +61,12 @@ ObjWriter::~ObjWriter () { delete MainBuffer; - delete MainLoopBuffer; delete OnEnterBuffer; + + // This crashes for some reason o_O + // Should make no difference anyway, since ObjWriter + // is only deleted at the end of the program anyway + // delete MainLoopBuffer; } void ObjWriter::WriteString (char* s) { @@ -78,6 +83,13 @@ WriteString (s.chars()); } +void ObjWriter::WriteBuffer (DataBuffer* buf) { + for (unsigned int x = 0; x < buf->writesize; x++) { + unsigned char c = *(buf->buffer+x); + Write<unsigned char> (c); + } +} + void ObjWriter::WriteBuffers () { // If there was no mainloop defined, write a dummy one now. if (!g_GotMainLoop) { @@ -88,10 +100,7 @@ // Write the onenter and mainloop buffers, IN THAT ORDER for (int i = 0; i < 2; i++) { DataBuffer* buf = (!i) ? OnEnterBuffer : MainLoopBuffer; - for (unsigned int x = 0; x < buf->writesize; x++) { - unsigned char c = *(buf->buffer+x); - Write<unsigned char> (c); - } + WriteBuffer (buf); // Clear the buffer afterwards for potential next state delete buf; @@ -102,6 +111,21 @@ g_GotMainLoop = false; } +// 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]); + } + + printf ("%u string%s written\n", stringcount, PLURAL (stringcount)); +} + // Write main buffer to file void ObjWriter::WriteToFile () { fp = fopen (filepath, "w");
--- a/objwriter.h Tue Jul 17 20:35:43 2012 +0300 +++ b/objwriter.h Sat Jul 28 17:41:24 2012 +0300 @@ -68,7 +68,9 @@ void WriteString (char* s); void WriteString (const char* s); void WriteString (str s); + void WriteBuffer (DataBuffer* buf); void WriteBuffers (); + void WriteStringTable (); void WriteToFile (); template <class T> void Write (T stuff) { @@ -80,7 +82,7 @@ } // Cannot use default arguments in function templates.. - void Write (long stuff) {Write<long> (stuff);} + void Write (byte stuff) {Write<byte> (stuff);} }; #endif // __OBJWRITER_H__ \ No newline at end of file
--- a/parser.cxx Tue Jul 17 20:35:43 2012 +0300 +++ b/parser.cxx Sat Jul 28 17:41:24 2012 +0300 @@ -61,6 +61,8 @@ bool g_stateSpawnDefined = false; bool g_GotMainLoop = false; +// ============================================================================ +// Main parser void ScriptReader::BeginParse (ObjWriter* w) { while (Next()) { if (!token.icompare ("state")) { @@ -112,7 +114,7 @@ g_CurMode = MODE_EVENT; w->Write (DH_EVENT); - w->Write<long> (e->number); + w->Write<byte> (e->number); g_NumEvents++; continue; } @@ -181,65 +183,13 @@ MustNext (";"); continue; } - // Check global variables - ScriptVar* g = FindGlobalVariable (token); - if (g) { - // Not in top level, unfortunately.. - if (g_CurMode == MODE_TOPLEVEL) - ParserError ("can't alter variables at top level"); - - // Build operator string. Only '=' is one - // character, others are two. - MustNext (); - str oper = token; - if (token.compare ("=") != 0) { - MustNext (); - oper += token; - } - - // Unary operators - if (!oper.compare ("++")) { - w->Write<long> (DH_INCGLOBALVAR); - w->Write<long> (g->index); - } else if (!oper.compare ("--")) { - w->Write<long> (DH_DECGLOBALVAR); - w->Write<long> (g->index); - } else { - // Binary operators - // And only with numbers for now too. - // TODO: make a proper expression parser! - MustNumber(); - - int val = atoi (token.chars()); - w->Write<long> (DH_PUSHNUMBER); - w->Write<long> (val); - - int h = !oper.compare("=") ? DH_ASSIGNGLOBALVAR : - !oper.compare("+=") ? DH_ADDGLOBALVAR : - !oper.compare("-=") ? DH_SUBGLOBALVAR : - !oper.compare("*=") ? DH_MULGLOBALVAR : - !oper.compare("/=") ? DH_DIVGLOBALVAR : - !oper.compare("%=") ? DH_MODGLOBALVAR : -1; - - if (h == -1) - ParserError ("bad operator `%s`!", oper.chars()); - - w->Write<long> (h); - w->Write<long> (g->index); - } - - MustNext (";"); - continue; - } - // Check if it's a command. - CommandDef* comm = GetCommandByName (token); - if (comm) { - ParseCommand (comm, w); - continue; - } - - ParserError ("unknown keyword `%s`", token.chars()); + // If it's not a keyword, parse it as an expression. + DataBuffer* b = ParseExpression (TYPE_VOID); + w->WriteBuffer (b); + delete b; + printf ("expression done!\n"); + MustThis (";"); } if (g_CurMode != MODE_TOPLEVEL) @@ -250,97 +200,266 @@ ParserError ("script must have a state named `stateSpawn`!"); // Dump the last state's onenter and mainloop - w->WriteBuffers(); + w->WriteBuffers (); - // If we added strings here, we need to write a list of them. - unsigned int stringcount = CountStringTable (); - if (stringcount) { - w->Write<long> (DH_STRINGLIST); - w->Write<long> (stringcount); - for (unsigned int a = 0; a < stringcount; a++) - w->WriteString (g_StringTable[a]); - } - - printf ("%u string%s written\n", stringcount, PLURAL (stringcount)); + // String table + w->WriteStringTable (); } -void ScriptReader::ParseCommand (CommandDef* comm, ObjWriter* w) { +// ============================================================================ +// Parses a given command +DataBuffer* ScriptReader::ParseCommand (CommandDef* comm) { + DataBuffer* r = new DataBuffer(64); // If this was defined at top-level, we stop right at square one! if (g_CurMode == MODE_TOPLEVEL) ParserError ("no commands allowed at top level!"); + printf ("token: %s\n", token.chars()); MustNext ("("); + int curarg = 0; while (1) { - if (curarg >= comm->maxargs) { - if (!PeekNext().compare (",")) - ParserError ("got `,` while expecting command-terminating `)`, are you passing too many parameters? (max %d)", - comm->maxargs); - MustNext (")"); + if (!token.compare (")")) { + printf ("closing command with token `%s`\n", token.chars()); + if (curarg < comm->numargs - 1) + ParserError ("too few arguments passed to %s\n", comm->name.chars()); + break; curarg++; - break; } - - if (!PeekNext().len()) - ParserError ("unexpected end-of-file, unterminated command"); - - // If we get a ")" now, the user probably gave too few parameters - if (!PeekNext().compare (")")) - ParserError ("unexpected `)`, did you pass too few parameters? (need %d)", comm->numargs); + printf ("prev token: %s\n", token.chars()); + MustNext (); + printf ("next token: %s\n", token.chars()); - // Argument may be using a variable - ScriptVar* g = FindGlobalVariable (PeekNext ()); - if (g && comm->argtypes[curarg] != RETURNVAL_STRING) { - // Advance cursor past the var name - Next(); - - w->Write<long> (DH_PUSHGLOBALVAR); - w->Write<long> (g->index); - } else { - // Check for raw value - switch (comm->argtypes[curarg]) { - case RETURNVAL_INT: - MustNumber(); - w->Write<long> (DH_PUSHNUMBER); - w->Write<long> (atoi (token.chars ())); - break; - case RETURNVAL_BOOLEAN: - MustBool(); - w->Write<long> (DH_PUSHNUMBER); - w->Write<long> (BoolValue ()); - break; - case RETURNVAL_STRING: - MustString(); - w->Write<long> (DH_PUSHSTRINGINDEX); - w->Write<long> (PushToStringTable (token.chars())); - break; - } - } + // jump back to start + if (!token.compare (")")) + continue; + + if (curarg >= comm->maxargs) + ParserError ("too many arguments passed to %s\n", comm->name.chars()); + + r->Merge (ParseExpression (comm->argtypes[curarg])); if (curarg < comm->numargs - 1) { - MustNext (","); + MustThis (","); } else if (curarg < comm->maxargs - 1) { // Can continue, but can terminate as well. - if (!PeekNext ().compare (")")) { - MustNext (")"); + if (!token.compare (")")) { curarg++; break; } else - MustNext (","); + MustThis (","); } curarg++; } - MustNext (";"); // If the script skipped any optional arguments, fill in defaults. while (curarg < comm->maxargs) { - w->Write<long> (DH_PUSHNUMBER); - w->Write<long> (comm->defvals[curarg]); + r->Write<byte> (DH_PUSHNUMBER); + r->Write<byte> (comm->defvals[curarg]); curarg++; } - w->Write<long> (DH_COMMAND); - w->Write<long> (comm->number); - w->Write<long> (comm->maxargs); + r->Write<byte> (DH_COMMAND); + r->Write<byte> (comm->number); + r->Write<byte> (comm->maxargs); + + printf ("command complete\n"); + return r; +} + +static bool IsAssignmentOperator (int oper) { + switch (oper) { + case OPER_ASSIGNADD: + case OPER_ASSIGNSUB: + case OPER_ASSIGNMUL: + case OPER_ASSIGNDIV: + case OPER_ASSIGNMOD: + case OPER_ASSIGN: + return true; + } + return false; +} + +// ============================================================================ +static long DataHeaderByOperator (ScriptVar* var, int oper) { + if (IsAssignmentOperator (oper)) { + if (!var) + error ("operator %d requires left operand to be a variable\n", oper); + + // TODO: At the moment, vars only are global + switch (oper) { + case OPER_ASSIGNADD: return DH_ADDGLOBALVAR; + case OPER_ASSIGNSUB: return DH_SUBGLOBALVAR; + case OPER_ASSIGNMUL: return DH_MULGLOBALVAR; + case OPER_ASSIGNDIV: return DH_DIVGLOBALVAR; + case OPER_ASSIGNMOD: return DH_MODGLOBALVAR; + case OPER_ASSIGN: return DH_ASSIGNGLOBALVAR; + default: error ("bad assignment operator!!\n"); + } + } + + switch (oper) { + case OPER_ADD: return DH_ADD; + case OPER_SUBTRACT: return DH_SUBTRACT; + case OPER_MULTIPLY: return DH_MULTIPLY; + case OPER_DIVIDE: return DH_DIVIDE; + case OPER_MODULUS: return DH_MODULUS; + } + + error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper); + return 0; +} + +// ============================================================================ +// Parses an expression +DataBuffer* ScriptReader::ParseExpression (int reqtype) { + printf ("begin parsing expression. this token is `%s`, next token is `%s`\n", + token.chars(), PeekNext().chars()); + DataBuffer* retbuf = new DataBuffer (64); + + DataBuffer* lb = NULL; + + ScriptVar* var = FindGlobalVariable (token); + if (var) { + MustNext (); + if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction + ParserError ("can't alter variables at top level"); + reqtype = TYPE_INT; + } else + lb = ParseExprValue (reqtype); + printf ("done\n"); + + // Get an operator + printf ("parse operator at token %s\n", token.chars()); + int oper = ParseOperator (); + printf ("got %d\n", oper); + // No operator found - stop here. + if (oper == -1) { + retbuf->Merge (lb); + printf ("expression complete without operator, stopping at `%s`\n", token.chars()); + return retbuf; + } + + // Parse the right operand, + printf ("parse right operand\n"); + MustNext (); + DataBuffer* rb = ParseExprValue (reqtype); + printf ("done\n"); + + retbuf->Merge (lb); + retbuf->Merge (rb); + + long dh = DataHeaderByOperator (var, oper); + retbuf->Write<byte> (dh); + + if (IsAssignmentOperator (oper)) + retbuf->Write<byte> (var->index); + + printf ("expression complete\n"); + return retbuf; +} + +// ============================================================================ +// Parsess an operator from tokens and returns an identifier. +int ScriptReader::ParseOperator () { + str oper; + oper += PeekNext (); + + // Check one-char operators + int o = !oper.compare ("=") ? OPER_ASSIGN : + !oper.compare ("+") ? OPER_ADD : + !oper.compare ("-") ? OPER_SUBTRACT : + !oper.compare ("*") ? OPER_MULTIPLY : + !oper.compare ("/") ? OPER_DIVIDE : + !oper.compare ("%") ? OPER_MODULUS : + -1; + + if (o != -1) { + MustNext (); + return o; + } + + // Two-char operators + MustNext (); + oper += PeekNext (1); + + o = !oper.compare ("+=") ? OPER_ASSIGNADD : + !oper.compare ("-=") ? OPER_ASSIGNSUB : + !oper.compare ("*=") ? OPER_ASSIGNMUL : + !oper.compare ("/=") ? OPER_ASSIGNDIV : + !oper.compare ("%=") ? OPER_ASSIGNMOD : + -1; + + if (o != -1) { + MustNext (); + MustNext (); + } + + return o; +} + +// ============================================================================ +// Parses a value in the expression and returns the data needed to push +// it, contained in a data buffer. A value can be either a variable, a command, +// a literal or an expression. +DataBuffer* ScriptReader::ParseExprValue (int reqtype) { + printf ("parse expr value `%s` with requirement type %d\n", token.chars(), reqtype); + DataBuffer* b = new DataBuffer(16); + + ScriptVar* g; + + if (!token.compare ("(")) { + printf ("value is an expression\n"); + // Expression + MustNext (); + DataBuffer* c = ParseExpression (reqtype); + b->Merge (c); + MustNext (")"); + } else if (CommandDef* comm = FindCommand (token)) { + printf ("value is a command\n"); + delete b; + + // Command + if (reqtype && comm->returnvalue != reqtype) + ParserError ("%s returns an incompatible data type", comm->name.chars()); + return ParseCommand (comm); + } else if ((g = FindGlobalVariable (token)) && reqtype != TYPE_STRING) { + printf ("value is a global var\n"); + // Global variable + b->Write<byte> (DH_PUSHGLOBALVAR); + b->Write<byte> (g->index); + } else { + printf ("value is a literal\n"); + // If nothing else, check for literal + switch (reqtype) { + case TYPE_VOID: + ParserError ("bad syntax"); + break; + case TYPE_INT: { + if (!token.isnumber ()) + ParserError ("expected an integer, got `%s`", token.chars()); + + // All values are written unsigned - thus we need to write the value's + // absolute value, followed by an unary minus if it was negative. + b->Write<byte> (DH_PUSHNUMBER); + long v = atoi (token.chars ()); + b->Write<byte> (static_cast<byte> (abs (v))); + if (v < 0) + b->Write<byte> (DH_UNARYMINUS); + break; + } + case TYPE_STRING: + // PushToStringTable either returns the string index of the + // string if it finds it in the table, or writes it to the + // table and returns it index if it doesn't find it there. + printf ("value is a string literal\n"); + MustString (true); + b->Write<byte> (DH_PUSHSTRINGINDEX); + b->Write<byte> (PushToStringTable (token.chars())); + break; + } + } + + return b; } \ No newline at end of file
--- a/scriptreader.cxx Tue Jul 17 20:35:43 2012 +0300 +++ b/scriptreader.cxx Sat Jul 28 17:41:24 2012 +0300 @@ -47,6 +47,8 @@ ScriptReader::ScriptReader (str path) { token = ""; + prevtoken = ""; + prevpos = 0; fc = -1; for (unsigned int u = 0; u < MAX_FILESTACK; u++) @@ -70,8 +72,7 @@ // Opens a file and pushes its pointer to stack void ScriptReader::OpenFile (str path) { if (fc+1 >= MAX_FILESTACK) - ParserError ("supposed to open file `%s` but file stack is full! \ - do you have recursive `#include` directives?", + ParserError ("supposed to open file `%s` but file stack is full! do you have recursive `#include` directives?", path.chars()); // Save the position first. @@ -157,10 +158,10 @@ return c[0]; } -// true if was found, false if not. +// Read a token from the file buffer. Returns true if token was found, false if not. bool ScriptReader::Next (bool peek) { + prevpos = ftell (fp[fc]); str tmp = ""; - // printf ("begin token\n"); while (1) { // Check end-of-file @@ -190,7 +191,6 @@ } c = ReadChar (); - // printf ("add char [%d] `%c`\n", c, c); // If this is a comment we're reading, check if this character // gets the comment terminated, otherwise ignore it. @@ -236,32 +236,46 @@ } // If we got nothing here, read failed. This should - // only hapen in the case of EOF. + // only happen in the case of EOF. if (!tmp.len()) { token = ""; return false; } pos[fc]++; + prevtoken = token; token = tmp; return true; } +void ScriptReader::Prev () { + if (!prevpos) + error ("ScriptReader::Prev: cannot go back twice!\n"); + + fseek (fp[fc], prevpos, SEEK_SET); + prevpos = 0; + token = prevtoken; +} + // Returns the next token without advancing the cursor. -str ScriptReader::PeekNext () { - // Store current position +str ScriptReader::PeekNext (int offset) { + // Store current information + str storedtoken = token; int cpos = ftell (fp[fc]); // Advance on the token. - if (!Next (true)) - return ""; + while (offset >= 0) { + if (!Next (true)) + return ""; + offset--; + } str tmp = token; // Restore position fseek (fp[fc], cpos, SEEK_SET); pos[fc]--; - + token = storedtoken; return tmp; } @@ -290,9 +304,13 @@ ParserError ("expected a token, reached end of file instead\n"); } - if (strlen (c) && token.compare (c) != 0) { + if (strlen (c)) + MustThis (c); +} + +void ScriptReader::MustThis (const char* c) { + if (token.compare (c) != 0) ParserError ("expected `%s`, got `%s` instead", c, token.chars()); - } } void ScriptReader::ParserError (const char* message, ...) { @@ -314,8 +332,12 @@ fprintf (stderr, "%s%s\n", header, message); } -void ScriptReader::MustString () { - MustNext ("\""); +// if gotquote == 1, the current token already holds the quotation mark. +void ScriptReader::MustString (bool gotquote) { + if (gotquote) + MustThis ("\""); + else + MustNext ("\""); str string; // Keep reading characters until we find a terminating quote.
--- a/scriptreader.h Tue Jul 17 20:35:43 2012 +0300 +++ b/scriptreader.h Sat Jul 28 17:41:24 2012 +0300 @@ -62,6 +62,8 @@ long savedpos[MAX_FILESTACK]; // filepointer cursor position str token; int commentmode; + long prevpos; + str prevtoken; // ==================================================================== // METHODS @@ -73,10 +75,12 @@ char ReadChar (); char PeekChar (int offset = 0); bool Next (bool peek = false); - str PeekNext (); + void Prev (); + str PeekNext (int offset = 0); void Seek (unsigned int n, int origin); void MustNext (const char* c = ""); - void MustString (); + void MustThis (const char* c); + void MustString (bool gotquote = false); void MustNumber (); void MustBool (); void MustValue (int type); @@ -89,7 +93,10 @@ // parser.cxx: void BeginParse (ObjWriter* w); - void ParseCommand (CommandDef* comm, ObjWriter* w); + DataBuffer* ParseCommand (CommandDef* comm); + DataBuffer* ParseExpression (int reqtype); + int ParseOperator (); + DataBuffer* ParseExprValue (int reqtype); // preprocessor.cxx: void PreprocessDirectives (); @@ -106,4 +113,26 @@ str PPReadWord (char &term); }; +enum { + TYPE_VOID = 0, + TYPE_INT, + TYPE_STRING, + TYPE_FLOAT +}; + +// Operators +enum { + OPER_ADD, + OPER_SUBTRACT, + OPER_MULTIPLY, + OPER_DIVIDE, + OPER_MODULUS, + OPER_ASSIGN, + OPER_ASSIGNADD, + OPER_ASSIGNSUB, + OPER_ASSIGNMUL, + OPER_ASSIGNDIV, + OPER_ASSIGNMOD +}; + #endif // __SCRIPTREADER_H__ \ No newline at end of file
--- a/str.cxx Tue Jul 17 20:35:43 2012 +0300 +++ b/str.cxx Sat Jul 28 17:41:24 2012 +0300 @@ -45,7 +45,8 @@ #include "str.h" #include "common.h" -#define ITERATE_STRING(u) for (unsigned int u = 0; u < strlen (text); u++) +#define ITERATE_STRING(u) \ + for (unsigned int u = 0; u < strlen (text); u++) // ============================================================================ // vdynformat: Try to write to a formatted string with size bytes first, if