Fri, 10 Jan 2014 16:11:49 +0200
- moved sources to src/, migrated to cmake
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMakeLists.txt Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,12 @@ +add_executable (botc + src/commands.cxx + src/events.cxx + src/main.cxx + src/objwriter.cxx + src/parser.cxx + src/preprocessor.cxx + src/scriptreader.cxx + src/str.cxx + src/stringtable.cxx + src/variables.cxx +) \ No newline at end of file
--- a/Makefile Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -all: - g++ -Wall -c -o scriptreader.o scriptreader.cxx - g++ -Wall -c -o objwriter.o objwriter.cxx - g++ -Wall -c -o str.o str.cxx - g++ -Wall -c -o main.o main.cxx - g++ -Wall -c -o parser.o parser.cxx - g++ -Wall -c -o events.o events.cxx - g++ -Wall -c -o commands.o commands.cxx - g++ -Wall -c -o stringtable.o stringtable.cxx - g++ -Wall -c -o variables.o variables.cxx - g++ -Wall -c -o preprocessor.o preprocessor.cxx - g++ -Wall -o botc scriptreader.o objwriter.o str.o main.o parser.o events.o \ - commands.o stringtable.o variables.o preprocessor.o - -clean: - rm -f *.o *~ botc
--- a/array.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,190 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include "common.h" - -#define ITERATE_SUBSCRIPTS(link) \ - for (link = data; link; link = link->next) - -#define foreach_counter(NAME) \ - (int)(reinterpret_cast<int> (foreach_counter##NAME) / sizeof (foreach_counter##NAME)) -#define foreach(T,NAME,ARRAY) \ - if (ARRAY.size() > 0) for (T NAME = ARRAY[0], *foreach_counter##NAME = 0; \ - foreach_counter(NAME) < (int)(ARRAY.size()); \ - NAME = ARRAY[reinterpret_cast<int> (++foreach_counter##NAME) / sizeof (foreach_counter##NAME)]) - -// Single element of an array -template <class T> class arrayElement { -public: - T value; - arrayElement<T>* next; - - arrayElement () { - next = NULL; - } -}; - -// Dynamic array -template <class T> class array { -public: - array () { - data = NULL; - } - - array (T* stuff, unsigned int c) { - printf ("%d elements\n", c); - data = NULL; - - for (unsigned int i = 0; i < c; i++) - push (stuff[c]); - } - - ~array () { - if (data) - deleteElement (data); - } - - void push (T stuff) { - arrayElement<T>* e = new arrayElement<T>; - e->value = stuff; - e->next = NULL; - - if (!data) { - data = e; - return; - } - - arrayElement<T>* link; - for (link = data; link && link->next; link = link->next); - link->next = e; - } - - T pop () { - int pos = size() - 1; - if (pos == -1) - error ("array::pop: tried to pop an array with no elements\n"); - T res = subscript (pos); - remove (pos); - return res; - } - - void remove (unsigned int pos) { - if (!data) - error ("tried to use remove on an array with no elements"); - - if (pos == 0) { - // special case for first element - arrayElement<T>* first = data; - data = data->next; - delete first; - return; - } - - arrayElement<T>* link = data; - unsigned int x = 0; - while (link->next) { - if (x == pos - 1) - break; - link = link->next; - x++; - } - if (!link) - error ("no such element in array\n"); - - arrayElement<T>* nextlink = link->next->next; - delete link->next; - link->next = nextlink; - } - - unsigned int size () { - unsigned int x = 0; - arrayElement<T>* link; - ITERATE_SUBSCRIPTS(link) - x++; - return x; - } - - T& subscript (unsigned int i) { - arrayElement<T>* link; - unsigned int x = 0; - ITERATE_SUBSCRIPTS(link) { - if (x == i) - return link->value; - x++; - } - - error ("array: tried to access subscript %u in an array of %u elements\n", i, x); - return data->value; - } - - T* out () { - int s = size(); - T* out = new T[s]; - unsigned int x = 0; - - arrayElement<T>* link; - ITERATE_SUBSCRIPTS(link) { - out[x] = link->value; - x++; - } - - return out; - } - - T& operator [] (const unsigned int i) { - return subscript (i); - } - - void operator << (const T stuff) { - push (stuff); - } - -private: - void deleteElement (arrayElement<T>* e) { - if (e->next) - deleteElement (e->next); - delete e; - } - - arrayElement<T>* data; -};
--- a/botcommands.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,173 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Skulltag Source -// Copyright (C) 2002 Brad Carney -// Copyright (C) 2007-2012 Skulltag Development Team -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// 3. Neither the name of the Skulltag Development Team nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// 4. Redistributions in any form must be accompanied by information on how to -// obtain complete source code for the software and any accompanying -// software that uses the software. The source code must either be included -// in the distribution or be available for no more than the cost of -// distribution plus a nominal fee, and must be freely redistributable -// under reasonable conditions. For an executable file, complete source -// code means the source code for all modules it contains. It does not -// include source code for modules or files that typically accompany the -// major components of the operating system on which the executable file -// runs. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Date created: 5/18/04 -// -// -// Filename: botcommands.h -// -// Description: Contains bot structures and prototypes -// [Dusk] Clipped stuff that botc doesn't need. -// -//----------------------------------------------------------------------------- - -#ifndef __BOTCOMMANDS_H__ -#define __BOTCOMMANDS_H__ - -#include "bots.h" - -//***************************************************************************** -// DEFINES - -#define SETENEMY_LASTSEEN 0 -#define SETENEMY_LASTSHOTBY 1 - -// Different results for pathing commands. -#define PATH_UNREACHABLE -1 -#define PATH_INCOMPLETE 0 -#define PATH_COMPLETE 1 -#define PATH_REACHEDGOAL 2 - -// This is the size of the return string for the bot command functions. -#define BOTCMD_RETURNSTRING_SIZE 256 - -//***************************************************************************** -typedef enum -{ - BOTCMD_CHANGESTATE, // Basic botcmd utility functions. - BOTCMD_DELAY, - BOTCMD_RAND, - BOTCMD_STRINGSAREEQUAL, - BOTCMD_LOOKFORPOWERUPS, // Search functions. - BOTCMD_LOOKFORWEAPONS, - BOTCMD_LOOKFORAMMO, - BOTCMD_LOOKFORBASEHEALTH, - BOTCMD_LOOKFORBASEARMOR, - BOTCMD_LOOKFORSUPERHEALTH, - BOTCMD_LOOKFORSUPERARMOR, /* 10 */ - BOTCMD_LOOKFORPLAYERENEMIES, - BOTCMD_GETCLOSESTPLAYERENEMY, - BOTCMD_MOVELEFT, // Movement functions. - BOTCMD_MOVERIGHT, - BOTCMD_MOVEFORWARD, - BOTCMD_MOVEBACKWARDS, - BOTCMD_STOPMOVEMENT, - BOTCMD_STOPFORWARDMOVEMENT, - BOTCMD_STOPSIDEWAYSMOVEMENT, - BOTCMD_CHECKTERRAIN, /* 20 */ - BOTCMD_PATHTOGOAL, // Pathing functions. - BOTCMD_PATHTOLASTKNOWNENEMYPOSITION, - BOTCMD_PATHTOLASTHEARDSOUND, - BOTCMD_ROAM, - BOTCMD_GETPATHINGCOSTTOITEM, - BOTCMD_GETDISTANCETOITEM, - BOTCMD_GETITEMNAME, - BOTCMD_ISITEMVISIBLE, - BOTCMD_SETGOAL, - BOTCMD_BEGINAIMINGATENEMY, /* 30 */ // Aiming functions. - BOTCMD_STOPAIMINGATENEMY, - BOTCMD_TURN, - BOTCMD_GETCURRENTANGLE, - BOTCMD_SETENEMY, // Enemy functions. - BOTCMD_CLEARENEMY, - BOTCMD_ISENEMYALIVE, - BOTCMD_ISENEMYVISIBLE, - BOTCMD_GETDISTANCETOENEMY, - BOTCMD_GETPLAYERDAMAGEDBY, - BOTCMD_GETENEMYINVULNERABILITYTICKS, /* 40 */ - BOTCMD_FIREWEAPON, // Weapon functions. - BOTCMD_BEGINFIRINGWEAPON, - BOTCMD_STOPFIRINGWEAPON, - BOTCMD_GETCURRENTWEAPON, - BOTCMD_CHANGEWEAPON, - BOTCMD_GETWEAPONFROMITEM, - BOTCMD_ISWEAPONOWNED, - BOTCMD_ISFAVORITEWEAPON, - BOTCMD_SAY, // Chat functions. - BOTCMD_SAYFROMFILE, /* 50 */ - BOTCMD_SAYFROMCHATFILE, - BOTCMD_BEGINCHATTING, - BOTCMD_STOPCHATTING, - BOTCMD_CHATSECTIONEXISTS, - BOTCMD_CHATSECTIONEXISTSINFILE, - BOTCMD_GETLASTCHATSTRING, - BOTCMD_GETLASTCHATPLAYER, - BOTCMD_GETCHATFREQUENCY, - BOTCMD_JUMP, // Jumping functions. - BOTCMD_BEGINJUMPING, /* 60 */ - BOTCMD_STOPJUMPING, - BOTCMD_TAUNT, // Other action functions. - BOTCMD_RESPAWN, - BOTCMD_TRYTOJOINGAME, - BOTCMD_ISDEAD, // Information about self functions. - BOTCMD_ISSPECTATING, - BOTCMD_GETHEALTH, - BOTCMD_GETARMOR, - BOTCMD_GETBASEHEALTH, - BOTCMD_GETBASEARMOR, /* 70 */ - BOTCMD_GETBOTSKILL, // Botskill functions. - BOTCMD_GETACCURACY, - BOTCMD_GETINTELLECT, - BOTCMD_GETANTICIPATION, - BOTCMD_GETEVADE, - BOTCMD_GETREACTIONTIME, - BOTCMD_GETPERCEPTION, - BOTCMD_SETSKILLINCREASE, // Botskill modifying functions functions. - BOTCMD_ISSKILLINCREASED, - BOTCMD_SETSKILLDECREASE, /* 80 */ - BOTCMD_ISSKILLDECREASED, - BOTCMD_GETGAMEMODE, // Other functions. - BOTCMD_GETSPREAD, - BOTCMD_GETLASTJOINEDPLAYER, - BOTCMD_GETPLAYERNAME, - BOTCMD_GETRECEIVEDMEDAL, - BOTCMD_ACS_EXECUTE, - BOTCMD_GETFAVORITEWEAPON, - BOTCMD_SAYFROMLUMP, - BOTCMD_SAYFROMCHATLUMP, /* 90 */ - BOTCMD_CHATSECTIONEXISTSINLUMP, - BOTCMD_CHATSECTIONEXISTSINCHATLUMP, - - NUM_BOTCMDS - -} BOTCMD_e; - -#endif // __BOTCOMMANDS_H__ \ No newline at end of file
--- a/bots.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,272 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Skulltag Source -// Copyright (C) 2002 Brad Carney -// Copyright (C) 2007-2012 Skulltag Development Team -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// 3. Neither the name of the Skulltag Development Team nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// 4. Redistributions in any form must be accompanied by information on how to -// obtain complete source code for the software and any accompanying -// software that uses the software. The source code must either be included -// in the distribution or be available for no more than the cost of -// distribution plus a nominal fee, and must be freely redistributable -// under reasonable conditions. For an executable file, complete source -// code means the source code for all modules it contains. It does not -// include source code for modules or files that typically accompany the -// major components of the operating system on which the executable file -// runs. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// -// -// Filename: bots.h -// -// Description: Contains bot structures and prototypes -// [Dusk] Cropped out the stuff botc doesn't need. -// -//----------------------------------------------------------------------------- - -#ifndef __BOTS_H__ -#define __BOTS_H__ - -//***************************************************************************** -// DEFINES - -// Maximum number of variables on the bot evaluation stack. -#define BOTSCRIPT_STACK_SIZE 8 - -// Maximum number of botinto structures that can be defines. -#define MAX_BOTINFO 128 - -// Maximum number of states that can appear in a script. -#define MAX_NUM_STATES 256 - -// Maximum number of bot events that can be defined. -#define MAX_NUM_EVENTS 32 - -// Maximum number of global bot events that can be defined. -#define MAX_NUM_GLOBAL_EVENTS 32 - -// Maximum number of global variables that can be defined in a script. -#define MAX_SCRIPT_VARIABLES 128 - -// Maximum number of global arrays that can be defined in a script. -#define MAX_SCRIPT_ARRAYS 16 - -// Maximum number of global arrays that can be defined in a script. -#define MAX_SCRIPTARRAY_SIZE 65536 - -// Maxmimum number of state (local) variables that can appear in a script. -#define MAX_STATE_VARIABLES 16 - -// Maximum number of strings that can appear in a script stringlist. -#define MAX_LIST_STRINGS 128 - -// Maximum length of those strings in the stringlist. -#define MAX_STRING_LENGTH 256 - -// Maximum reaction time for a bot. -#define MAX_REACTION_TIME 52 - -// Maximum number of events the bots can store up that it's waiting to react to. -#define MAX_STORED_EVENTS 64 - -//***************************************************************************** -typedef enum -{ - // Bot skill ratings. - BOTSKILL_VERYPOOR, - BOTSKILL_POOR, - BOTSKILL_LOW, - BOTSKILL_MEDIUM, - BOTSKILL_HIGH, - BOTSKILL_EXCELLENT, - BOTSKILL_SUPREME, - BOTSKILL_GODLIKE, - BOTSKILL_PERFECT, - - NUM_BOT_SKILLS - -} BOTSKILL_e; - -//***************************************************************************** -// STRUCTURES -// - -//***************************************************************************** -// These are the botscript data headers that it writes out. -typedef enum -{ - DH_COMMAND, - DH_STATEIDX, - DH_STATENAME, - DH_ONENTER, - DH_MAINLOOP, - DH_ONEXIT, - DH_EVENT, - DH_ENDONENTER, - DH_ENDMAINLOOP, - DH_ENDONEXIT, - DH_ENDEVENT, - DH_IFGOTO, - DH_IFNOTGOTO, - DH_GOTO, - DH_ORLOGICAL, - DH_ANDLOGICAL, - DH_ORBITWISE, - DH_EORBITWISE, - DH_ANDBITWISE, - DH_EQUALS, - DH_NOTEQUALS, - DH_LESSTHAN, - DH_LESSTHANEQUALS, - DH_GREATERTHAN, - DH_GREATERTHANEQUALS, - DH_NEGATELOGICAL, - DH_LSHIFT, - DH_RSHIFT, - DH_ADD, - DH_SUBTRACT, - DH_UNARYMINUS, - DH_MULTIPLY, - DH_DIVIDE, - DH_MODULUS, - DH_PUSHNUMBER, - DH_PUSHSTRINGINDEX, - DH_PUSHGLOBALVAR, - DH_PUSHLOCALVAR, - DH_DROPSTACKPOSITION, - DH_SCRIPTVARLIST, - DH_STRINGLIST, - DH_INCGLOBALVAR, - DH_DECGLOBALVAR, - DH_ASSIGNGLOBALVAR, - DH_ADDGLOBALVAR, - DH_SUBGLOBALVAR, - DH_MULGLOBALVAR, - DH_DIVGLOBALVAR, - DH_MODGLOBALVAR, - DH_INCLOCALVAR, - DH_DECLOCALVAR, - DH_ASSIGNLOCALVAR, - DH_ADDLOCALVAR, - DH_SUBLOCALVAR, - DH_MULLOCALVAR, - DH_DIVLOCALVAR, - DH_MODLOCALVAR, - DH_CASEGOTO, - DH_DROP, - DH_INCGLOBALARRAY, - DH_DECGLOBALARRAY, - DH_ASSIGNGLOBALARRAY, - DH_ADDGLOBALARRAY, - DH_SUBGLOBALARRAY, - DH_MULGLOBALARRAY, - DH_DIVGLOBALARRAY, - DH_MODGLOBALARRAY, - DH_PUSHGLOBALARRAY, - DH_SWAP, - DH_DUP, - DH_ARRAYSET, - - NUM_DATAHEADERS - -} DATAHEADERS_e; - -//***************************************************************************** -// These are the different bot events that can be posted to a bot's state. -typedef enum -{ - BOTEVENT_KILLED_BYENEMY, - BOTEVENT_KILLED_BYPLAYER, - BOTEVENT_KILLED_BYSELF, - BOTEVENT_KILLED_BYENVIORNMENT, - BOTEVENT_REACHED_GOAL, - BOTEVENT_GOAL_REMOVED, - BOTEVENT_DAMAGEDBY_PLAYER, - BOTEVENT_PLAYER_SAY, - BOTEVENT_ENEMY_KILLED, - BOTEVENT_RESPAWNED, - BOTEVENT_INTERMISSION, - BOTEVENT_NEWMAP, - BOTEVENT_ENEMY_USEDFIST, - BOTEVENT_ENEMY_USEDCHAINSAW, - BOTEVENT_ENEMY_FIREDPISTOL, - BOTEVENT_ENEMY_FIREDSHOTGUN, - BOTEVENT_ENEMY_FIREDSSG, - BOTEVENT_ENEMY_FIREDCHAINGUN, - BOTEVENT_ENEMY_FIREDMINIGUN, - BOTEVENT_ENEMY_FIREDROCKET, - BOTEVENT_ENEMY_FIREDGRENADE, - BOTEVENT_ENEMY_FIREDRAILGUN, - BOTEVENT_ENEMY_FIREDPLASMA, - BOTEVENT_ENEMY_FIREDBFG, - BOTEVENT_ENEMY_FIREDBFG10K, - BOTEVENT_PLAYER_USEDFIST, - BOTEVENT_PLAYER_USEDCHAINSAW, - BOTEVENT_PLAYER_FIREDPISTOL, - BOTEVENT_PLAYER_FIREDSHOTGUN, - BOTEVENT_PLAYER_FIREDSSG, - BOTEVENT_PLAYER_FIREDCHAINGUN, - BOTEVENT_PLAYER_FIREDMINIGUN, - BOTEVENT_PLAYER_FIREDROCKET, - BOTEVENT_PLAYER_FIREDGRENADE, - BOTEVENT_PLAYER_FIREDRAILGUN, - BOTEVENT_PLAYER_FIREDPLASMA, - BOTEVENT_PLAYER_FIREDBFG, - BOTEVENT_PLAYER_FIREDBFG10K, - BOTEVENT_USEDFIST, - BOTEVENT_USEDCHAINSAW, - BOTEVENT_FIREDPISTOL, - BOTEVENT_FIREDSHOTGUN, - BOTEVENT_FIREDSSG, - BOTEVENT_FIREDCHAINGUN, - BOTEVENT_FIREDMINIGUN, - BOTEVENT_FIREDROCKET, - BOTEVENT_FIREDGRENADE, - BOTEVENT_FIREDRAILGUN, - BOTEVENT_FIREDPLASMA, - BOTEVENT_FIREDBFG, - BOTEVENT_FIREDBFG10K, - BOTEVENT_PLAYER_JOINEDGAME, - BOTEVENT_JOINEDGAME, - BOTEVENT_DUEL_STARTINGCOUNTDOWN, - BOTEVENT_DUEL_FIGHT, - BOTEVENT_DUEL_WINSEQUENCE, - BOTEVENT_SPECTATING, - BOTEVENT_LMS_STARTINGCOUNTDOWN, - BOTEVENT_LMS_FIGHT, - BOTEVENT_LMS_WINSEQUENCE, - BOTEVENT_WEAPONCHANGE, - BOTEVENT_ENEMY_BFGEXPLODE, - BOTEVENT_PLAYER_BFGEXPLODE, - BOTEVENT_BFGEXPLODE, - BOTEVENT_RECEIVEDMEDAL, - - NUM_BOTEVENTS - -} BOTEVENT_e; - -#endif // __BOTS_H__
--- a/commands.cxx Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,217 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#define __COMMANDS_CXX__ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include "common.h" -#include "scriptreader.h" -#include "str.h" -#include "commands.h" - -// ============================================================================ -// Reads command definitions from commands.def and stores them to memory. -void ReadCommands () { - ScriptReader* r = new ScriptReader ("commands.def"); - g_CommDef = NULL; - CommandDef* curdef = g_CommDef; - unsigned int numCommDefs = 0; - - while (r->PeekNext().len()) { - CommandDef* comm = new CommandDef; - comm->next = NULL; - - // Number - r->MustNumber (); - comm->number = r->token; - - r->MustNext (":"); - - // Name - r->MustNext (); - comm->name = r->token; - if (IsKeyword (comm->name)) - r->ParserError ("command name `%s` conflicts with keyword", comm->name.chars()); - - r->MustNext (":"); - - // Return value - r->MustNext (); - comm->returnvalue = GetTypeByName (r->token); - if (comm->returnvalue == -1) - r->ParserError ("bad return value type `%s` for command %s", r->token.chars(), comm->name.chars()); - - r->MustNext (":"); - - // Num args - r->MustNumber (); - comm->numargs = r->token; - - r->MustNext (":"); - - // Max args - r->MustNumber (); - comm->maxargs = r->token; - - if (comm->maxargs > MAX_MAXARGS) - r->ParserError ("maxargs (%d) greater than %d!", comm->maxargs, MAX_MAXARGS); - - // Argument types - int curarg = 0; - while (curarg < comm->maxargs) { - r->MustNext (":"); - r->MustNext (); - - type_e type = GetTypeByName (r->token); - if (type == -1) - r->ParserError ("bad argument %d type `%s`", curarg, r->token.chars()); - if (type == TYPE_VOID) - r->ParserError ("void is not a valid argument type!"); - comm->argtypes[curarg] = type; - - r->MustNext ("("); - r->MustNext (); - - // - 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); - - strncpy (comm->argnames[curarg], r->token.chars(), MAX_ARGNAMELEN); - comm->argnames[curarg][MAX_ARGNAMELEN-1] = 0; - - // If this is an optional parameter, we need the default value. - if (curarg >= comm->numargs) { - r->MustNext ("="); - switch (type) { - case TYPE_INT: - case TYPE_BOOL: - r->MustNumber(); - break; - case TYPE_STRING: - r->MustString(); - break; - case TYPE_UNKNOWN: - case TYPE_VOID: - break; - } - - comm->defvals[curarg] = r->token; - } - - r->MustNext (")"); - curarg++; - } - - if (!g_CommDef) - g_CommDef = comm; - - if (!curdef) { - curdef = comm; - } else { - curdef->next = comm; - curdef = comm; - } - numCommDefs++; - } - - if (!numCommDefs) - r->ParserError ("no commands defined!\n"); - - r->CloseFile (); - delete r; - printf ("%d command definitions read.\n", numCommDefs); -} - -// ============================================================================ -// Finds a command by name -CommandDef* FindCommand (str fname) { - CommandDef* comm; - ITERATE_COMMANDS (comm) { - if (!fname.icompare (comm->name)) - return comm; - } - - return NULL; -} - -// ============================================================================ -// Returns the prototype of the command -str GetCommandPrototype (CommandDef* comm) { - str text; - text += GetTypeName (comm->returnvalue); - text += ' '; - text += comm->name; - text += '('; - - bool hasOptionalArguments = false; - for (int i = 0; i < comm->maxargs; i++) { - if (i == comm->numargs) { - hasOptionalArguments = true; - text += '['; - } - - if (i) - text += ", "; - - text += GetTypeName (comm->argtypes[i]); - text += ' '; - text += comm->argnames[i]; - - if (i >= comm->numargs) { - text += '='; - - bool isString = comm->argtypes[i] == TYPE_STRING; - if (isString) text += '"'; - - char defvalstring[8]; - sprintf (defvalstring, "%d", comm->defvals[i]); - text += defvalstring; - - if (isString) text += '"'; - } - } - - if (hasOptionalArguments) - text += ']'; - text += ')'; - return text; -} \ No newline at end of file
--- a/commands.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef __COMMANDS_H__ -#define __COMMANDS_H__ - -#define MAX_MAXARGS 8 -#define MAX_ARGNAMELEN 16 - -#include "common.h" -#include "str.h" -#include "botcommands.h" - -#define ITERATE_COMMANDS(comm) \ - for (comm = g_CommDef; comm->next != NULL; comm = comm->next) - -struct CommandDef { - str name; - int number; - int numargs; - int maxargs; - type_e returnvalue; - type_e argtypes[MAX_MAXARGS]; - char argnames[MAX_MAXARGS][MAX_ARGNAMELEN]; - int defvals[MAX_MAXARGS]; - CommandDef* next; -}; - -void ReadCommands (); -CommandDef* FindCommand (str a); -str GetCommandPrototype (CommandDef* comm); - -#ifndef __COMMANDS_CXX__ -extern -#endif -CommandDef* g_CommDef; - -#endif // __COMMANDS_H__ \ No newline at end of file
--- a/common.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,186 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef __COMMON_H__ -#define __COMMON_H__ - -#include <stdio.h> -#include <stdarg.h> -#include <stdint.h> -#include "bots.h" -#include "str.h" - -// Application name and version -#define APPNAME "botc" -#define VERSION_MAJOR 0 -#define VERSION_MINOR 999 - -// Use a macro for Write so we can get the function name -// This can be pretty crucial in debugging. -#ifdef __GNUC__ -#define Write(STUFF) DoWrite (__PRETTY_FUNCTION__, STUFF) -#else -#define Write(STUFF) DoWrite (__func__, STUFF) -#endif - -// On Windows, files are case-insensitive -#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__) - #define FILE_CASEINSENSITIVE -#endif - -// Parser mode: where is the parser at? -enum parsermode_e { - MODE_TOPLEVEL, // at top level - MODE_EVENT, // inside event definition - MODE_MAINLOOP, // inside mainloop - MODE_ONENTER, // inside onenter - MODE_ONEXIT, // inside onexit -}; - -enum type_e { - TYPE_UNKNOWN = 0, - TYPE_VOID, - TYPE_INT, - TYPE_STRING, - TYPE_BOOL, -}; - -#define CHECK_FILE(pointer,path,action) \ - if (!pointer) { \ - error ("couldn't open %s for %s!\n", (char*)path, action); \ - 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" : "" - -// Shortcut for zeroing something -#define ZERO(obj) memset (&obj, 0, sizeof (obj)); - -void error (const char* text, ...); -char* ObjectFileName (str s); -bool fexists (char* path); -type_e GetTypeByName (str token); -str GetTypeName (type_e type); - -// Make the parser's variables globally available -extern int g_NumStates; -extern int g_NumEvents; -extern parsermode_e g_CurMode; -extern str g_CurState; - -#define neurosphere if (g_Neurosphere) -#define twice for (int repeat_token = 0; repeat_token < 2; repeat_token++) - -#ifndef __GNUC__ -#define __attribute__(X) -#endif -#define deprecated __attribute__ ((deprecated)) - -// Power function -template<class T> T pow (T a, unsigned int b) { - if (!b) - return 1; - - T r = a; - while (b > 1) { - b--; - r = r * a; - } - - return r; -} - -// Whitespace check -inline bool IsCharWhitespace (char c) { - return (c <= 32 || c == 127 || c == 255); -} - -// Byte datatype -typedef int32_t word; -typedef unsigned char byte; - -// Keywords -#ifndef __MAIN_CXX__ -extern const char** g_Keywords; -#endif - -bool IsKeyword (str s); -unsigned int NumKeywords (); - -// Script mark and reference -struct ScriptMark { - str name; - size_t pos; -}; - -struct ScriptMarkReference { - unsigned int num; - size_t pos; -}; - -// ==================================================================== -// Generic union -template <class T> union union_t { - T val; - byte b[sizeof (T)]; - char c[sizeof (T)]; - double d; - float f; - int i; - word w; -}; - -// ==================================================================== -// Finds a byte in the given value. -template <class T> inline unsigned char GetByteIndex (T a, unsigned int b) { - union_t<T> uni; - uni.val = a; - return uni.b[b]; -} - -#endif // __COMMON_H__
--- a/databuffer.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,303 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __DATABUFFER_H__ -#define __DATABUFFER_H__ -#include <stdio.h> -#include <string.h> -#include "common.h" -#include "stringtable.h" - -#define MAX_MARKS 512 - -extern int g_NextMark; - -// ============================================================================ -// DataBuffer: A dynamic data buffer. -class DataBuffer { -public: - // The actual buffer - byte* buffer; - - // Allocated size of the buffer - unsigned int allocsize; - - // 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 DoWrite (const char* func, T stuff) { - // printf ("DoWrite: called from %s\n", func); - if (writesize + sizeof (T) >= allocsize) { - // We don't have enough space in the buffer to write - // the stuff - thus resize. First, store the old - // buffer temporarily: - char* copy = new char[allocsize]; - memcpy (copy, buffer, allocsize); - - // Remake the buffer with the new size. Have enough space - // for the stuff we're going to write, as well as a bit - // of leeway so we don't have to resize immediately again. - size_t newsize = allocsize + sizeof (T) + 128; - - delete buffer; - buffer = new unsigned char[newsize]; - allocsize = newsize; - - // Now, copy the stuff back. - memcpy (buffer, copy, allocsize); - delete copy; - } - - // Buffer is now guaranteed to have enough space. - // Write the stuff one byte at a time. - union_t<T> uni; - uni.val = stuff; - for (unsigned int x = 0; x < sizeof (T); x++) { - if (writesize >= allocsize) // should NEVER happen because resizing is done above - error ("DataBuffer: written size exceeds allocated size!\n"); - - buffer[writesize] = uni.b[x]; - writesize++; - } - } - - // ==================================================================== - // Merge another data buffer into this one. - void Merge (DataBuffer* other) { - if (!other) - return; - int oldsize = writesize; - - for (unsigned int x = 0; x < other->writesize; x++) - Write (*(other->buffer+x)); - - // Merge its marks and references - unsigned int u = 0; - for (u = 0; u < MAX_MARKS; u++) { - if (other->marks[u]) { - // Merge the mark and offset its position. - 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 - // TODO: add a g_NextRef system like here, akin to marks! - unsigned int r = AddMarkReference (other->refs[u]->num, false); - refs[r]->pos = other->refs[u]->pos + oldsize; - } - } - - delete other; - } - - // Clones this databuffer to a new one and returns it. - DataBuffer* Clone () { - DataBuffer* other = new DataBuffer; - for (unsigned int x = 0; x < writesize; x++) - other->Write (*(buffer+x)); - return other; - } - - // ==================================================================== - // Adds a mark to the buffer. A mark is a "pointer" 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 (str name) { - // Find a free slot for the mark - 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"); - - ScriptMark* m = new ScriptMark; - m->name = name; - m->pos = writesize; - marks[u] = m; - return u; - } - - // ==================================================================== - // 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, bool placeholder = true) { - unsigned int u; - for (u = 0; u < MAX_MARKS; u++) - if (!refs[u]) - break; - - if (u == MAX_MARKS) - error ("mark reference quota exceeded, all goto-statements, if-structs and loops add refs\n"); - - ScriptMarkReference* r = new ScriptMarkReference; - r->num = marknum; - r->pos = writesize; - refs[u] = r; - - // Write a dummy placeholder for the reference - if (placeholder) - Write (1234); - - return u; - } - - // Delete a mark and all references to it. - void DeleteMark (unsigned int marknum) { - if (!marks[marknum]) - return; - - // 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; - } - } - } - - // Adjusts a mark to the current position - void MoveMark (unsigned int mark, int offset = -1) { - if (!marks[mark]) - return; - marks[mark]->pos = writesize; - } - - void OffsetMark (unsigned int mark, size_t offset) { - if (!marks[mark]) - return; - marks[mark]->pos += offset; - } - - // Dump the buffer (for debugging purposes) - void Dump() { - for (unsigned int x = 0; x < writesize; x++) - printf ("%d. [%d]\n", x, *(buffer+x)); - } - - // Count the amount of marks - unsigned int CountMarks () { - unsigned int count = 0; - for (unsigned int u = 0; u < MAX_MARKS; u++) - count += !!marks[u]; - return count; - } - - // Count the amount of refs - unsigned int CountReferences () { - unsigned int count = 0; - for (unsigned int u = 0; u < MAX_MARKS; u++) - count += !!refs[u]; - return count; - } - - // Write a float into the buffer - void WriteFloat (str floatstring) { - // TODO: Casting float to word causes the decimal to be lost. - // Find a way to store the number without such loss. - float val = atof (floatstring); - Write (DH_PUSHNUMBER); - Write (static_cast<word> ((val > 0) ? val : -val)); - if (val < 0) - Write (DH_UNARYMINUS); - } - - void WriteString (str string) { - Write (DH_PUSHSTRINGINDEX); - Write (PushToStringTable (string)); - } -}; - -#endif // __DATABUFFER_H__ \ No newline at end of file
--- a/events.cxx Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,112 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#define __EVENTS_CXX__ -#include <stdlib.h> -#include <stdio.h> -#include "common.h" -#include "scriptreader.h" -#include "str.h" -#include "events.h" - -EventDef* g_EventDef; - -// ============================================================================ -// Read event definitions from file -void ReadEvents () { - ScriptReader* r = new ScriptReader ("events.def"); - g_EventDef = NULL; - EventDef* curdef = g_EventDef; - unsigned int numEventDefs = 0; - while (r->Next()) { - EventDef* e = new EventDef; - e->name = r->token; - e->number = numEventDefs; - e->next = NULL; - - // g_EventDef becomes the first eventdef - if (!g_EventDef) - g_EventDef = e; - - if (!curdef) { - curdef = e; - } else { - curdef->next = e; - curdef = e; - } - numEventDefs++; - } - - delete r; - printf ("%d event definitions read.\n", numEventDefs); -} - -// ============================================================================ -// Delete event definitions recursively -void UnlinkEvents (EventDef* e) { - if (e->next) - UnlinkEvents (e->next); - delete e; -} - -// ============================================================================ -// Finds an event definition by index -EventDef* FindEventByIdx (unsigned int idx) { - EventDef* e = g_EventDef; - while (idx > 0) { - if (!e->next) - return NULL; - e = e->next; - idx--; - } - return e; -} - -// ============================================================================ -// Finds an event definition by name -EventDef* FindEventByName (str a) { - EventDef* e; - for (e = g_EventDef; e->next != NULL; e = e->next) { - if (!a.icompare (e->name)) - return e; - } - - return NULL; -} \ No newline at end of file
--- a/events.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __EVENT_H__ -#define __EVENT_H__ - -#include "str.h" - -struct EventDef { - str name; - int number; - EventDef* next; -}; - -void ReadEvents (); -void UnlinkEvents (EventDef* e); -EventDef* FindEventByIdx (unsigned int idx); -EventDef* FindEventByName (str a); - -#endif // __EVENT_H__ \ No newline at end of file
--- a/main.cxx Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,273 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#define __MAIN_CXX__ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "common.h" - -#include "str.h" -#include "scriptreader.h" -#include "objwriter.h" -#include "events.h" -#include "commands.h" -#include "stringtable.h" -#include "variables.h" -#include "array.h" -#include "databuffer.h" - -#include "bots.h" -#include "botcommands.h" - -// List of keywords -const char* g_Keywords[] = { - "bool", - "break", - "case", - "continue", - "const", - "default", - "do", - "else", - "event", - "for", - "goto", - "if", - "int", - "mainloop", - "onenter", - "onexit", - "state", - "switch", - "str" - "void", - "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 - "enum", // Would enum actually be useful? I think so. - "func", // Would function support need external support from zandronum? - "return", -}; - -// databuffer global variable -int g_NextMark = 0; - -int main (int argc, char** argv) { - // Intepret command-line parameters: - // -l: list commands - // I guess there should be a better way to do this. - if (argc == 2 && !strcmp (argv[1], "-l")) { - ReadCommands (); - printf ("Begin list of commands:\n"); - printf ("------------------------------------------------------\n"); - - CommandDef* comm; - ITERATE_COMMANDS (comm) - printf ("%s\n", GetCommandPrototype (comm).chars()); - - printf ("------------------------------------------------------\n"); - printf ("End of command list\n"); - exit (0); - } - - // Print header - str header; - str headerline = "-="; - header.appendformat ("%s version %d.%d", APPNAME, VERSION_MAJOR, VERSION_MINOR); - - headerline *= (header.len() / 2) - 1; - headerline += '-'; - printf ("%s\n%s\n", header.chars(), headerline.chars()); - - if (argc < 2) { - fprintf (stderr, "usage: %s <infile> [outfile] # compiles botscript\n", argv[0]); - fprintf (stderr, " %s -l # lists commands\n", argv[0]); - exit (1); - } - - // A word should always be exactly 4 bytes. The above list command - // doesn't need it, but the rest of the program does. - if (sizeof (word) != 4) - error ("%s expects a word (uint32_t) to be 4 bytes in size, is %d\n", - APPNAME, sizeof (word)); - - str outfile; - if (argc < 3) - outfile = ObjectFileName (argv[1]); - else - outfile = argv[2]; - - // If we'd end up writing into an existing file, - // ask the user if we want to overwrite it - if (fexists (outfile)) { - // Additional warning if the paths are the same - str warning; -#ifdef FILE_CASEINSENSITIVE - if (!outfile.icompare (argv[1])) -#else - if (!outfile.compare (argv[1])) -#endif - { - warning = "\nWARNING: Output file is the same as the input file. "; - warning += "Answering yes here will destroy the source!\n"; - warning += "Continue nevertheless?"; - } - printf ("output file `%s` already exists! overwrite?%s (y/n) ", outfile.chars(), warning.chars()); - - char ans; - fgets (&ans, 2, stdin); - if (ans != 'y') { - printf ("abort\n"); - exit (1); - } - } - - // Read definitions - printf ("Reading definitions...\n"); - ReadEvents (); - ReadCommands (); - - // Init stuff - InitStringTable (); - - // Prepare reader and writer - ScriptReader* r = new ScriptReader (argv[1]); - ObjWriter* w = new ObjWriter (outfile); - - // We're set, begin parsing :) - printf ("Parsing script...\n"); - r->ParseBotScript (w); - printf ("Script parsed successfully.\n"); - - // Parse done, print statistics and write to file - unsigned int globalcount = g_GlobalVariables.size(); - unsigned int stringcount = CountStringTable (); - int NumMarks = w->MainBuffer->CountMarks (); - int NumRefs = w->MainBuffer->CountReferences (); - 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 - delete r; - delete w; - - // Done! - exit (0); -} - -// ============================================================================ -// Utility functions - -// ============================================================================ -// Does the given file exist? -bool fexists (char* path) { - if (FILE* test = fopen (path, "r")) { - fclose (test); - return true; - } - return false; -} - -// ============================================================================ -// Generic error -void error (const char* text, ...) { - PERFORM_FORMAT (text, c); - fprintf (stderr, "error: %s", c); - exit (1); -} - -// ============================================================================ -// Mutates given filename to an object filename -char* ObjectFileName (str s) { - // Locate the extension and chop it out - unsigned int extdot = s.last ("."); - if (extdot >= s.len()-4) - s -= (s.len() - extdot); - - s += ".o"; - return s.chars(); -} - -// ============================================================================ -// Is the given argument a reserved keyword? -bool IsKeyword (str s) { - for (unsigned int u = 0; u < NumKeywords (); u++) - if (!s.icompare (g_Keywords[u])) - return true; - return false; -} - -unsigned int NumKeywords () { - return sizeof (g_Keywords) / sizeof (const char*); -} - -// ============================================================================ -type_e GetTypeByName (str t) { - t = t.tolower(); - return (t == "int") ? TYPE_INT : - (t == "str") ? TYPE_STRING : - (t == "void") ? TYPE_VOID : - (t == "bool") ? TYPE_BOOL : - TYPE_UNKNOWN; -} - - -// ============================================================================ -// Inverse operation - type name by value -str GetTypeName (type_e type) { - switch (type) { - case TYPE_INT: return "int"; break; - case TYPE_STRING: return "str"; break; - case TYPE_VOID: return "void"; break; - case TYPE_BOOL: return "bool"; break; - case TYPE_UNKNOWN: return "???"; break; - } - - return ""; -} \ No newline at end of file
--- a/objwriter.cxx Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,193 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#define __OBJWRITER_CXX__ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "common.h" -#include "str.h" -#include "objwriter.h" -#include "databuffer.h" -#include "stringtable.h" - -#include "bots.h" - -extern bool g_GotMainLoop; - -ObjWriter::ObjWriter (str path) { - MainBuffer = new DataBuffer; - MainLoopBuffer = new DataBuffer; - OnEnterBuffer = new DataBuffer; - SwitchBuffer = NULL; // created on demand - numWrittenBytes = 0; - numWrittenReferences = 0; - filepath = path; -} - -void ObjWriter::WriteString (char* s) { - Write (strlen (s)); - for (unsigned int u = 0; u < strlen (s); u++) - Write ((s)[u]); -} - -void ObjWriter::WriteString (const char* s) { - WriteString (const_cast<char*> (s)); -} - -void ObjWriter::WriteString (str s) { - WriteString (s.chars()); -} - -void ObjWriter::WriteBuffer (DataBuffer* buf) { - GetCurrentBuffer()->Merge (buf); -} - -void ObjWriter::WriteBuffers () { - // If there was no mainloop defined, write a dummy one now. - if (!g_GotMainLoop) { - MainLoopBuffer->Write (DH_MAINLOOP); - MainLoopBuffer->Write (DH_ENDMAINLOOP); - } - - // Write the onenter and mainloop buffers, IN THAT ORDER - for (int i = 0; i < 2; i++) { - DataBuffer** buf = (!i) ? &OnEnterBuffer : &MainLoopBuffer; - WriteBuffer (*buf); - - // Clear the buffer afterwards for potential next state - *buf = new DataBuffer; - } - - // Next state definitely has no mainloop yet - g_GotMainLoop = false; -} - -// Write string table -void ObjWriter::WriteStringTable () { - unsigned int stringcount = CountStringTable (); - if (!stringcount) - return; - - // Write header - Write (DH_STRINGLIST); - Write (stringcount); - - // Write all strings - for (unsigned int a = 0; a < stringcount; a++) - WriteString (g_StringTable[a]); -} - -// Write main buffer to file -void ObjWriter::WriteToFile () { - fp = fopen (filepath, "w"); - CHECK_FILE (fp, filepath, "writing"); - - // First, resolve references - numWrittenReferences = 0; - for (unsigned int u = 0; u < MAX_MARKS; u++) { - ScriptMarkReference* ref = MainBuffer->refs[u]; - if (!ref) - continue; - - // 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", - u, ref->pos, ref->num, MainBuffer->marks[ref->num]->pos); - */ - numWrittenReferences++; - } - - // Then, dump the main buffer to the file - for (unsigned int x = 0; x < MainBuffer->writesize; x++) - WriteDataToFile<byte> (*(MainBuffer->buffer+x)); - - printf ("-- %u byte%s written to %s\n", numWrittenBytes, PLURAL (numWrittenBytes), filepath.chars()); - fclose (fp); -} - -DataBuffer* ObjWriter::GetCurrentBuffer() { - return SwitchBuffer ? SwitchBuffer : - (g_CurMode == MODE_MAINLOOP) ? MainLoopBuffer : - (g_CurMode == MODE_ONENTER) ? OnEnterBuffer : - MainBuffer; -} - -ScriptMark* g_ScriptMark = NULL; - -// Adds a mark -unsigned int ObjWriter::AddMark (str name) { - return GetCurrentBuffer()->AddMark (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 (str name) { - DataBuffer* b = GetCurrentBuffer(); - for (unsigned int u = 0; u < MAX_MARKS; u++) { - if (b->marks[u] && !b->marks[u]->name.icompare (name)) - return u; - } - return MAX_MARKS; -} - -// Moves a mark to the current position -void ObjWriter::MoveMark (unsigned int mark) { - GetCurrentBuffer()->MoveMark (mark); -} - -// Deletes a mark -void ObjWriter::DeleteMark (unsigned int mark) { - GetCurrentBuffer()->DeleteMark (mark); -} - -void ObjWriter::OffsetMark (unsigned int mark, int offset) { - GetCurrentBuffer()->OffsetMark (mark, offset); -} \ No newline at end of file
--- a/objwriter.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,128 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __OBJWRITER_H__ -#define __OBJWRITER_H__ - -#include <stdio.h> -#include <typeinfo> -#include <string.h> -#include "common.h" -#include "str.h" -#include "databuffer.h" - -class ObjWriter { -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; - - // ==================================================================== - // METHODS - ObjWriter (str path); - void WriteString (char* s); - void WriteString (const char* s); - void WriteString (str s); - void WriteBuffer (DataBuffer* buf); - void WriteBuffers (); - void WriteStringTable (); - void WriteToFile (); - DataBuffer* GetCurrentBuffer (); - - unsigned int AddMark (str name); - unsigned int FindMark (str name); - unsigned int AddReference (unsigned int mark); - void MoveMark (unsigned int mark); - void OffsetMark (unsigned int mark, int offset); - void DeleteMark (unsigned int mark); - template <class T> void DoWrite (const char* func, T stuff) { - GetCurrentBuffer ()->DoWrite (func, stuff); - } - - // Default to word - void DoWrite (const char* func, word stuff) { - DoWrite<word> (func, stuff); - } - - void DoWrite (const char* func, byte stuff) { - DoWrite<byte> (func, 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++) { - fwrite (&uni.b[x], 1, 1, fp); - numWrittenBytes++; - } - } -}; - -#endif // __OBJWRITER_H__ \ No newline at end of file
--- a/parser.cxx Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1203 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#define __PARSER_CXX__ - -#include <stdio.h> -#include <stdlib.h> -#include "common.h" -#include "str.h" -#include "objwriter.h" -#include "scriptreader.h" -#include "events.h" -#include "commands.h" -#include "stringtable.h" -#include "variables.h" -#include "array.h" - -#define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \ - 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()); - -#define SCOPE(n) scopestack[g_ScopeCursor - n] - -int g_NumStates = 0; -int g_NumEvents = 0; -parsermode_e g_CurMode = MODE_TOPLEVEL; -str g_CurState = ""; -bool g_stateSpawnDefined = false; -bool g_GotMainLoop = false; -unsigned int g_ScopeCursor = 0; -DataBuffer* g_IfExpression = NULL; -bool g_CanElse = false; -str* g_UndefinedLabels[MAX_MARKS]; -bool g_Neurosphere = false; // neurosphere-compat -array<constinfo_t> g_ConstInfo; - -// ============================================================================ -// 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::ParseBotScript (ObjWriter* w) { - // Zero the entire block stack first - for (int i = 0; i < MAX_SCOPE; i++) - ZERO(scopestack[i]); - - for (int i = 0; i < MAX_MARKS; i++) - g_UndefinedLabels[i] = NULL; - - while (Next()) { - // Check if else is potentically valid - if (token == "else" && !g_CanElse) - ParserError ("else without preceding if"); - if (token != "else") - g_CanElse = false; - - // ============================================================ - if (token == "state") { - MUST_TOPLEVEL - - MustString (); - - // State name must be a word. - if (token.first (" ") != token.len()) - ParserError ("state name must be a single word, got `%s`", token.chars()); - str statename = token; - - // stateSpawn is special - it *must* be defined. If we - // encountered it, then mark down that we have it. - if (-token == "statespawn") - g_stateSpawnDefined = true; - - // Must end in a colon - MustNext (":"); - - // Write the previous state's onenter and - // mainloop buffers to file now - if (g_CurState.len()) - w->WriteBuffers(); - - w->Write (DH_STATENAME); - w->WriteString (statename); - w->Write (DH_STATEIDX); - w->Write (g_NumStates); - - g_NumStates++; - g_CurState = token; - g_GotMainLoop = false; - continue; - } - - // ============================================================ - if (token == "event") { - MUST_TOPLEVEL - - // Event definition - MustString (); - - EventDef* e = FindEventByName (token); - if (!e) - ParserError ("bad event, got `%s`\n", token.chars()); - - MustNext ("{"); - - g_CurMode = MODE_EVENT; - - w->Write (DH_EVENT); - w->Write (e->number); - g_NumEvents++; - continue; - } - - // ============================================================ - if (token == "mainloop") { - MUST_TOPLEVEL - MustNext ("{"); - - // Mode must be set before dataheader is written here! - g_CurMode = MODE_MAINLOOP; - w->Write (DH_MAINLOOP); - continue; - } - - // ============================================================ - if (token == "onenter" || token == "onexit") { - MUST_TOPLEVEL - bool onenter = token == "onenter"; - MustNext ("{"); - - // Mode must be set before dataheader is written here, - // because onenter goes to a separate buffer. - g_CurMode = onenter ? MODE_ONENTER : MODE_ONEXIT; - w->Write (onenter ? DH_ONENTER : DH_ONEXIT); - continue; - } - - // ============================================================ - if (token == "int" || token == "str" || token == "bool") { - // For now, only globals are supported - if (g_CurMode != MODE_TOPLEVEL || g_CurState.len()) - ParserError ("variables must only be global for now"); - - type_e type = (token == "int") ? TYPE_INT : - (token == "str") ? TYPE_STRING : - TYPE_BOOL; - - MustNext (); - - // Var name must not be a number - if (token.isnumber()) - ParserError ("variable name must not be a number"); - - str varname = token; - ScriptVar* var = DeclareGlobalVariable (this, type, varname); - - if (!var) - ParserError ("declaring %s variable %s failed", - g_CurState.len() ? "state" : "global", varname.chars()); - - MustNext (";"); - continue; - } - - // ============================================================ - // Goto - if (token == "goto") { - MUST_NOT_TOPLEVEL - - // Get the name of the label - MustNext (); - - // Find the mark this goto statement points to - unsigned int m = w->FindMark (token); - - // If not set, define it - if (m == MAX_MARKS) { - m = w->AddMark (token); - g_UndefinedLabels[m] = new str (token); - } - - // Add a reference to the mark. - w->Write (DH_GOTO); - w->AddReference (m); - MustNext (";"); - continue; - } - - // ============================================================ - // If - if (token == "if") { - MUST_NOT_TOPLEVEL - PushScope (); - - // Condition - MustNext ("("); - - // Read the expression and write it. - MustNext (); - DataBuffer* c = ParseExpression (TYPE_INT); - w->WriteBuffer (c); - - MustNext (")"); - MustNext ("{"); - - // Add a mark - to here temporarily - and add a reference to it. - // Upon a closing brace, the mark will be adjusted. - unsigned int marknum = w->AddMark (""); - - // Use DH_IFNOTGOTO - if the expression is not true, we goto the mark - // we just defined - and this mark will be at the end of the scope block. - w->Write (DH_IFNOTGOTO); - w->AddReference (marknum); - - // Store it - SCOPE(0).mark1 = marknum; - SCOPE(0).type = SCOPETYPE_IF; - continue; - } - - if (token == "else") { - MUST_NOT_TOPLEVEL - MustNext ("{"); - - // Don't use PushScope as it resets the scope - g_ScopeCursor++; - if (g_ScopeCursor >= MAX_SCOPE) - ParserError ("too deep scope"); - - if (SCOPE(0).type != SCOPETYPE_IF) - ParserError ("else without preceding if"); - - // Write down to jump to the end of the else statement - // Otherwise we have fall-throughs - SCOPE(0).mark2 = w->AddMark (""); - - // Instruction to jump to the end after if block is complete - w->Write (DH_GOTO); - w->AddReference (SCOPE(0).mark2); - - // Move the ifnot mark here and set type to else - w->MoveMark (SCOPE(0).mark1); - SCOPE(0).type = SCOPETYPE_ELSE; - continue; - } - - // ============================================================ - // While - if (token == "while") { - MUST_NOT_TOPLEVEL - PushScope (); - - // While loops need two marks - one at the start of the loop and one at the - // end. The condition is checked at the very start of the loop, if it fails, - // we use goto to skip to the end of the loop. At the end, we loop back to - // the beginning with a go-to statement. - unsigned int mark1 = w->AddMark (""); // start - unsigned int mark2 = w->AddMark (""); // end - - // Condition - MustNext ("("); - MustNext (); - DataBuffer* expr = ParseExpression (TYPE_INT); - MustNext (")"); - MustNext ("{"); - - // Write condition - w->WriteBuffer (expr); - - // Instruction to go to the end if it fails - w->Write (DH_IFNOTGOTO); - w->AddReference (mark2); - - // Store the needed stuff - SCOPE(0).mark1 = mark1; - SCOPE(0).mark2 = mark2; - SCOPE(0).type = SCOPETYPE_WHILE; - continue; - } - - // ============================================================ - // For loop - if (token == "for") { - MUST_NOT_TOPLEVEL - PushScope (); - - // Initializer - MustNext ("("); - MustNext (); - DataBuffer* init = ParseStatement (w); - if (!init) - ParserError ("bad statement for initializer of for"); - - MustNext (";"); - - // Condition - MustNext (); - DataBuffer* cond = ParseExpression (TYPE_INT); - if (!cond) - ParserError ("bad statement for condition of for"); - - MustNext (";"); - - // Incrementor - MustNext (); - DataBuffer* incr = ParseStatement (w); - if (!incr) - ParserError ("bad statement for incrementor of for"); - - MustNext (")"); - MustNext ("{"); - - // First, write out the initializer - w->WriteBuffer (init); - - // Init two marks - int mark1 = w->AddMark (""); - int mark2 = w->AddMark (""); - - // Add the condition - w->WriteBuffer (cond); - w->Write (DH_IFNOTGOTO); - w->AddReference (mark2); - - // Store the marks and incrementor - SCOPE(0).mark1 = mark1; - SCOPE(0).mark2 = mark2; - SCOPE(0).buffer1 = incr; - SCOPE(0).type = SCOPETYPE_FOR; - continue; - } - - // ============================================================ - // Do/while loop - if (token == "do") { - MUST_NOT_TOPLEVEL - PushScope (); - MustNext ("{"); - SCOPE(0).mark1 = w->AddMark (""); - SCOPE(0).type = SCOPETYPE_DO; - continue; - } - - // ============================================================ - // Switch - if (token == "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 - PushScope (); - MustNext ("("); - MustNext (); - w->WriteBuffer (ParseExpression (TYPE_INT)); - MustNext (")"); - MustNext ("{"); - SCOPE(0).type = SCOPETYPE_SWITCH; - SCOPE(0).mark1 = w->AddMark (""); // end mark - SCOPE(0).buffer1 = NULL; // default header - continue; - } - - // ============================================================ - if (token == "case") { - // case is only allowed inside switch - if (SCOPE(0).type != SCOPETYPE_SWITCH) - ParserError ("case label outside switch"); - - // Get the literal (Zandronum does not support expressions here) - MustNumber (); - int num = atoi (token.chars ()); - MustNext (":"); - - for (int i = 0; i < MAX_CASE; i++) - if (SCOPE(0).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. - // 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 (DH_CASEGOTO); - w->Write (num); - AddSwitchCase (w, NULL); - SCOPE(0).casenumbers[SCOPE(0).casecursor] = num; - continue; - } - - if (token == "default") { - if (SCOPE(0).type != SCOPETYPE_SWITCH) - ParserError ("default label outside switch"); - - if (SCOPE(0).buffer1) - ParserError ("multiple default labels in one switch"); - - 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; - SCOPE(0).buffer1 = b; - b->Write (DH_DROP); - b->Write (DH_GOTO); - AddSwitchCase (w, b); - continue; - } - - // ============================================================ - // Break statement. - if (token == "break") { - if (!g_ScopeCursor) - ParserError ("unexpected `break`"); - - w->Write (DH_GOTO); - - // switch and if use mark1 for the closing point, - // for and while use mark2. - switch (SCOPE(0).type) { - case SCOPETYPE_IF: - case SCOPETYPE_SWITCH: - w->AddReference (SCOPE(0).mark1); - break; - case SCOPETYPE_FOR: - case SCOPETYPE_WHILE: - w->AddReference (SCOPE(0).mark2); - break; - default: - ParserError ("unexpected `break`"); - break; - } - - MustNext (";"); - continue; - } - - // ============================================================ - // Continue - if (token == "continue") { - MustNext (";"); - - int curs; - bool found = false; - - // Drop through the scope until we find a loop block - for (curs = g_ScopeCursor; curs > 0 && !found; curs--) { - switch (scopestack[curs].type) { - case SCOPETYPE_FOR: - case SCOPETYPE_WHILE: - case SCOPETYPE_DO: - w->Write (DH_GOTO); - w->AddReference (scopestack[curs].mark1); - found = true; - break; - default: - break; - } - } - - // No loop blocks - if (!found) - ParserError ("`continue`-statement not inside a loop"); - - continue; - } - - // ============================================================ - // Label - if (PeekNext() == ":") { - MUST_NOT_TOPLEVEL - - // want no conflicts.. - 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()); - - // See if a mark already exists for this label - int mark = -1; - for (int i = 0; i < MAX_MARKS; i++) { - if (g_UndefinedLabels[i] && *g_UndefinedLabels[i] == token) { - mark = i; - w->MoveMark (i); - - // No longer undefinde - delete g_UndefinedLabels[i]; - g_UndefinedLabels[i] = NULL; - } - } - - // Not found in unmarked lists, define it now - if (mark == -1) - w->AddMark (token); - - MustNext (":"); - continue; - } - - // ============================================================ - if (token == "const") { - constinfo_t info; - - // Get the type - MustNext (); - info.type = GetTypeByName (token); - - if (info.type == TYPE_UNKNOWN || info.type == TYPE_VOID) - ParserError ("unknown type `%s` for constant", (char*)token); - - MustNext (); - info.name = token; - - MustNext ("="); - - switch (info.type) { - case TYPE_BOOL: - case TYPE_INT: - MustNumber (false); - info.val = token; - break; - case TYPE_STRING: - MustString (); - info.val = token; - break; - case TYPE_UNKNOWN: - case TYPE_VOID: - break; - } - - g_ConstInfo << info; - - MustNext (";"); - continue; - } - - // ============================================================ - if (token == "}") { - // Closing brace - - // If we're in the block stack, we're descending down from it now - if (g_ScopeCursor > 0) { - switch (SCOPE(0).type) { - case SCOPETYPE_IF: - // Adjust the closing mark. - w->MoveMark (SCOPE(0).mark1); - - // We're returning from if, thus else can be next - g_CanElse = true; - break; - case SCOPETYPE_ELSE: - // else instead uses mark1 for itself (so if expression - // fails, jump to else), mark2 means end of else - w->MoveMark (SCOPE(0).mark2); - break; - case SCOPETYPE_FOR: - // Write the incrementor at the end of the loop block - w->WriteBuffer (SCOPE(0).buffer1); - // fall-thru - case SCOPETYPE_WHILE: - // Write down the instruction to go back to the start of the loop - w->Write (DH_GOTO); - w->AddReference (SCOPE(0).mark1); - - // Move the closing mark here since we're at the end of the while loop - w->MoveMark (SCOPE(0).mark2); - break; - case SCOPETYPE_DO: { - MustNext ("while"); - MustNext ("("); - MustNext (); - DataBuffer* expr = ParseExpression (TYPE_INT); - MustNext (")"); - MustNext (";"); - - // If the condition runs true, go back to the start. - w->WriteBuffer (expr); - w->Write (DH_IFGOTO); - w->AddReference (SCOPE(0).mark1); - break; - } - case SCOPETYPE_SWITCH: { - // Switch closes. Move down to the record buffer of - // the lower block. - if (SCOPE(1).casecursor != -1) - w->SwitchBuffer = SCOPE(1).casebuffers[SCOPE(1).casecursor]; - 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 (SCOPE(0).buffer1) - w->WriteBuffer (SCOPE(0).buffer1); - else { - w->Write (DH_DROP); - w->Write (DH_GOTO); - w->AddReference (SCOPE(0).mark1); - } - - // Go through all of the buffers we - // recorded down and write them. - for (unsigned int u = 0; u < MAX_CASE; u++) { - if (!SCOPE(0).casebuffers[u]) - continue; - - w->MoveMark (SCOPE(0).casemarks[u]); - w->WriteBuffer (SCOPE(0).casebuffers[u]); - } - - // Move the closing mark here - w->MoveMark (SCOPE(0).mark1); - break; - } - case SCOPETYPE_UNKNOWN: - break; - } - - // Descend down the stack - g_ScopeCursor--; - continue; - } - - int dataheader = (g_CurMode == MODE_EVENT) ? DH_ENDEVENT : - (g_CurMode == MODE_MAINLOOP) ? DH_ENDMAINLOOP : - (g_CurMode == MODE_ONENTER) ? DH_ENDONENTER : - (g_CurMode == MODE_ONEXIT) ? DH_ENDONEXIT : -1; - - if (dataheader == -1) - ParserError ("unexpected `}`"); - - // Data header must be written before mode is changed because - // onenter and mainloop go into special buffers, and we want - // the closing data headers into said buffers too. - w->Write (dataheader); - g_CurMode = MODE_TOPLEVEL; - - if (PeekNext() == ";") - MustNext (";"); - continue; - } - - // Check if it's a command - CommandDef* comm = FindCommand (token); - if (comm) { - w->GetCurrentBuffer()->Merge (ParseCommand (comm)); - MustNext (";"); - continue; - } - - // ============================================================ - // If nothing else, parse it as a statement - DataBuffer* b = ParseStatement (w); - if (!b) - ParserError ("unknown token `%s`", token.chars()); - - w->WriteBuffer (b); - MustNext (";"); - } - - // =============================================================================== - // Script file ended. Do some last checks and write the last things to main buffer - if (g_CurMode != MODE_TOPLEVEL) - ParserError ("script did not end at top level; did you forget a `}`?"); - - // stateSpawn must be defined! - if (!g_stateSpawnDefined) - ParserError ("script must have a state named `stateSpawn`!"); - - for (int i = 0; i < MAX_MARKS; i++) - if (g_UndefinedLabels[i]) - ParserError ("label `%s` is referenced via `goto` but isn't defined\n", g_UndefinedLabels[i]->chars()); - - // Dump the last state's onenter and mainloop - w->WriteBuffers (); - - // String table - w->WriteStringTable (); -} - -// ============================================================================ -// Parses a command call -DataBuffer* ScriptReader::ParseCommand (CommandDef* comm) { - DataBuffer* r = new DataBuffer(64); - if (g_CurMode == MODE_TOPLEVEL) - ParserError ("command call at top level"); - - MustNext ("("); - MustNext (); - - int curarg = 0; - while (1) { - if (token == ")") { - if (curarg < comm->numargs) - ParserError ("too few arguments passed to %s\n\tprototype: %s", - comm->name.chars(), GetCommandPrototype (comm).chars()); - break; - curarg++; - } - - if (curarg >= comm->maxargs) - ParserError ("too many arguments passed to %s\n\tprototype: %s", - comm->name.chars(), GetCommandPrototype (comm).chars()); - - r->Merge (ParseExpression (comm->argtypes[curarg])); - MustNext (); - - if (curarg < comm->numargs - 1) { - MustThis (","); - MustNext (); - } else if (curarg < comm->maxargs - 1) { - // Can continue, but can terminate as well. - if (token == ")") { - curarg++; - break; - } else { - MustThis (","); - MustNext (); - } - } - - curarg++; - } - - // If the script skipped any optional arguments, fill in defaults. - while (curarg < comm->maxargs) { - r->Write (DH_PUSHNUMBER); - r->Write (comm->defvals[curarg]); - curarg++; - } - - r->Write (DH_COMMAND); - r->Write (comm->number); - r->Write (comm->maxargs); - - return r; -} - -// ============================================================================ -// Is the given operator an assignment operator? -static bool IsAssignmentOperator (int oper) { - switch (oper) { - case OPER_ASSIGNADD: - case OPER_ASSIGNSUB: - case OPER_ASSIGNMUL: - case OPER_ASSIGNDIV: - case OPER_ASSIGNMOD: - case OPER_ASSIGNLEFTSHIFT: - case OPER_ASSIGNRIGHTSHIFT: - case OPER_ASSIGN: - return true; - } - return false; -} - -// ============================================================================ -// Finds an operator's corresponding dataheader -static word 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 - // OPER_ASSIGNLEFTSHIFT and OPER_ASSIGNRIGHTSHIFT do not - // have data headers, instead they are expanded out in - // the operator parser - 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; - case OPER_EQUALS: return DH_EQUALS; - case OPER_NOTEQUALS: return DH_NOTEQUALS; - case OPER_LESSTHAN: return DH_LESSTHAN; - case OPER_GREATERTHAN: return DH_GREATERTHAN; - case OPER_LESSTHANEQUALS: return DH_LESSTHANEQUALS; - case OPER_GREATERTHANEQUALS: return DH_GREATERTHANEQUALS; - case OPER_LEFTSHIFT: return DH_LSHIFT; - case OPER_RIGHTSHIFT: return DH_RSHIFT; - case OPER_OR: return DH_ORLOGICAL; - case OPER_AND: return DH_ANDLOGICAL; - case OPER_BITWISEOR: return DH_ORBITWISE; - case OPER_BITWISEEOR: return DH_EORBITWISE; - case OPER_BITWISEAND: return DH_ANDBITWISE; - } - - error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper); - return 0; -} - -// ============================================================================ -// Parses an expression, potentially recursively -DataBuffer* ScriptReader::ParseExpression (type_e reqtype) { - DataBuffer* retbuf = new DataBuffer (64); - - // Parse first operand - retbuf->Merge (ParseExprValue (reqtype)); - - // Parse any and all operators we get - int oper; - while ((oper = ParseOperator (true)) != -1) { - // We peeked the operator, move forward now - Next (); - - // Can't be an assignement operator, those belong in assignments. - if (IsAssignmentOperator (oper)) - ParserError ("assignment operator inside expression"); - - // Parse the right operand. - MustNext (); - DataBuffer* rb = ParseExprValue (reqtype); - - if (oper == OPER_TERNARY) { - // Ternary operator requires - naturally - a third operand. - MustNext (":"); - MustNext (); - DataBuffer* tb = ParseExprValue (reqtype); - - // It also is handled differently: there isn't a dataheader for ternary - // operator. Instead, we abuse PUSHNUMBER and IFNOTGOTO for this. - // Behold, big block of writing madness! :P - int mark1 = retbuf->AddMark (""); // start of "else" case - int mark2 = retbuf->AddMark (""); // end of expression - retbuf->Write (DH_IFNOTGOTO); // if the first operand (condition) - retbuf->AddMarkReference (mark1); // didn't eval true, jump into mark1 - retbuf->Merge (rb); // otherwise, perform second operand (true case) - retbuf->Write (DH_GOTO); // afterwards, jump to the end, which is - retbuf->AddMarkReference (mark2); // marked by mark2. - retbuf->MoveMark (mark1); // move mark1 at the end of the true case - retbuf->Merge (tb); // perform third operand (false case) - retbuf->MoveMark (mark2); // move the ending mark2 here - } else { - // Write to buffer - retbuf->Merge (rb); - retbuf->Write (DataHeaderByOperator (NULL, oper)); - } - } - - return retbuf; -} - -// ============================================================================ -// Parses an operator string. Returns the operator number code. -#define ISNEXT(char) (!PeekNext (peek ? 1 : 0) == char) -int ScriptReader::ParseOperator (bool peek) { - str oper; - if (peek) - oper += PeekNext (); - else - oper += token; - - if (-oper == "strlen") - return OPER_STRLEN; - - // Check one-char operators - bool equalsnext = ISNEXT ("="); - - int o = (oper == "=" && !equalsnext) ? OPER_ASSIGN : - (oper == ">" && !equalsnext && !ISNEXT (">")) ? OPER_GREATERTHAN : - (oper == "<" && !equalsnext && !ISNEXT ("<")) ? OPER_LESSTHAN : - (oper == "&" && !ISNEXT ("&")) ? OPER_BITWISEAND : - (oper == "|" && !ISNEXT ("|")) ? OPER_BITWISEOR : - (oper == "+" && !equalsnext) ? OPER_ADD : - (oper == "-" && !equalsnext) ? OPER_SUBTRACT : - (oper == "*" && !equalsnext) ? OPER_MULTIPLY : - (oper == "/" && !equalsnext) ? OPER_DIVIDE : - (oper == "%" && !equalsnext) ? OPER_MODULUS : - (oper == "^") ? OPER_BITWISEEOR : - (oper == "?") ? OPER_TERNARY : - -1; - - if (o != -1) { - return o; - } - - // Two-char operators - oper += PeekNext (peek ? 1 : 0); - equalsnext = PeekNext (peek ? 2 : 1) == ("="); - - o = (oper == "+=") ? OPER_ASSIGNADD : - (oper == "-=") ? OPER_ASSIGNSUB : - (oper == "*=") ? OPER_ASSIGNMUL : - (oper == "/=") ? OPER_ASSIGNDIV : - (oper == "%=") ? OPER_ASSIGNMOD : - (oper == "==") ? OPER_EQUALS : - (oper == "!=") ? OPER_NOTEQUALS : - (oper == ">=") ? OPER_GREATERTHANEQUALS : - (oper == "<=") ? OPER_LESSTHANEQUALS : - (oper == "&&") ? OPER_AND : - (oper == "||") ? OPER_OR : - (oper == "<<" && !equalsnext) ? OPER_LEFTSHIFT : - (oper == ">>" && !equalsnext) ? OPER_RIGHTSHIFT : - -1; - - if (o != -1) { - MustNext (); - return o; - } - - // Three-char opers - oper += PeekNext (peek ? 2 : 1); - o = oper == "<<=" ? OPER_ASSIGNLEFTSHIFT : - oper == ">>=" ? OPER_ASSIGNRIGHTSHIFT : - -1; - - if (o != -1) { - MustNext (); - MustNext (); - } - - return o; -} - -// ============================================================================ -str ScriptReader::ParseFloat () { - MustNumber (true); - str floatstring = token; - - // Go after the decimal point - if (PeekNext () == ".") { - Next ("."); - MustNumber (false); - floatstring += "."; - floatstring += token; - } - - return floatstring; -} - -// ============================================================================ -// 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 (type_e reqtype) { - DataBuffer* b = new DataBuffer(16); - - ScriptVar* g; - - // Prefixing "!" means negation. - bool negate = (token == "!"); - if (negate) // Jump past the "!" - Next (); - - // Handle strlen - if (token == "strlen") { - MustNext ("("); - MustNext (); - - // By this token we should get a string constant. - constinfo_t* constant = FindConstant (token); - if (!constant || constant->type != TYPE_STRING) - ParserError ("strlen only works with const str"); - - if (reqtype != TYPE_INT) - ParserError ("strlen returns int but %s is expected\n", (char*)GetTypeName (reqtype)); - - b->Write (DH_PUSHNUMBER); - b->Write (constant->val.len ()); - - MustNext (")"); - } else if (token == "(") { - // Expression - MustNext (); - DataBuffer* c = ParseExpression (reqtype); - b->Merge (c); - MustNext (")"); - } else if (CommandDef* comm = FindCommand (token)) { - delete b; - - // Command - if (reqtype && comm->returnvalue != reqtype) - ParserError ("%s returns an incompatible data type", comm->name.chars()); - b = ParseCommand (comm); - } else if (constinfo_t* constant = FindConstant (token)) { - // Type check - if (reqtype != constant->type) - ParserError ("constant `%s` is %s, expression requires %s\n", - (char*)constant->name, (char*)GetTypeName (constant->type), - (char*)GetTypeName (reqtype)); - - switch (constant->type) { - case TYPE_BOOL: - case TYPE_INT: - b->Write (DH_PUSHNUMBER); - b->Write (atoi (constant->val)); - break; - case TYPE_STRING: - b->WriteString (constant->val); - break; - case TYPE_VOID: - case TYPE_UNKNOWN: - break; - } - } else if ((g = FindGlobalVariable (token))) { - // Global variable - b->Write (DH_PUSHGLOBALVAR); - b->Write (g->index); - } else { - // If nothing else, check for literal - switch (reqtype) { - case TYPE_VOID: - case TYPE_UNKNOWN: - ParserError ("unknown identifier `%s` (expected keyword, function or variable)", token.chars()); - break; - case TYPE_BOOL: - case TYPE_INT: { - MustNumber (true); - - // All values are written unsigned - thus we need to write the value's - // absolute value, followed by an unary minus for negatives. - b->Write (DH_PUSHNUMBER); - - long v = atol (token); - b->Write (static_cast<word> (abs (v))); - if (v < 0) - b->Write (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. - MustString (true); - b->WriteString (token); - break; - } - } - - // Negate it now if desired - if (negate) - b->Write (DH_NEGATELOGICAL); - - return b; -} - -// ============================================================================ -// Parses an assignment. An assignment starts with a variable name, followed -// 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) { - bool global = !var->statename.len (); - - // Get an operator - MustNext (); - int oper = ParseOperator (); - if (!IsAssignmentOperator (oper)) - ParserError ("expected assignment operator"); - - if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction - ParserError ("can't alter variables at top level"); - - // Parse the right operand - MustNext (); - DataBuffer* retbuf = new DataBuffer; - DataBuffer* expr = ParseExpression (var->type); - - // <<= and >>= do not have data headers. Solution: expand them. - // a <<= b -> a = a << b - // a >>= b -> a = a >> b - if (oper == OPER_ASSIGNLEFTSHIFT || oper == OPER_ASSIGNRIGHTSHIFT) { - retbuf->Write (global ? DH_PUSHGLOBALVAR : DH_PUSHLOCALVAR); - retbuf->Write (var->index); - retbuf->Merge (expr); - retbuf->Write ((oper == OPER_ASSIGNLEFTSHIFT) ? DH_LSHIFT : DH_RSHIFT); - retbuf->Write (global ? DH_ASSIGNGLOBALVAR : DH_ASSIGNLOCALVAR); - retbuf->Write (var->index); - } else { - retbuf->Merge (expr); - long dh = DataHeaderByOperator (var, oper); - retbuf->Write (dh); - retbuf->Write (var->index); - } - - return retbuf; -} - -void ScriptReader::PushScope () { - g_ScopeCursor++; - if (g_ScopeCursor >= MAX_SCOPE) - ParserError ("too deep scope"); - - ScopeInfo* info = &SCOPE(0); - info->type = SCOPETYPE_UNKNOWN; - 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; - info->casenumbers[i] = -1; - } -} - -DataBuffer* ScriptReader::ParseStatement (ObjWriter* w) { - if (FindConstant (token)) // There should not be constants here. - ParserError ("invalid use for constant\n"); - - // If it's a variable, expect assignment. - if (ScriptVar* var = FindGlobalVariable (token)) - return ParseAssignment (var); - - return NULL; -} - -void ScriptReader::AddSwitchCase (ObjWriter* w, DataBuffer* b) { - ScopeInfo* info = &SCOPE(0); - - 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; -} - -constinfo_t* FindConstant (str token) { - for (uint i = 0; i < g_ConstInfo.size(); i++) - if (g_ConstInfo[i].name == token) - return &g_ConstInfo[i]; - return NULL; -} \ No newline at end of file
--- a/preprocessor.cxx Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#define __PARSER_CXX__ - -#include <stdio.h> -#include <stdlib.h> -#include "common.h" -#include "str.h" -#include "scriptreader.h" - -/* Since the preprocessor is *called* from ReadChar and I don't want - * to worry about recursive preprocessing, the preprocessor uses its - * own bare-bones variant of the function for file reading. - */ -char ScriptReader::PPReadChar () { - char c; - if (!fread (&c, sizeof (char), 1, fp[fc])) - return 0; - curchar[fc]++; - return c; -} - -void ScriptReader::PPMustChar (char c) { - char d = PPReadChar (); - if (c != d) - ParserError ("expected `%c`, got `%d`", c, d); -} - -// ============================================================================ -// Reads a word until whitespace -str ScriptReader::PPReadWord (char &term) { - str word; - while (1) { - char c = PPReadChar(); - if (feof (fp[fc]) || (IsCharWhitespace (c) && word.len ())) { - term = c; - break; - } - word += c; - } - return word; -} - -// ============================================================================ -// Preprocess any directives found in the script file -void ScriptReader::PreprocessDirectives () { - size_t spos = ftell (fp[fc]); - if (!DoDirectivePreprocessing ()) - fseek (fp[fc], spos, SEEK_SET); -} - -/* ============================================================================ - * Returns true if the pre-processing was successful, false if not. - * If pre-processing was successful, the file pointer remains where - * it was, if not, it's pushed back to where it was before preprocessing - * took place and is parsed normally. - */ -bool ScriptReader::DoDirectivePreprocessing () { - char trash; - // Directives start with a pound sign - if (PPReadChar() != '#') - return false; - - // Read characters until next whitespace to - // build the name of the directive - str directive = PPReadWord (trash); - - // Now check the directive name against known names - if (directive == "include") { - // #include-directive - char terminator; - str file = PPReadWord (terminator); - - if (!file.len()) - ParserError ("expected file name for #include, got nothing instead"); - OpenFile (file); - return true; - } else if (directive == "neurosphere") { - // #neurosphere - activates neurosphere compatibility, aka stuff - // that is still WIP and what main zandronum does not yet support. - // Most end users should never need this. - g_Neurosphere = true; - return true; - } - - ParserError ("unknown directive `#%s`!", directive.chars()); - return false; -} \ No newline at end of file
--- a/scriptreader.cxx Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,412 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include "string.h" -#include "str.h" -#include "common.h" -#include "scriptreader.h" - -#define STORE_POSITION \ - bool _atnewline = atnewline; \ - unsigned int _curline = curline[fc]; \ - unsigned int _curchar = curchar[fc]; - -#define RESTORE_POSITION \ - atnewline = _atnewline; \ - curline[fc] = _curline; \ - curchar[fc] = _curchar; - -// ============================================================================ -ScriptReader::ScriptReader (str path) { - token = ""; - prevtoken = ""; - prevpos = 0; - fc = -1; - - for (unsigned int u = 0; u < MAX_FILESTACK; u++) - fp[u] = NULL; - - OpenFile (path); - commentmode = 0; -} - -// ============================================================================ -ScriptReader::~ScriptReader () { - // If comment mode is 2 by the time the file ended, the - // comment was left unterminated. 1 is no problem, since - // it's terminated by newlines anyway. - if (commentmode == 2) - ParserError ("unterminated `/*`-style comment"); - - for (unsigned int u = 0; u < MAX_FILESTACK; u++) { - if (fp[u]) { - ParserWarning ("file idx %u remained open after parsing", u); - CloseFile (u); - } - } -} - -// ============================================================================ -// 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?", - path.chars()); - - // Save the position first. - if (fc != -1) { - savedpos[fc] = ftell (fp[fc]); - } - - fc++; - - fp[fc] = fopen (path, "r"); - if (!fp[fc]) { - ParserError ("couldn't open %s for reading!\n", path.chars ()); - exit (1); - } - - fseek (fp[fc], 0, SEEK_SET); - filepath[fc] = path.chars(); - curline[fc] = 1; - curchar[fc] = 1; - pos[fc] = 0; - atnewline = 0; -} - -// ============================================================================ -// Closes the current file -void ScriptReader::CloseFile (unsigned int u) { - if (u >= MAX_FILESTACK) - u = fc; - - if (!fp[u]) - return; - - fclose (fp[u]); - fp[u] = NULL; - fc--; - - if (fc != -1) - fseek (fp[fc], savedpos[fc], SEEK_SET); -} - -// ============================================================================ -char ScriptReader::ReadChar () { - if (feof (fp[fc])) - return 0; - - 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 - if (atnewline) { - atnewline = false; - curline[fc]++; - curchar[fc] = 0; // gets incremented to 1 - } - - if (c == '\n') { - atnewline = true; - - // Check for pre-processor directives - PreprocessDirectives (); - } - - curchar[fc]++; - return c; -} - -// ============================================================================ -// Peeks the next character -char ScriptReader::PeekChar (int offset) { - // Store current position - long curpos = ftell (fp[fc]); - STORE_POSITION - - // Forward by offset - fseek (fp[fc], offset, SEEK_CUR); - - // Read the character - char* c = (char*)malloc (sizeof (char)); - - if (!fread (c, sizeof (char), 1, fp[fc])) { - fseek (fp[fc], curpos, SEEK_SET); - return 0; - } - - // Rewind back - fseek (fp[fc], curpos, SEEK_SET); - RESTORE_POSITION - - return c[0]; -} - -// ============================================================================ -// 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 = ""; - - while (1) { - // Check end-of-file - if (feof (fp[fc])) { - // If we're just peeking, we shouldn't - // actually close anything.. - if (peek) - break; - - CloseFile (); - if (fc == -1) - break; - } - - // Check if the next token possibly starts a comment. - if (PeekChar () == '/' && !tmp.len()) { - char c2 = PeekChar (1); - // C++-style comment - if (c2 == '/') - commentmode = 1; - else if (c2 == '*') - commentmode = 2; - - // We don't need to actually read in the - // comment characters, since they will get - // ignored due to comment mode anyway. - } - - c = ReadChar (); - - // If this is a comment we're reading, check if this character - // gets the comment terminated, otherwise ignore it. - if (commentmode > 0) { - if (commentmode == 1 && c == '\n') { - // C++-style comments are terminated by a newline - commentmode = 0; - continue; - } else if (commentmode == 2 && c == '*') { - // C-style comments are terminated by a `*/` - if (PeekChar() == '/') { - commentmode = 0; - ReadChar (); - } - } - - // Otherwise, ignore it. - continue; - } - - // Non-alphanumber characters (sans underscore) break the word too. - // If there was prior data, the delimeter pushes the cursor back so - // that the next character will be the same delimeter. If there isn't, - // the delimeter itself is included (and thus becomes a token itself.) - if ((c >= 33 && c <= 47) || - (c >= 58 && c <= 64) || - (c >= 91 && c <= 96 && c != '_') || - (c >= 123 && c <= 126)) { - if (tmp.len()) - fseek (fp[fc], ftell (fp[fc]) - 1, SEEK_SET); - else - tmp += c; - break; - } - - if (IsCharWhitespace (c)) { - // Don't break if we haven't gathered anything yet. - if (tmp.len()) - break; - } else { - tmp += c; - } - } - - // If we got nothing here, read failed. This should - // only happen in the case of EOF. - if (!tmp.len()) { - token = ""; - return false; - } - - pos[fc]++; - prevtoken = token; - token = tmp; - return true; -} - -// ============================================================================ -// Returns the next token without advancing the cursor. -str ScriptReader::PeekNext (int offset) { - // Store current information - str storedtoken = token; - int cpos = ftell (fp[fc]); - STORE_POSITION - - // Advance on the token. - while (offset >= 0) { - if (!Next (true)) - return ""; - offset--; - } - - str tmp = token; - - // Restore position - fseek (fp[fc], cpos, SEEK_SET); - pos[fc]--; - token = storedtoken; - RESTORE_POSITION - return tmp; -} - -// ============================================================================ -void ScriptReader::Seek (unsigned int n, int origin) { - switch (origin) { - case SEEK_SET: - fseek (fp[fc], 0, SEEK_SET); - pos[fc] = 0; - break; - case SEEK_CUR: - break; - case SEEK_END: - printf ("ScriptReader::Seek: SEEK_END not yet supported.\n"); - break; - } - - for (unsigned int i = 0; i < n+1; i++) - Next(); -} - -// ============================================================================ -void ScriptReader::MustNext (const char* c) { - if (!Next()) { - if (strlen (c)) - ParserError ("expected `%s`, reached end of file instead\n", c); - else - ParserError ("expected a token, reached end of file instead\n"); - } - - if (strlen (c)) - MustThis (c); -} - -// ============================================================================ -void ScriptReader::MustThis (const char* c) { - if (token != c) - ParserError ("expected `%s`, got `%s` instead", c, token.chars()); -} - -// ============================================================================ -void ScriptReader::ParserError (const char* message, ...) { - PERFORM_FORMAT (message, outmessage); - ParserMessage ("\nError: ", outmessage); - exit (1); -} - -// ============================================================================ -void ScriptReader::ParserWarning (const char* message, ...) { - PERFORM_FORMAT (message, outmessage); - ParserMessage ("Warning: ", outmessage); -} - -// ============================================================================ -void ScriptReader::ParserMessage (const char* header, char* message) { - if (fc >= 0 && fc < MAX_FILESTACK) - fprintf (stderr, "%s%s:%u:%u: %s\n", - header, filepath[fc], curline[fc], curchar[fc], message); - else - fprintf (stderr, "%s%s\n", header, message); -} - -// ============================================================================ -// 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. - while (1) { - // can't end here! - if (feof (fp[fc])) - ParserError ("unterminated string"); - - char c = ReadChar (); - if (c == '"') - break; - - string += c; - } - - token = string; -} - -// ============================================================================ -void ScriptReader::MustNumber (bool fromthis) { - if (!fromthis) - MustNext (); - - str num = token; - if (num == "-") { - MustNext (); - num += token; - } - - // "true" and "false" are valid numbers - if (!token.icompare ("true")) - token = "1"; - else if (!token.icompare ("false")) - token = "0"; - else { - if (!token.isnumber()) - ParserError ("expected a number, got `%s`", num.chars()); - - str check; - check.appendformat ("%d", atoi (num)); - if (token != check) - ParserWarning ("integer too large: %s -> %s", num.chars(), check.chars()); - - token = num; - } -} \ No newline at end of file
--- a/scriptreader.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,211 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __SCRIPTREADER_H__ -#define __SCRIPTREADER_H__ - -#include <stdio.h> -#include "str.h" -#include "commands.h" -#include "objwriter.h" - -#define MAX_FILESTACK 8 -#define MAX_SCOPE 32 -#define MAX_CASE 64 - -class ScriptVar; - -// Operators -enum operator_e { - OPER_ADD, - OPER_SUBTRACT, - OPER_MULTIPLY, - OPER_DIVIDE, - OPER_MODULUS, - OPER_ASSIGN, - OPER_ASSIGNADD, - OPER_ASSIGNSUB, - OPER_ASSIGNMUL, - OPER_ASSIGNDIV, - OPER_ASSIGNMOD, // -- 10 - OPER_EQUALS, - OPER_NOTEQUALS, - OPER_LESSTHAN, - OPER_GREATERTHAN, - OPER_LESSTHANEQUALS, - OPER_GREATERTHANEQUALS, - OPER_LEFTSHIFT, - OPER_RIGHTSHIFT, - OPER_ASSIGNLEFTSHIFT, - OPER_ASSIGNRIGHTSHIFT, // -- 20 - OPER_OR, - OPER_AND, - OPER_BITWISEOR, - OPER_BITWISEAND, - OPER_BITWISEEOR, - OPER_TERNARY, - OPER_STRLEN, -}; - -// Mark types -enum marktype_e { - MARKTYPE_LABEL, - MARKTYPE_IF, - MARKTYPE_INTERNAL, // internal structures -}; - -// Block types -enum scopetype_e { - SCOPETYPE_UNKNOWN, - SCOPETYPE_IF, - SCOPETYPE_WHILE, - SCOPETYPE_FOR, - SCOPETYPE_DO, - SCOPETYPE_SWITCH, - SCOPETYPE_ELSE, -}; - -// ============================================================================ -// Meta-data about blocks -struct ScopeInfo { - unsigned int mark1; - unsigned int mark2; - scopetype_e type; - DataBuffer* buffer1; - - // switch-related stuff - // Which case are we at? - short casecursor; - - // Marks to case-blocks - int casemarks[MAX_CASE]; - - // Numbers of the case labels - int casenumbers[MAX_CASE]; - - // actual case blocks - DataBuffer* casebuffers[MAX_CASE]; - - // What is the current buffer of the block? - DataBuffer* recordbuffer; -}; - -// ============================================================================ -typedef struct { - str name; - type_e type; - str val; -} constinfo_t; - -// ============================================================================ -// The script reader reads the script, parses it and tells the object writer -// the bytecode it needs to write to file. -class ScriptReader { -public: - // ==================================================================== - // MEMBERS - FILE* fp[MAX_FILESTACK]; - char* filepath[MAX_FILESTACK]; - int fc; - - unsigned int pos[MAX_FILESTACK]; - unsigned int curline[MAX_FILESTACK]; - unsigned int curchar[MAX_FILESTACK]; - ScopeInfo scopestack[MAX_SCOPE]; - long savedpos[MAX_FILESTACK]; // filepointer cursor position - str token; - int commentmode; - long prevpos; - str prevtoken; - - // ==================================================================== - // METHODS - // scriptreader.cxx: - ScriptReader (str path); - ~ScriptReader (); - void OpenFile (str path); - void CloseFile (unsigned int u = MAX_FILESTACK); - char ReadChar (); - char PeekChar (int offset = 0); - bool Next (bool peek = false); - void Prev (); - str PeekNext (int offset = 0); - void Seek (unsigned int n, int origin); - void MustNext (const char* c = ""); - void MustThis (const char* c); - void MustString (bool gotquote = false); - void MustNumber (bool fromthis = false); - void MustBool (); - bool BoolValue (); - - void ParserError (const char* message, ...); - void ParserWarning (const char* message, ...); - - // parser.cxx: - void ParseBotScript (ObjWriter* w); - DataBuffer* ParseCommand (CommandDef* comm); - DataBuffer* ParseExpression (type_e reqtype); - DataBuffer* ParseAssignment (ScriptVar* var); - int ParseOperator (bool peek = false); - DataBuffer* ParseExprValue (type_e reqtype); - str ParseFloat (); - void PushScope (); - - // preprocessor.cxx: - void PreprocessDirectives (); - void PreprocessMacros (); - DataBuffer* ParseStatement (ObjWriter* w); - void AddSwitchCase (ObjWriter* w, DataBuffer* b); - -private: - bool atnewline; - char c; - void ParserMessage (const char* header, char* message); - - bool DoDirectivePreprocessing (); - char PPReadChar (); - void PPMustChar (char c); - str PPReadWord (char &term); -}; - -constinfo_t* FindConstant (str token); -extern bool g_Neurosphere; - -#endif // __SCRIPTREADER_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/array.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,190 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "common.h" + +#define ITERATE_SUBSCRIPTS(link) \ + for (link = data; link; link = link->next) + +#define foreach_counter(NAME) \ + (int)(reinterpret_cast<int> (foreach_counter##NAME) / sizeof (foreach_counter##NAME)) +#define foreach(T,NAME,ARRAY) \ + if (ARRAY.size() > 0) for (T NAME = ARRAY[0], *foreach_counter##NAME = 0; \ + foreach_counter(NAME) < (int)(ARRAY.size()); \ + NAME = ARRAY[reinterpret_cast<int> (++foreach_counter##NAME) / sizeof (foreach_counter##NAME)]) + +// Single element of an array +template <class T> class arrayElement { +public: + T value; + arrayElement<T>* next; + + arrayElement () { + next = NULL; + } +}; + +// Dynamic array +template <class T> class array { +public: + array () { + data = NULL; + } + + array (T* stuff, unsigned int c) { + printf ("%d elements\n", c); + data = NULL; + + for (unsigned int i = 0; i < c; i++) + push (stuff[c]); + } + + ~array () { + if (data) + deleteElement (data); + } + + void push (T stuff) { + arrayElement<T>* e = new arrayElement<T>; + e->value = stuff; + e->next = NULL; + + if (!data) { + data = e; + return; + } + + arrayElement<T>* link; + for (link = data; link && link->next; link = link->next); + link->next = e; + } + + T pop () { + int pos = size() - 1; + if (pos == -1) + error ("array::pop: tried to pop an array with no elements\n"); + T res = subscript (pos); + remove (pos); + return res; + } + + void remove (unsigned int pos) { + if (!data) + error ("tried to use remove on an array with no elements"); + + if (pos == 0) { + // special case for first element + arrayElement<T>* first = data; + data = data->next; + delete first; + return; + } + + arrayElement<T>* link = data; + unsigned int x = 0; + while (link->next) { + if (x == pos - 1) + break; + link = link->next; + x++; + } + if (!link) + error ("no such element in array\n"); + + arrayElement<T>* nextlink = link->next->next; + delete link->next; + link->next = nextlink; + } + + unsigned int size () { + unsigned int x = 0; + arrayElement<T>* link; + ITERATE_SUBSCRIPTS(link) + x++; + return x; + } + + T& subscript (unsigned int i) { + arrayElement<T>* link; + unsigned int x = 0; + ITERATE_SUBSCRIPTS(link) { + if (x == i) + return link->value; + x++; + } + + error ("array: tried to access subscript %u in an array of %u elements\n", i, x); + return data->value; + } + + T* out () { + int s = size(); + T* out = new T[s]; + unsigned int x = 0; + + arrayElement<T>* link; + ITERATE_SUBSCRIPTS(link) { + out[x] = link->value; + x++; + } + + return out; + } + + T& operator [] (const unsigned int i) { + return subscript (i); + } + + void operator << (const T stuff) { + push (stuff); + } + +private: + void deleteElement (arrayElement<T>* e) { + if (e->next) + deleteElement (e->next); + delete e; + } + + arrayElement<T>* data; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/botcommands.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// +// Skulltag Source +// Copyright (C) 2002 Brad Carney +// Copyright (C) 2007-2012 Skulltag Development Team +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// 3. Neither the name of the Skulltag Development Team nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// 4. Redistributions in any form must be accompanied by information on how to +// obtain complete source code for the software and any accompanying +// software that uses the software. The source code must either be included +// in the distribution or be available for no more than the cost of +// distribution plus a nominal fee, and must be freely redistributable +// under reasonable conditions. For an executable file, complete source +// code means the source code for all modules it contains. It does not +// include source code for modules or files that typically accompany the +// major components of the operating system on which the executable file +// runs. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Date created: 5/18/04 +// +// +// Filename: botcommands.h +// +// Description: Contains bot structures and prototypes +// [Dusk] Clipped stuff that botc doesn't need. +// +//----------------------------------------------------------------------------- + +#ifndef __BOTCOMMANDS_H__ +#define __BOTCOMMANDS_H__ + +#include "bots.h" + +//***************************************************************************** +// DEFINES + +#define SETENEMY_LASTSEEN 0 +#define SETENEMY_LASTSHOTBY 1 + +// Different results for pathing commands. +#define PATH_UNREACHABLE -1 +#define PATH_INCOMPLETE 0 +#define PATH_COMPLETE 1 +#define PATH_REACHEDGOAL 2 + +// This is the size of the return string for the bot command functions. +#define BOTCMD_RETURNSTRING_SIZE 256 + +//***************************************************************************** +typedef enum +{ + BOTCMD_CHANGESTATE, // Basic botcmd utility functions. + BOTCMD_DELAY, + BOTCMD_RAND, + BOTCMD_STRINGSAREEQUAL, + BOTCMD_LOOKFORPOWERUPS, // Search functions. + BOTCMD_LOOKFORWEAPONS, + BOTCMD_LOOKFORAMMO, + BOTCMD_LOOKFORBASEHEALTH, + BOTCMD_LOOKFORBASEARMOR, + BOTCMD_LOOKFORSUPERHEALTH, + BOTCMD_LOOKFORSUPERARMOR, /* 10 */ + BOTCMD_LOOKFORPLAYERENEMIES, + BOTCMD_GETCLOSESTPLAYERENEMY, + BOTCMD_MOVELEFT, // Movement functions. + BOTCMD_MOVERIGHT, + BOTCMD_MOVEFORWARD, + BOTCMD_MOVEBACKWARDS, + BOTCMD_STOPMOVEMENT, + BOTCMD_STOPFORWARDMOVEMENT, + BOTCMD_STOPSIDEWAYSMOVEMENT, + BOTCMD_CHECKTERRAIN, /* 20 */ + BOTCMD_PATHTOGOAL, // Pathing functions. + BOTCMD_PATHTOLASTKNOWNENEMYPOSITION, + BOTCMD_PATHTOLASTHEARDSOUND, + BOTCMD_ROAM, + BOTCMD_GETPATHINGCOSTTOITEM, + BOTCMD_GETDISTANCETOITEM, + BOTCMD_GETITEMNAME, + BOTCMD_ISITEMVISIBLE, + BOTCMD_SETGOAL, + BOTCMD_BEGINAIMINGATENEMY, /* 30 */ // Aiming functions. + BOTCMD_STOPAIMINGATENEMY, + BOTCMD_TURN, + BOTCMD_GETCURRENTANGLE, + BOTCMD_SETENEMY, // Enemy functions. + BOTCMD_CLEARENEMY, + BOTCMD_ISENEMYALIVE, + BOTCMD_ISENEMYVISIBLE, + BOTCMD_GETDISTANCETOENEMY, + BOTCMD_GETPLAYERDAMAGEDBY, + BOTCMD_GETENEMYINVULNERABILITYTICKS, /* 40 */ + BOTCMD_FIREWEAPON, // Weapon functions. + BOTCMD_BEGINFIRINGWEAPON, + BOTCMD_STOPFIRINGWEAPON, + BOTCMD_GETCURRENTWEAPON, + BOTCMD_CHANGEWEAPON, + BOTCMD_GETWEAPONFROMITEM, + BOTCMD_ISWEAPONOWNED, + BOTCMD_ISFAVORITEWEAPON, + BOTCMD_SAY, // Chat functions. + BOTCMD_SAYFROMFILE, /* 50 */ + BOTCMD_SAYFROMCHATFILE, + BOTCMD_BEGINCHATTING, + BOTCMD_STOPCHATTING, + BOTCMD_CHATSECTIONEXISTS, + BOTCMD_CHATSECTIONEXISTSINFILE, + BOTCMD_GETLASTCHATSTRING, + BOTCMD_GETLASTCHATPLAYER, + BOTCMD_GETCHATFREQUENCY, + BOTCMD_JUMP, // Jumping functions. + BOTCMD_BEGINJUMPING, /* 60 */ + BOTCMD_STOPJUMPING, + BOTCMD_TAUNT, // Other action functions. + BOTCMD_RESPAWN, + BOTCMD_TRYTOJOINGAME, + BOTCMD_ISDEAD, // Information about self functions. + BOTCMD_ISSPECTATING, + BOTCMD_GETHEALTH, + BOTCMD_GETARMOR, + BOTCMD_GETBASEHEALTH, + BOTCMD_GETBASEARMOR, /* 70 */ + BOTCMD_GETBOTSKILL, // Botskill functions. + BOTCMD_GETACCURACY, + BOTCMD_GETINTELLECT, + BOTCMD_GETANTICIPATION, + BOTCMD_GETEVADE, + BOTCMD_GETREACTIONTIME, + BOTCMD_GETPERCEPTION, + BOTCMD_SETSKILLINCREASE, // Botskill modifying functions functions. + BOTCMD_ISSKILLINCREASED, + BOTCMD_SETSKILLDECREASE, /* 80 */ + BOTCMD_ISSKILLDECREASED, + BOTCMD_GETGAMEMODE, // Other functions. + BOTCMD_GETSPREAD, + BOTCMD_GETLASTJOINEDPLAYER, + BOTCMD_GETPLAYERNAME, + BOTCMD_GETRECEIVEDMEDAL, + BOTCMD_ACS_EXECUTE, + BOTCMD_GETFAVORITEWEAPON, + BOTCMD_SAYFROMLUMP, + BOTCMD_SAYFROMCHATLUMP, /* 90 */ + BOTCMD_CHATSECTIONEXISTSINLUMP, + BOTCMD_CHATSECTIONEXISTSINCHATLUMP, + + NUM_BOTCMDS + +} BOTCMD_e; + +#endif // __BOTCOMMANDS_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/bots.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,272 @@ +//----------------------------------------------------------------------------- +// +// Skulltag Source +// Copyright (C) 2002 Brad Carney +// Copyright (C) 2007-2012 Skulltag Development Team +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// 3. Neither the name of the Skulltag Development Team nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// 4. Redistributions in any form must be accompanied by information on how to +// obtain complete source code for the software and any accompanying +// software that uses the software. The source code must either be included +// in the distribution or be available for no more than the cost of +// distribution plus a nominal fee, and must be freely redistributable +// under reasonable conditions. For an executable file, complete source +// code means the source code for all modules it contains. It does not +// include source code for modules or files that typically accompany the +// major components of the operating system on which the executable file +// runs. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// +// +// Filename: bots.h +// +// Description: Contains bot structures and prototypes +// [Dusk] Cropped out the stuff botc doesn't need. +// +//----------------------------------------------------------------------------- + +#ifndef __BOTS_H__ +#define __BOTS_H__ + +//***************************************************************************** +// DEFINES + +// Maximum number of variables on the bot evaluation stack. +#define BOTSCRIPT_STACK_SIZE 8 + +// Maximum number of botinto structures that can be defines. +#define MAX_BOTINFO 128 + +// Maximum number of states that can appear in a script. +#define MAX_NUM_STATES 256 + +// Maximum number of bot events that can be defined. +#define MAX_NUM_EVENTS 32 + +// Maximum number of global bot events that can be defined. +#define MAX_NUM_GLOBAL_EVENTS 32 + +// Maximum number of global variables that can be defined in a script. +#define MAX_SCRIPT_VARIABLES 128 + +// Maximum number of global arrays that can be defined in a script. +#define MAX_SCRIPT_ARRAYS 16 + +// Maximum number of global arrays that can be defined in a script. +#define MAX_SCRIPTARRAY_SIZE 65536 + +// Maxmimum number of state (local) variables that can appear in a script. +#define MAX_STATE_VARIABLES 16 + +// Maximum number of strings that can appear in a script stringlist. +#define MAX_LIST_STRINGS 128 + +// Maximum length of those strings in the stringlist. +#define MAX_STRING_LENGTH 256 + +// Maximum reaction time for a bot. +#define MAX_REACTION_TIME 52 + +// Maximum number of events the bots can store up that it's waiting to react to. +#define MAX_STORED_EVENTS 64 + +//***************************************************************************** +typedef enum +{ + // Bot skill ratings. + BOTSKILL_VERYPOOR, + BOTSKILL_POOR, + BOTSKILL_LOW, + BOTSKILL_MEDIUM, + BOTSKILL_HIGH, + BOTSKILL_EXCELLENT, + BOTSKILL_SUPREME, + BOTSKILL_GODLIKE, + BOTSKILL_PERFECT, + + NUM_BOT_SKILLS + +} BOTSKILL_e; + +//***************************************************************************** +// STRUCTURES +// + +//***************************************************************************** +// These are the botscript data headers that it writes out. +typedef enum +{ + DH_COMMAND, + DH_STATEIDX, + DH_STATENAME, + DH_ONENTER, + DH_MAINLOOP, + DH_ONEXIT, + DH_EVENT, + DH_ENDONENTER, + DH_ENDMAINLOOP, + DH_ENDONEXIT, + DH_ENDEVENT, + DH_IFGOTO, + DH_IFNOTGOTO, + DH_GOTO, + DH_ORLOGICAL, + DH_ANDLOGICAL, + DH_ORBITWISE, + DH_EORBITWISE, + DH_ANDBITWISE, + DH_EQUALS, + DH_NOTEQUALS, + DH_LESSTHAN, + DH_LESSTHANEQUALS, + DH_GREATERTHAN, + DH_GREATERTHANEQUALS, + DH_NEGATELOGICAL, + DH_LSHIFT, + DH_RSHIFT, + DH_ADD, + DH_SUBTRACT, + DH_UNARYMINUS, + DH_MULTIPLY, + DH_DIVIDE, + DH_MODULUS, + DH_PUSHNUMBER, + DH_PUSHSTRINGINDEX, + DH_PUSHGLOBALVAR, + DH_PUSHLOCALVAR, + DH_DROPSTACKPOSITION, + DH_SCRIPTVARLIST, + DH_STRINGLIST, + DH_INCGLOBALVAR, + DH_DECGLOBALVAR, + DH_ASSIGNGLOBALVAR, + DH_ADDGLOBALVAR, + DH_SUBGLOBALVAR, + DH_MULGLOBALVAR, + DH_DIVGLOBALVAR, + DH_MODGLOBALVAR, + DH_INCLOCALVAR, + DH_DECLOCALVAR, + DH_ASSIGNLOCALVAR, + DH_ADDLOCALVAR, + DH_SUBLOCALVAR, + DH_MULLOCALVAR, + DH_DIVLOCALVAR, + DH_MODLOCALVAR, + DH_CASEGOTO, + DH_DROP, + DH_INCGLOBALARRAY, + DH_DECGLOBALARRAY, + DH_ASSIGNGLOBALARRAY, + DH_ADDGLOBALARRAY, + DH_SUBGLOBALARRAY, + DH_MULGLOBALARRAY, + DH_DIVGLOBALARRAY, + DH_MODGLOBALARRAY, + DH_PUSHGLOBALARRAY, + DH_SWAP, + DH_DUP, + DH_ARRAYSET, + + NUM_DATAHEADERS + +} DATAHEADERS_e; + +//***************************************************************************** +// These are the different bot events that can be posted to a bot's state. +typedef enum +{ + BOTEVENT_KILLED_BYENEMY, + BOTEVENT_KILLED_BYPLAYER, + BOTEVENT_KILLED_BYSELF, + BOTEVENT_KILLED_BYENVIORNMENT, + BOTEVENT_REACHED_GOAL, + BOTEVENT_GOAL_REMOVED, + BOTEVENT_DAMAGEDBY_PLAYER, + BOTEVENT_PLAYER_SAY, + BOTEVENT_ENEMY_KILLED, + BOTEVENT_RESPAWNED, + BOTEVENT_INTERMISSION, + BOTEVENT_NEWMAP, + BOTEVENT_ENEMY_USEDFIST, + BOTEVENT_ENEMY_USEDCHAINSAW, + BOTEVENT_ENEMY_FIREDPISTOL, + BOTEVENT_ENEMY_FIREDSHOTGUN, + BOTEVENT_ENEMY_FIREDSSG, + BOTEVENT_ENEMY_FIREDCHAINGUN, + BOTEVENT_ENEMY_FIREDMINIGUN, + BOTEVENT_ENEMY_FIREDROCKET, + BOTEVENT_ENEMY_FIREDGRENADE, + BOTEVENT_ENEMY_FIREDRAILGUN, + BOTEVENT_ENEMY_FIREDPLASMA, + BOTEVENT_ENEMY_FIREDBFG, + BOTEVENT_ENEMY_FIREDBFG10K, + BOTEVENT_PLAYER_USEDFIST, + BOTEVENT_PLAYER_USEDCHAINSAW, + BOTEVENT_PLAYER_FIREDPISTOL, + BOTEVENT_PLAYER_FIREDSHOTGUN, + BOTEVENT_PLAYER_FIREDSSG, + BOTEVENT_PLAYER_FIREDCHAINGUN, + BOTEVENT_PLAYER_FIREDMINIGUN, + BOTEVENT_PLAYER_FIREDROCKET, + BOTEVENT_PLAYER_FIREDGRENADE, + BOTEVENT_PLAYER_FIREDRAILGUN, + BOTEVENT_PLAYER_FIREDPLASMA, + BOTEVENT_PLAYER_FIREDBFG, + BOTEVENT_PLAYER_FIREDBFG10K, + BOTEVENT_USEDFIST, + BOTEVENT_USEDCHAINSAW, + BOTEVENT_FIREDPISTOL, + BOTEVENT_FIREDSHOTGUN, + BOTEVENT_FIREDSSG, + BOTEVENT_FIREDCHAINGUN, + BOTEVENT_FIREDMINIGUN, + BOTEVENT_FIREDROCKET, + BOTEVENT_FIREDGRENADE, + BOTEVENT_FIREDRAILGUN, + BOTEVENT_FIREDPLASMA, + BOTEVENT_FIREDBFG, + BOTEVENT_FIREDBFG10K, + BOTEVENT_PLAYER_JOINEDGAME, + BOTEVENT_JOINEDGAME, + BOTEVENT_DUEL_STARTINGCOUNTDOWN, + BOTEVENT_DUEL_FIGHT, + BOTEVENT_DUEL_WINSEQUENCE, + BOTEVENT_SPECTATING, + BOTEVENT_LMS_STARTINGCOUNTDOWN, + BOTEVENT_LMS_FIGHT, + BOTEVENT_LMS_WINSEQUENCE, + BOTEVENT_WEAPONCHANGE, + BOTEVENT_ENEMY_BFGEXPLODE, + BOTEVENT_PLAYER_BFGEXPLODE, + BOTEVENT_BFGEXPLODE, + BOTEVENT_RECEIVEDMEDAL, + + NUM_BOTEVENTS + +} BOTEVENT_e; + +#endif // __BOTS_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/commands.cxx Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,217 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define __COMMANDS_CXX__ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "common.h" +#include "scriptreader.h" +#include "str.h" +#include "commands.h" + +// ============================================================================ +// Reads command definitions from commands.def and stores them to memory. +void ReadCommands () { + ScriptReader* r = new ScriptReader ("commands.def"); + g_CommDef = NULL; + CommandDef* curdef = g_CommDef; + unsigned int numCommDefs = 0; + + while (r->PeekNext().len()) { + CommandDef* comm = new CommandDef; + comm->next = NULL; + + // Number + r->MustNumber (); + comm->number = r->token; + + r->MustNext (":"); + + // Name + r->MustNext (); + comm->name = r->token; + if (IsKeyword (comm->name)) + r->ParserError ("command name `%s` conflicts with keyword", comm->name.chars()); + + r->MustNext (":"); + + // Return value + r->MustNext (); + comm->returnvalue = GetTypeByName (r->token); + if (comm->returnvalue == -1) + r->ParserError ("bad return value type `%s` for command %s", r->token.chars(), comm->name.chars()); + + r->MustNext (":"); + + // Num args + r->MustNumber (); + comm->numargs = r->token; + + r->MustNext (":"); + + // Max args + r->MustNumber (); + comm->maxargs = r->token; + + if (comm->maxargs > MAX_MAXARGS) + r->ParserError ("maxargs (%d) greater than %d!", comm->maxargs, MAX_MAXARGS); + + // Argument types + int curarg = 0; + while (curarg < comm->maxargs) { + r->MustNext (":"); + r->MustNext (); + + type_e type = GetTypeByName (r->token); + if (type == -1) + r->ParserError ("bad argument %d type `%s`", curarg, r->token.chars()); + if (type == TYPE_VOID) + r->ParserError ("void is not a valid argument type!"); + comm->argtypes[curarg] = type; + + r->MustNext ("("); + r->MustNext (); + + // - 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); + + strncpy (comm->argnames[curarg], r->token.chars(), MAX_ARGNAMELEN); + comm->argnames[curarg][MAX_ARGNAMELEN-1] = 0; + + // If this is an optional parameter, we need the default value. + if (curarg >= comm->numargs) { + r->MustNext ("="); + switch (type) { + case TYPE_INT: + case TYPE_BOOL: + r->MustNumber(); + break; + case TYPE_STRING: + r->MustString(); + break; + case TYPE_UNKNOWN: + case TYPE_VOID: + break; + } + + comm->defvals[curarg] = r->token; + } + + r->MustNext (")"); + curarg++; + } + + if (!g_CommDef) + g_CommDef = comm; + + if (!curdef) { + curdef = comm; + } else { + curdef->next = comm; + curdef = comm; + } + numCommDefs++; + } + + if (!numCommDefs) + r->ParserError ("no commands defined!\n"); + + r->CloseFile (); + delete r; + printf ("%d command definitions read.\n", numCommDefs); +} + +// ============================================================================ +// Finds a command by name +CommandDef* FindCommand (str fname) { + CommandDef* comm; + ITERATE_COMMANDS (comm) { + if (!fname.icompare (comm->name)) + return comm; + } + + return NULL; +} + +// ============================================================================ +// Returns the prototype of the command +str GetCommandPrototype (CommandDef* comm) { + str text; + text += GetTypeName (comm->returnvalue); + text += ' '; + text += comm->name; + text += '('; + + bool hasOptionalArguments = false; + for (int i = 0; i < comm->maxargs; i++) { + if (i == comm->numargs) { + hasOptionalArguments = true; + text += '['; + } + + if (i) + text += ", "; + + text += GetTypeName (comm->argtypes[i]); + text += ' '; + text += comm->argnames[i]; + + if (i >= comm->numargs) { + text += '='; + + bool isString = comm->argtypes[i] == TYPE_STRING; + if (isString) text += '"'; + + char defvalstring[8]; + sprintf (defvalstring, "%d", comm->defvals[i]); + text += defvalstring; + + if (isString) text += '"'; + } + } + + if (hasOptionalArguments) + text += ']'; + text += ')'; + return text; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/commands.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,75 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __COMMANDS_H__ +#define __COMMANDS_H__ + +#define MAX_MAXARGS 8 +#define MAX_ARGNAMELEN 16 + +#include "common.h" +#include "str.h" +#include "botcommands.h" + +#define ITERATE_COMMANDS(comm) \ + for (comm = g_CommDef; comm->next != NULL; comm = comm->next) + +struct CommandDef { + str name; + int number; + int numargs; + int maxargs; + type_e returnvalue; + type_e argtypes[MAX_MAXARGS]; + char argnames[MAX_MAXARGS][MAX_ARGNAMELEN]; + int defvals[MAX_MAXARGS]; + CommandDef* next; +}; + +void ReadCommands (); +CommandDef* FindCommand (str a); +str GetCommandPrototype (CommandDef* comm); + +#ifndef __COMMANDS_CXX__ +extern +#endif +CommandDef* g_CommDef; + +#endif // __COMMANDS_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,186 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include <stdio.h> +#include <stdarg.h> +#include <stdint.h> +#include "bots.h" +#include "str.h" + +// Application name and version +#define APPNAME "botc" +#define VERSION_MAJOR 0 +#define VERSION_MINOR 999 + +// Use a macro for Write so we can get the function name +// This can be pretty crucial in debugging. +#ifdef __GNUC__ +#define Write(STUFF) DoWrite (__PRETTY_FUNCTION__, STUFF) +#else +#define Write(STUFF) DoWrite (__func__, STUFF) +#endif + +// On Windows, files are case-insensitive +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__) + #define FILE_CASEINSENSITIVE +#endif + +// Parser mode: where is the parser at? +enum parsermode_e { + MODE_TOPLEVEL, // at top level + MODE_EVENT, // inside event definition + MODE_MAINLOOP, // inside mainloop + MODE_ONENTER, // inside onenter + MODE_ONEXIT, // inside onexit +}; + +enum type_e { + TYPE_UNKNOWN = 0, + TYPE_VOID, + TYPE_INT, + TYPE_STRING, + TYPE_BOOL, +}; + +#define CHECK_FILE(pointer,path,action) \ + if (!pointer) { \ + error ("couldn't open %s for %s!\n", (char*)path, action); \ + 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" : "" + +// Shortcut for zeroing something +#define ZERO(obj) memset (&obj, 0, sizeof (obj)); + +void error (const char* text, ...); +char* ObjectFileName (str s); +bool fexists (char* path); +type_e GetTypeByName (str token); +str GetTypeName (type_e type); + +// Make the parser's variables globally available +extern int g_NumStates; +extern int g_NumEvents; +extern parsermode_e g_CurMode; +extern str g_CurState; + +#define neurosphere if (g_Neurosphere) +#define twice for (int repeat_token = 0; repeat_token < 2; repeat_token++) + +#ifndef __GNUC__ +#define __attribute__(X) +#endif +#define deprecated __attribute__ ((deprecated)) + +// Power function +template<class T> T pow (T a, unsigned int b) { + if (!b) + return 1; + + T r = a; + while (b > 1) { + b--; + r = r * a; + } + + return r; +} + +// Whitespace check +inline bool IsCharWhitespace (char c) { + return (c <= 32 || c == 127 || c == 255); +} + +// Byte datatype +typedef int32_t word; +typedef unsigned char byte; + +// Keywords +#ifndef __MAIN_CXX__ +extern const char** g_Keywords; +#endif + +bool IsKeyword (str s); +unsigned int NumKeywords (); + +// Script mark and reference +struct ScriptMark { + str name; + size_t pos; +}; + +struct ScriptMarkReference { + unsigned int num; + size_t pos; +}; + +// ==================================================================== +// Generic union +template <class T> union union_t { + T val; + byte b[sizeof (T)]; + char c[sizeof (T)]; + double d; + float f; + int i; + word w; +}; + +// ==================================================================== +// Finds a byte in the given value. +template <class T> inline unsigned char GetByteIndex (T a, unsigned int b) { + union_t<T> uni; + uni.val = a; + return uni.b[b]; +} + +#endif // __COMMON_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/databuffer.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,303 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DATABUFFER_H__ +#define __DATABUFFER_H__ +#include <stdio.h> +#include <string.h> +#include "common.h" +#include "stringtable.h" + +#define MAX_MARKS 512 + +extern int g_NextMark; + +// ============================================================================ +// DataBuffer: A dynamic data buffer. +class DataBuffer { +public: + // The actual buffer + byte* buffer; + + // Allocated size of the buffer + unsigned int allocsize; + + // 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 DoWrite (const char* func, T stuff) { + // printf ("DoWrite: called from %s\n", func); + if (writesize + sizeof (T) >= allocsize) { + // We don't have enough space in the buffer to write + // the stuff - thus resize. First, store the old + // buffer temporarily: + char* copy = new char[allocsize]; + memcpy (copy, buffer, allocsize); + + // Remake the buffer with the new size. Have enough space + // for the stuff we're going to write, as well as a bit + // of leeway so we don't have to resize immediately again. + size_t newsize = allocsize + sizeof (T) + 128; + + delete buffer; + buffer = new unsigned char[newsize]; + allocsize = newsize; + + // Now, copy the stuff back. + memcpy (buffer, copy, allocsize); + delete copy; + } + + // Buffer is now guaranteed to have enough space. + // Write the stuff one byte at a time. + union_t<T> uni; + uni.val = stuff; + for (unsigned int x = 0; x < sizeof (T); x++) { + if (writesize >= allocsize) // should NEVER happen because resizing is done above + error ("DataBuffer: written size exceeds allocated size!\n"); + + buffer[writesize] = uni.b[x]; + writesize++; + } + } + + // ==================================================================== + // Merge another data buffer into this one. + void Merge (DataBuffer* other) { + if (!other) + return; + int oldsize = writesize; + + for (unsigned int x = 0; x < other->writesize; x++) + Write (*(other->buffer+x)); + + // Merge its marks and references + unsigned int u = 0; + for (u = 0; u < MAX_MARKS; u++) { + if (other->marks[u]) { + // Merge the mark and offset its position. + 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 + // TODO: add a g_NextRef system like here, akin to marks! + unsigned int r = AddMarkReference (other->refs[u]->num, false); + refs[r]->pos = other->refs[u]->pos + oldsize; + } + } + + delete other; + } + + // Clones this databuffer to a new one and returns it. + DataBuffer* Clone () { + DataBuffer* other = new DataBuffer; + for (unsigned int x = 0; x < writesize; x++) + other->Write (*(buffer+x)); + return other; + } + + // ==================================================================== + // Adds a mark to the buffer. A mark is a "pointer" 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 (str name) { + // Find a free slot for the mark + 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"); + + ScriptMark* m = new ScriptMark; + m->name = name; + m->pos = writesize; + marks[u] = m; + return u; + } + + // ==================================================================== + // 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, bool placeholder = true) { + unsigned int u; + for (u = 0; u < MAX_MARKS; u++) + if (!refs[u]) + break; + + if (u == MAX_MARKS) + error ("mark reference quota exceeded, all goto-statements, if-structs and loops add refs\n"); + + ScriptMarkReference* r = new ScriptMarkReference; + r->num = marknum; + r->pos = writesize; + refs[u] = r; + + // Write a dummy placeholder for the reference + if (placeholder) + Write (1234); + + return u; + } + + // Delete a mark and all references to it. + void DeleteMark (unsigned int marknum) { + if (!marks[marknum]) + return; + + // 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; + } + } + } + + // Adjusts a mark to the current position + void MoveMark (unsigned int mark, int offset = -1) { + if (!marks[mark]) + return; + marks[mark]->pos = writesize; + } + + void OffsetMark (unsigned int mark, size_t offset) { + if (!marks[mark]) + return; + marks[mark]->pos += offset; + } + + // Dump the buffer (for debugging purposes) + void Dump() { + for (unsigned int x = 0; x < writesize; x++) + printf ("%d. [%d]\n", x, *(buffer+x)); + } + + // Count the amount of marks + unsigned int CountMarks () { + unsigned int count = 0; + for (unsigned int u = 0; u < MAX_MARKS; u++) + count += !!marks[u]; + return count; + } + + // Count the amount of refs + unsigned int CountReferences () { + unsigned int count = 0; + for (unsigned int u = 0; u < MAX_MARKS; u++) + count += !!refs[u]; + return count; + } + + // Write a float into the buffer + void WriteFloat (str floatstring) { + // TODO: Casting float to word causes the decimal to be lost. + // Find a way to store the number without such loss. + float val = atof (floatstring); + Write (DH_PUSHNUMBER); + Write (static_cast<word> ((val > 0) ? val : -val)); + if (val < 0) + Write (DH_UNARYMINUS); + } + + void WriteString (str string) { + Write (DH_PUSHSTRINGINDEX); + Write (PushToStringTable (string)); + } +}; + +#endif // __DATABUFFER_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/events.cxx Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,112 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define __EVENTS_CXX__ +#include <stdlib.h> +#include <stdio.h> +#include "common.h" +#include "scriptreader.h" +#include "str.h" +#include "events.h" + +EventDef* g_EventDef; + +// ============================================================================ +// Read event definitions from file +void ReadEvents () { + ScriptReader* r = new ScriptReader ("events.def"); + g_EventDef = NULL; + EventDef* curdef = g_EventDef; + unsigned int numEventDefs = 0; + while (r->Next()) { + EventDef* e = new EventDef; + e->name = r->token; + e->number = numEventDefs; + e->next = NULL; + + // g_EventDef becomes the first eventdef + if (!g_EventDef) + g_EventDef = e; + + if (!curdef) { + curdef = e; + } else { + curdef->next = e; + curdef = e; + } + numEventDefs++; + } + + delete r; + printf ("%d event definitions read.\n", numEventDefs); +} + +// ============================================================================ +// Delete event definitions recursively +void UnlinkEvents (EventDef* e) { + if (e->next) + UnlinkEvents (e->next); + delete e; +} + +// ============================================================================ +// Finds an event definition by index +EventDef* FindEventByIdx (unsigned int idx) { + EventDef* e = g_EventDef; + while (idx > 0) { + if (!e->next) + return NULL; + e = e->next; + idx--; + } + return e; +} + +// ============================================================================ +// Finds an event definition by name +EventDef* FindEventByName (str a) { + EventDef* e; + for (e = g_EventDef; e->next != NULL; e = e->next) { + if (!a.icompare (e->name)) + return e; + } + + return NULL; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/events.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,57 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __EVENT_H__ +#define __EVENT_H__ + +#include "str.h" + +struct EventDef { + str name; + int number; + EventDef* next; +}; + +void ReadEvents (); +void UnlinkEvents (EventDef* e); +EventDef* FindEventByIdx (unsigned int idx); +EventDef* FindEventByName (str a); + +#endif // __EVENT_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.cxx Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,273 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define __MAIN_CXX__ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "common.h" + +#include "str.h" +#include "scriptreader.h" +#include "objwriter.h" +#include "events.h" +#include "commands.h" +#include "stringtable.h" +#include "variables.h" +#include "array.h" +#include "databuffer.h" + +#include "bots.h" +#include "botcommands.h" + +// List of keywords +const char* g_Keywords[] = { + "bool", + "break", + "case", + "continue", + "const", + "default", + "do", + "else", + "event", + "for", + "goto", + "if", + "int", + "mainloop", + "onenter", + "onexit", + "state", + "switch", + "str" + "void", + "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 + "enum", // Would enum actually be useful? I think so. + "func", // Would function support need external support from zandronum? + "return", +}; + +// databuffer global variable +int g_NextMark = 0; + +int main (int argc, char** argv) { + // Intepret command-line parameters: + // -l: list commands + // I guess there should be a better way to do this. + if (argc == 2 && !strcmp (argv[1], "-l")) { + ReadCommands (); + printf ("Begin list of commands:\n"); + printf ("------------------------------------------------------\n"); + + CommandDef* comm; + ITERATE_COMMANDS (comm) + printf ("%s\n", GetCommandPrototype (comm).chars()); + + printf ("------------------------------------------------------\n"); + printf ("End of command list\n"); + exit (0); + } + + // Print header + str header; + str headerline = "-="; + header.appendformat ("%s version %d.%d", APPNAME, VERSION_MAJOR, VERSION_MINOR); + + headerline *= (header.len() / 2) - 1; + headerline += '-'; + printf ("%s\n%s\n", header.chars(), headerline.chars()); + + if (argc < 2) { + fprintf (stderr, "usage: %s <infile> [outfile] # compiles botscript\n", argv[0]); + fprintf (stderr, " %s -l # lists commands\n", argv[0]); + exit (1); + } + + // A word should always be exactly 4 bytes. The above list command + // doesn't need it, but the rest of the program does. + if (sizeof (word) != 4) + error ("%s expects a word (uint32_t) to be 4 bytes in size, is %d\n", + APPNAME, sizeof (word)); + + str outfile; + if (argc < 3) + outfile = ObjectFileName (argv[1]); + else + outfile = argv[2]; + + // If we'd end up writing into an existing file, + // ask the user if we want to overwrite it + if (fexists (outfile)) { + // Additional warning if the paths are the same + str warning; +#ifdef FILE_CASEINSENSITIVE + if (!outfile.icompare (argv[1])) +#else + if (!outfile.compare (argv[1])) +#endif + { + warning = "\nWARNING: Output file is the same as the input file. "; + warning += "Answering yes here will destroy the source!\n"; + warning += "Continue nevertheless?"; + } + printf ("output file `%s` already exists! overwrite?%s (y/n) ", outfile.chars(), warning.chars()); + + char ans; + fgets (&ans, 2, stdin); + if (ans != 'y') { + printf ("abort\n"); + exit (1); + } + } + + // Read definitions + printf ("Reading definitions...\n"); + ReadEvents (); + ReadCommands (); + + // Init stuff + InitStringTable (); + + // Prepare reader and writer + ScriptReader* r = new ScriptReader (argv[1]); + ObjWriter* w = new ObjWriter (outfile); + + // We're set, begin parsing :) + printf ("Parsing script...\n"); + r->ParseBotScript (w); + printf ("Script parsed successfully.\n"); + + // Parse done, print statistics and write to file + unsigned int globalcount = g_GlobalVariables.size(); + unsigned int stringcount = CountStringTable (); + int NumMarks = w->MainBuffer->CountMarks (); + int NumRefs = w->MainBuffer->CountReferences (); + 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 + delete r; + delete w; + + // Done! + exit (0); +} + +// ============================================================================ +// Utility functions + +// ============================================================================ +// Does the given file exist? +bool fexists (char* path) { + if (FILE* test = fopen (path, "r")) { + fclose (test); + return true; + } + return false; +} + +// ============================================================================ +// Generic error +void error (const char* text, ...) { + PERFORM_FORMAT (text, c); + fprintf (stderr, "error: %s", c); + exit (1); +} + +// ============================================================================ +// Mutates given filename to an object filename +char* ObjectFileName (str s) { + // Locate the extension and chop it out + unsigned int extdot = s.last ("."); + if (extdot >= s.len()-4) + s -= (s.len() - extdot); + + s += ".o"; + return s.chars(); +} + +// ============================================================================ +// Is the given argument a reserved keyword? +bool IsKeyword (str s) { + for (unsigned int u = 0; u < NumKeywords (); u++) + if (!s.icompare (g_Keywords[u])) + return true; + return false; +} + +unsigned int NumKeywords () { + return sizeof (g_Keywords) / sizeof (const char*); +} + +// ============================================================================ +type_e GetTypeByName (str t) { + t = t.tolower(); + return (t == "int") ? TYPE_INT : + (t == "str") ? TYPE_STRING : + (t == "void") ? TYPE_VOID : + (t == "bool") ? TYPE_BOOL : + TYPE_UNKNOWN; +} + + +// ============================================================================ +// Inverse operation - type name by value +str GetTypeName (type_e type) { + switch (type) { + case TYPE_INT: return "int"; break; + case TYPE_STRING: return "str"; break; + case TYPE_VOID: return "void"; break; + case TYPE_BOOL: return "bool"; break; + case TYPE_UNKNOWN: return "???"; break; + } + + return ""; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objwriter.cxx Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,193 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define __OBJWRITER_CXX__ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "common.h" +#include "str.h" +#include "objwriter.h" +#include "databuffer.h" +#include "stringtable.h" + +#include "bots.h" + +extern bool g_GotMainLoop; + +ObjWriter::ObjWriter (str path) { + MainBuffer = new DataBuffer; + MainLoopBuffer = new DataBuffer; + OnEnterBuffer = new DataBuffer; + SwitchBuffer = NULL; // created on demand + numWrittenBytes = 0; + numWrittenReferences = 0; + filepath = path; +} + +void ObjWriter::WriteString (char* s) { + Write (strlen (s)); + for (unsigned int u = 0; u < strlen (s); u++) + Write ((s)[u]); +} + +void ObjWriter::WriteString (const char* s) { + WriteString (const_cast<char*> (s)); +} + +void ObjWriter::WriteString (str s) { + WriteString (s.chars()); +} + +void ObjWriter::WriteBuffer (DataBuffer* buf) { + GetCurrentBuffer()->Merge (buf); +} + +void ObjWriter::WriteBuffers () { + // If there was no mainloop defined, write a dummy one now. + if (!g_GotMainLoop) { + MainLoopBuffer->Write (DH_MAINLOOP); + MainLoopBuffer->Write (DH_ENDMAINLOOP); + } + + // Write the onenter and mainloop buffers, IN THAT ORDER + for (int i = 0; i < 2; i++) { + DataBuffer** buf = (!i) ? &OnEnterBuffer : &MainLoopBuffer; + WriteBuffer (*buf); + + // Clear the buffer afterwards for potential next state + *buf = new DataBuffer; + } + + // Next state definitely has no mainloop yet + g_GotMainLoop = false; +} + +// Write string table +void ObjWriter::WriteStringTable () { + unsigned int stringcount = CountStringTable (); + if (!stringcount) + return; + + // Write header + Write (DH_STRINGLIST); + Write (stringcount); + + // Write all strings + for (unsigned int a = 0; a < stringcount; a++) + WriteString (g_StringTable[a]); +} + +// Write main buffer to file +void ObjWriter::WriteToFile () { + fp = fopen (filepath, "w"); + CHECK_FILE (fp, filepath, "writing"); + + // First, resolve references + numWrittenReferences = 0; + for (unsigned int u = 0; u < MAX_MARKS; u++) { + ScriptMarkReference* ref = MainBuffer->refs[u]; + if (!ref) + continue; + + // 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", + u, ref->pos, ref->num, MainBuffer->marks[ref->num]->pos); + */ + numWrittenReferences++; + } + + // Then, dump the main buffer to the file + for (unsigned int x = 0; x < MainBuffer->writesize; x++) + WriteDataToFile<byte> (*(MainBuffer->buffer+x)); + + printf ("-- %u byte%s written to %s\n", numWrittenBytes, PLURAL (numWrittenBytes), filepath.chars()); + fclose (fp); +} + +DataBuffer* ObjWriter::GetCurrentBuffer() { + return SwitchBuffer ? SwitchBuffer : + (g_CurMode == MODE_MAINLOOP) ? MainLoopBuffer : + (g_CurMode == MODE_ONENTER) ? OnEnterBuffer : + MainBuffer; +} + +ScriptMark* g_ScriptMark = NULL; + +// Adds a mark +unsigned int ObjWriter::AddMark (str name) { + return GetCurrentBuffer()->AddMark (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 (str name) { + DataBuffer* b = GetCurrentBuffer(); + for (unsigned int u = 0; u < MAX_MARKS; u++) { + if (b->marks[u] && !b->marks[u]->name.icompare (name)) + return u; + } + return MAX_MARKS; +} + +// Moves a mark to the current position +void ObjWriter::MoveMark (unsigned int mark) { + GetCurrentBuffer()->MoveMark (mark); +} + +// Deletes a mark +void ObjWriter::DeleteMark (unsigned int mark) { + GetCurrentBuffer()->DeleteMark (mark); +} + +void ObjWriter::OffsetMark (unsigned int mark, int offset) { + GetCurrentBuffer()->OffsetMark (mark, offset); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objwriter.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,128 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __OBJWRITER_H__ +#define __OBJWRITER_H__ + +#include <stdio.h> +#include <typeinfo> +#include <string.h> +#include "common.h" +#include "str.h" +#include "databuffer.h" + +class ObjWriter { +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; + + // ==================================================================== + // METHODS + ObjWriter (str path); + void WriteString (char* s); + void WriteString (const char* s); + void WriteString (str s); + void WriteBuffer (DataBuffer* buf); + void WriteBuffers (); + void WriteStringTable (); + void WriteToFile (); + DataBuffer* GetCurrentBuffer (); + + unsigned int AddMark (str name); + unsigned int FindMark (str name); + unsigned int AddReference (unsigned int mark); + void MoveMark (unsigned int mark); + void OffsetMark (unsigned int mark, int offset); + void DeleteMark (unsigned int mark); + template <class T> void DoWrite (const char* func, T stuff) { + GetCurrentBuffer ()->DoWrite (func, stuff); + } + + // Default to word + void DoWrite (const char* func, word stuff) { + DoWrite<word> (func, stuff); + } + + void DoWrite (const char* func, byte stuff) { + DoWrite<byte> (func, 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++) { + fwrite (&uni.b[x], 1, 1, fp); + numWrittenBytes++; + } + } +}; + +#endif // __OBJWRITER_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser.cxx Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,1203 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define __PARSER_CXX__ + +#include <stdio.h> +#include <stdlib.h> +#include "common.h" +#include "str.h" +#include "objwriter.h" +#include "scriptreader.h" +#include "events.h" +#include "commands.h" +#include "stringtable.h" +#include "variables.h" +#include "array.h" + +#define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \ + 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()); + +#define SCOPE(n) scopestack[g_ScopeCursor - n] + +int g_NumStates = 0; +int g_NumEvents = 0; +parsermode_e g_CurMode = MODE_TOPLEVEL; +str g_CurState = ""; +bool g_stateSpawnDefined = false; +bool g_GotMainLoop = false; +unsigned int g_ScopeCursor = 0; +DataBuffer* g_IfExpression = NULL; +bool g_CanElse = false; +str* g_UndefinedLabels[MAX_MARKS]; +bool g_Neurosphere = false; // neurosphere-compat +array<constinfo_t> g_ConstInfo; + +// ============================================================================ +// 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::ParseBotScript (ObjWriter* w) { + // Zero the entire block stack first + for (int i = 0; i < MAX_SCOPE; i++) + ZERO(scopestack[i]); + + for (int i = 0; i < MAX_MARKS; i++) + g_UndefinedLabels[i] = NULL; + + while (Next()) { + // Check if else is potentically valid + if (token == "else" && !g_CanElse) + ParserError ("else without preceding if"); + if (token != "else") + g_CanElse = false; + + // ============================================================ + if (token == "state") { + MUST_TOPLEVEL + + MustString (); + + // State name must be a word. + if (token.first (" ") != token.len()) + ParserError ("state name must be a single word, got `%s`", token.chars()); + str statename = token; + + // stateSpawn is special - it *must* be defined. If we + // encountered it, then mark down that we have it. + if (-token == "statespawn") + g_stateSpawnDefined = true; + + // Must end in a colon + MustNext (":"); + + // Write the previous state's onenter and + // mainloop buffers to file now + if (g_CurState.len()) + w->WriteBuffers(); + + w->Write (DH_STATENAME); + w->WriteString (statename); + w->Write (DH_STATEIDX); + w->Write (g_NumStates); + + g_NumStates++; + g_CurState = token; + g_GotMainLoop = false; + continue; + } + + // ============================================================ + if (token == "event") { + MUST_TOPLEVEL + + // Event definition + MustString (); + + EventDef* e = FindEventByName (token); + if (!e) + ParserError ("bad event, got `%s`\n", token.chars()); + + MustNext ("{"); + + g_CurMode = MODE_EVENT; + + w->Write (DH_EVENT); + w->Write (e->number); + g_NumEvents++; + continue; + } + + // ============================================================ + if (token == "mainloop") { + MUST_TOPLEVEL + MustNext ("{"); + + // Mode must be set before dataheader is written here! + g_CurMode = MODE_MAINLOOP; + w->Write (DH_MAINLOOP); + continue; + } + + // ============================================================ + if (token == "onenter" || token == "onexit") { + MUST_TOPLEVEL + bool onenter = token == "onenter"; + MustNext ("{"); + + // Mode must be set before dataheader is written here, + // because onenter goes to a separate buffer. + g_CurMode = onenter ? MODE_ONENTER : MODE_ONEXIT; + w->Write (onenter ? DH_ONENTER : DH_ONEXIT); + continue; + } + + // ============================================================ + if (token == "int" || token == "str" || token == "bool") { + // For now, only globals are supported + if (g_CurMode != MODE_TOPLEVEL || g_CurState.len()) + ParserError ("variables must only be global for now"); + + type_e type = (token == "int") ? TYPE_INT : + (token == "str") ? TYPE_STRING : + TYPE_BOOL; + + MustNext (); + + // Var name must not be a number + if (token.isnumber()) + ParserError ("variable name must not be a number"); + + str varname = token; + ScriptVar* var = DeclareGlobalVariable (this, type, varname); + + if (!var) + ParserError ("declaring %s variable %s failed", + g_CurState.len() ? "state" : "global", varname.chars()); + + MustNext (";"); + continue; + } + + // ============================================================ + // Goto + if (token == "goto") { + MUST_NOT_TOPLEVEL + + // Get the name of the label + MustNext (); + + // Find the mark this goto statement points to + unsigned int m = w->FindMark (token); + + // If not set, define it + if (m == MAX_MARKS) { + m = w->AddMark (token); + g_UndefinedLabels[m] = new str (token); + } + + // Add a reference to the mark. + w->Write (DH_GOTO); + w->AddReference (m); + MustNext (";"); + continue; + } + + // ============================================================ + // If + if (token == "if") { + MUST_NOT_TOPLEVEL + PushScope (); + + // Condition + MustNext ("("); + + // Read the expression and write it. + MustNext (); + DataBuffer* c = ParseExpression (TYPE_INT); + w->WriteBuffer (c); + + MustNext (")"); + MustNext ("{"); + + // Add a mark - to here temporarily - and add a reference to it. + // Upon a closing brace, the mark will be adjusted. + unsigned int marknum = w->AddMark (""); + + // Use DH_IFNOTGOTO - if the expression is not true, we goto the mark + // we just defined - and this mark will be at the end of the scope block. + w->Write (DH_IFNOTGOTO); + w->AddReference (marknum); + + // Store it + SCOPE(0).mark1 = marknum; + SCOPE(0).type = SCOPETYPE_IF; + continue; + } + + if (token == "else") { + MUST_NOT_TOPLEVEL + MustNext ("{"); + + // Don't use PushScope as it resets the scope + g_ScopeCursor++; + if (g_ScopeCursor >= MAX_SCOPE) + ParserError ("too deep scope"); + + if (SCOPE(0).type != SCOPETYPE_IF) + ParserError ("else without preceding if"); + + // Write down to jump to the end of the else statement + // Otherwise we have fall-throughs + SCOPE(0).mark2 = w->AddMark (""); + + // Instruction to jump to the end after if block is complete + w->Write (DH_GOTO); + w->AddReference (SCOPE(0).mark2); + + // Move the ifnot mark here and set type to else + w->MoveMark (SCOPE(0).mark1); + SCOPE(0).type = SCOPETYPE_ELSE; + continue; + } + + // ============================================================ + // While + if (token == "while") { + MUST_NOT_TOPLEVEL + PushScope (); + + // While loops need two marks - one at the start of the loop and one at the + // end. The condition is checked at the very start of the loop, if it fails, + // we use goto to skip to the end of the loop. At the end, we loop back to + // the beginning with a go-to statement. + unsigned int mark1 = w->AddMark (""); // start + unsigned int mark2 = w->AddMark (""); // end + + // Condition + MustNext ("("); + MustNext (); + DataBuffer* expr = ParseExpression (TYPE_INT); + MustNext (")"); + MustNext ("{"); + + // Write condition + w->WriteBuffer (expr); + + // Instruction to go to the end if it fails + w->Write (DH_IFNOTGOTO); + w->AddReference (mark2); + + // Store the needed stuff + SCOPE(0).mark1 = mark1; + SCOPE(0).mark2 = mark2; + SCOPE(0).type = SCOPETYPE_WHILE; + continue; + } + + // ============================================================ + // For loop + if (token == "for") { + MUST_NOT_TOPLEVEL + PushScope (); + + // Initializer + MustNext ("("); + MustNext (); + DataBuffer* init = ParseStatement (w); + if (!init) + ParserError ("bad statement for initializer of for"); + + MustNext (";"); + + // Condition + MustNext (); + DataBuffer* cond = ParseExpression (TYPE_INT); + if (!cond) + ParserError ("bad statement for condition of for"); + + MustNext (";"); + + // Incrementor + MustNext (); + DataBuffer* incr = ParseStatement (w); + if (!incr) + ParserError ("bad statement for incrementor of for"); + + MustNext (")"); + MustNext ("{"); + + // First, write out the initializer + w->WriteBuffer (init); + + // Init two marks + int mark1 = w->AddMark (""); + int mark2 = w->AddMark (""); + + // Add the condition + w->WriteBuffer (cond); + w->Write (DH_IFNOTGOTO); + w->AddReference (mark2); + + // Store the marks and incrementor + SCOPE(0).mark1 = mark1; + SCOPE(0).mark2 = mark2; + SCOPE(0).buffer1 = incr; + SCOPE(0).type = SCOPETYPE_FOR; + continue; + } + + // ============================================================ + // Do/while loop + if (token == "do") { + MUST_NOT_TOPLEVEL + PushScope (); + MustNext ("{"); + SCOPE(0).mark1 = w->AddMark (""); + SCOPE(0).type = SCOPETYPE_DO; + continue; + } + + // ============================================================ + // Switch + if (token == "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 + PushScope (); + MustNext ("("); + MustNext (); + w->WriteBuffer (ParseExpression (TYPE_INT)); + MustNext (")"); + MustNext ("{"); + SCOPE(0).type = SCOPETYPE_SWITCH; + SCOPE(0).mark1 = w->AddMark (""); // end mark + SCOPE(0).buffer1 = NULL; // default header + continue; + } + + // ============================================================ + if (token == "case") { + // case is only allowed inside switch + if (SCOPE(0).type != SCOPETYPE_SWITCH) + ParserError ("case label outside switch"); + + // Get the literal (Zandronum does not support expressions here) + MustNumber (); + int num = atoi (token.chars ()); + MustNext (":"); + + for (int i = 0; i < MAX_CASE; i++) + if (SCOPE(0).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. + // 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 (DH_CASEGOTO); + w->Write (num); + AddSwitchCase (w, NULL); + SCOPE(0).casenumbers[SCOPE(0).casecursor] = num; + continue; + } + + if (token == "default") { + if (SCOPE(0).type != SCOPETYPE_SWITCH) + ParserError ("default label outside switch"); + + if (SCOPE(0).buffer1) + ParserError ("multiple default labels in one switch"); + + 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; + SCOPE(0).buffer1 = b; + b->Write (DH_DROP); + b->Write (DH_GOTO); + AddSwitchCase (w, b); + continue; + } + + // ============================================================ + // Break statement. + if (token == "break") { + if (!g_ScopeCursor) + ParserError ("unexpected `break`"); + + w->Write (DH_GOTO); + + // switch and if use mark1 for the closing point, + // for and while use mark2. + switch (SCOPE(0).type) { + case SCOPETYPE_IF: + case SCOPETYPE_SWITCH: + w->AddReference (SCOPE(0).mark1); + break; + case SCOPETYPE_FOR: + case SCOPETYPE_WHILE: + w->AddReference (SCOPE(0).mark2); + break; + default: + ParserError ("unexpected `break`"); + break; + } + + MustNext (";"); + continue; + } + + // ============================================================ + // Continue + if (token == "continue") { + MustNext (";"); + + int curs; + bool found = false; + + // Drop through the scope until we find a loop block + for (curs = g_ScopeCursor; curs > 0 && !found; curs--) { + switch (scopestack[curs].type) { + case SCOPETYPE_FOR: + case SCOPETYPE_WHILE: + case SCOPETYPE_DO: + w->Write (DH_GOTO); + w->AddReference (scopestack[curs].mark1); + found = true; + break; + default: + break; + } + } + + // No loop blocks + if (!found) + ParserError ("`continue`-statement not inside a loop"); + + continue; + } + + // ============================================================ + // Label + if (PeekNext() == ":") { + MUST_NOT_TOPLEVEL + + // want no conflicts.. + 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()); + + // See if a mark already exists for this label + int mark = -1; + for (int i = 0; i < MAX_MARKS; i++) { + if (g_UndefinedLabels[i] && *g_UndefinedLabels[i] == token) { + mark = i; + w->MoveMark (i); + + // No longer undefinde + delete g_UndefinedLabels[i]; + g_UndefinedLabels[i] = NULL; + } + } + + // Not found in unmarked lists, define it now + if (mark == -1) + w->AddMark (token); + + MustNext (":"); + continue; + } + + // ============================================================ + if (token == "const") { + constinfo_t info; + + // Get the type + MustNext (); + info.type = GetTypeByName (token); + + if (info.type == TYPE_UNKNOWN || info.type == TYPE_VOID) + ParserError ("unknown type `%s` for constant", (char*)token); + + MustNext (); + info.name = token; + + MustNext ("="); + + switch (info.type) { + case TYPE_BOOL: + case TYPE_INT: + MustNumber (false); + info.val = token; + break; + case TYPE_STRING: + MustString (); + info.val = token; + break; + case TYPE_UNKNOWN: + case TYPE_VOID: + break; + } + + g_ConstInfo << info; + + MustNext (";"); + continue; + } + + // ============================================================ + if (token == "}") { + // Closing brace + + // If we're in the block stack, we're descending down from it now + if (g_ScopeCursor > 0) { + switch (SCOPE(0).type) { + case SCOPETYPE_IF: + // Adjust the closing mark. + w->MoveMark (SCOPE(0).mark1); + + // We're returning from if, thus else can be next + g_CanElse = true; + break; + case SCOPETYPE_ELSE: + // else instead uses mark1 for itself (so if expression + // fails, jump to else), mark2 means end of else + w->MoveMark (SCOPE(0).mark2); + break; + case SCOPETYPE_FOR: + // Write the incrementor at the end of the loop block + w->WriteBuffer (SCOPE(0).buffer1); + // fall-thru + case SCOPETYPE_WHILE: + // Write down the instruction to go back to the start of the loop + w->Write (DH_GOTO); + w->AddReference (SCOPE(0).mark1); + + // Move the closing mark here since we're at the end of the while loop + w->MoveMark (SCOPE(0).mark2); + break; + case SCOPETYPE_DO: { + MustNext ("while"); + MustNext ("("); + MustNext (); + DataBuffer* expr = ParseExpression (TYPE_INT); + MustNext (")"); + MustNext (";"); + + // If the condition runs true, go back to the start. + w->WriteBuffer (expr); + w->Write (DH_IFGOTO); + w->AddReference (SCOPE(0).mark1); + break; + } + case SCOPETYPE_SWITCH: { + // Switch closes. Move down to the record buffer of + // the lower block. + if (SCOPE(1).casecursor != -1) + w->SwitchBuffer = SCOPE(1).casebuffers[SCOPE(1).casecursor]; + 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 (SCOPE(0).buffer1) + w->WriteBuffer (SCOPE(0).buffer1); + else { + w->Write (DH_DROP); + w->Write (DH_GOTO); + w->AddReference (SCOPE(0).mark1); + } + + // Go through all of the buffers we + // recorded down and write them. + for (unsigned int u = 0; u < MAX_CASE; u++) { + if (!SCOPE(0).casebuffers[u]) + continue; + + w->MoveMark (SCOPE(0).casemarks[u]); + w->WriteBuffer (SCOPE(0).casebuffers[u]); + } + + // Move the closing mark here + w->MoveMark (SCOPE(0).mark1); + break; + } + case SCOPETYPE_UNKNOWN: + break; + } + + // Descend down the stack + g_ScopeCursor--; + continue; + } + + int dataheader = (g_CurMode == MODE_EVENT) ? DH_ENDEVENT : + (g_CurMode == MODE_MAINLOOP) ? DH_ENDMAINLOOP : + (g_CurMode == MODE_ONENTER) ? DH_ENDONENTER : + (g_CurMode == MODE_ONEXIT) ? DH_ENDONEXIT : -1; + + if (dataheader == -1) + ParserError ("unexpected `}`"); + + // Data header must be written before mode is changed because + // onenter and mainloop go into special buffers, and we want + // the closing data headers into said buffers too. + w->Write (dataheader); + g_CurMode = MODE_TOPLEVEL; + + if (PeekNext() == ";") + MustNext (";"); + continue; + } + + // Check if it's a command + CommandDef* comm = FindCommand (token); + if (comm) { + w->GetCurrentBuffer()->Merge (ParseCommand (comm)); + MustNext (";"); + continue; + } + + // ============================================================ + // If nothing else, parse it as a statement + DataBuffer* b = ParseStatement (w); + if (!b) + ParserError ("unknown token `%s`", token.chars()); + + w->WriteBuffer (b); + MustNext (";"); + } + + // =============================================================================== + // Script file ended. Do some last checks and write the last things to main buffer + if (g_CurMode != MODE_TOPLEVEL) + ParserError ("script did not end at top level; did you forget a `}`?"); + + // stateSpawn must be defined! + if (!g_stateSpawnDefined) + ParserError ("script must have a state named `stateSpawn`!"); + + for (int i = 0; i < MAX_MARKS; i++) + if (g_UndefinedLabels[i]) + ParserError ("label `%s` is referenced via `goto` but isn't defined\n", g_UndefinedLabels[i]->chars()); + + // Dump the last state's onenter and mainloop + w->WriteBuffers (); + + // String table + w->WriteStringTable (); +} + +// ============================================================================ +// Parses a command call +DataBuffer* ScriptReader::ParseCommand (CommandDef* comm) { + DataBuffer* r = new DataBuffer(64); + if (g_CurMode == MODE_TOPLEVEL) + ParserError ("command call at top level"); + + MustNext ("("); + MustNext (); + + int curarg = 0; + while (1) { + if (token == ")") { + if (curarg < comm->numargs) + ParserError ("too few arguments passed to %s\n\tprototype: %s", + comm->name.chars(), GetCommandPrototype (comm).chars()); + break; + curarg++; + } + + if (curarg >= comm->maxargs) + ParserError ("too many arguments passed to %s\n\tprototype: %s", + comm->name.chars(), GetCommandPrototype (comm).chars()); + + r->Merge (ParseExpression (comm->argtypes[curarg])); + MustNext (); + + if (curarg < comm->numargs - 1) { + MustThis (","); + MustNext (); + } else if (curarg < comm->maxargs - 1) { + // Can continue, but can terminate as well. + if (token == ")") { + curarg++; + break; + } else { + MustThis (","); + MustNext (); + } + } + + curarg++; + } + + // If the script skipped any optional arguments, fill in defaults. + while (curarg < comm->maxargs) { + r->Write (DH_PUSHNUMBER); + r->Write (comm->defvals[curarg]); + curarg++; + } + + r->Write (DH_COMMAND); + r->Write (comm->number); + r->Write (comm->maxargs); + + return r; +} + +// ============================================================================ +// Is the given operator an assignment operator? +static bool IsAssignmentOperator (int oper) { + switch (oper) { + case OPER_ASSIGNADD: + case OPER_ASSIGNSUB: + case OPER_ASSIGNMUL: + case OPER_ASSIGNDIV: + case OPER_ASSIGNMOD: + case OPER_ASSIGNLEFTSHIFT: + case OPER_ASSIGNRIGHTSHIFT: + case OPER_ASSIGN: + return true; + } + return false; +} + +// ============================================================================ +// Finds an operator's corresponding dataheader +static word 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 + // OPER_ASSIGNLEFTSHIFT and OPER_ASSIGNRIGHTSHIFT do not + // have data headers, instead they are expanded out in + // the operator parser + 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; + case OPER_EQUALS: return DH_EQUALS; + case OPER_NOTEQUALS: return DH_NOTEQUALS; + case OPER_LESSTHAN: return DH_LESSTHAN; + case OPER_GREATERTHAN: return DH_GREATERTHAN; + case OPER_LESSTHANEQUALS: return DH_LESSTHANEQUALS; + case OPER_GREATERTHANEQUALS: return DH_GREATERTHANEQUALS; + case OPER_LEFTSHIFT: return DH_LSHIFT; + case OPER_RIGHTSHIFT: return DH_RSHIFT; + case OPER_OR: return DH_ORLOGICAL; + case OPER_AND: return DH_ANDLOGICAL; + case OPER_BITWISEOR: return DH_ORBITWISE; + case OPER_BITWISEEOR: return DH_EORBITWISE; + case OPER_BITWISEAND: return DH_ANDBITWISE; + } + + error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper); + return 0; +} + +// ============================================================================ +// Parses an expression, potentially recursively +DataBuffer* ScriptReader::ParseExpression (type_e reqtype) { + DataBuffer* retbuf = new DataBuffer (64); + + // Parse first operand + retbuf->Merge (ParseExprValue (reqtype)); + + // Parse any and all operators we get + int oper; + while ((oper = ParseOperator (true)) != -1) { + // We peeked the operator, move forward now + Next (); + + // Can't be an assignement operator, those belong in assignments. + if (IsAssignmentOperator (oper)) + ParserError ("assignment operator inside expression"); + + // Parse the right operand. + MustNext (); + DataBuffer* rb = ParseExprValue (reqtype); + + if (oper == OPER_TERNARY) { + // Ternary operator requires - naturally - a third operand. + MustNext (":"); + MustNext (); + DataBuffer* tb = ParseExprValue (reqtype); + + // It also is handled differently: there isn't a dataheader for ternary + // operator. Instead, we abuse PUSHNUMBER and IFNOTGOTO for this. + // Behold, big block of writing madness! :P + int mark1 = retbuf->AddMark (""); // start of "else" case + int mark2 = retbuf->AddMark (""); // end of expression + retbuf->Write (DH_IFNOTGOTO); // if the first operand (condition) + retbuf->AddMarkReference (mark1); // didn't eval true, jump into mark1 + retbuf->Merge (rb); // otherwise, perform second operand (true case) + retbuf->Write (DH_GOTO); // afterwards, jump to the end, which is + retbuf->AddMarkReference (mark2); // marked by mark2. + retbuf->MoveMark (mark1); // move mark1 at the end of the true case + retbuf->Merge (tb); // perform third operand (false case) + retbuf->MoveMark (mark2); // move the ending mark2 here + } else { + // Write to buffer + retbuf->Merge (rb); + retbuf->Write (DataHeaderByOperator (NULL, oper)); + } + } + + return retbuf; +} + +// ============================================================================ +// Parses an operator string. Returns the operator number code. +#define ISNEXT(char) (!PeekNext (peek ? 1 : 0) == char) +int ScriptReader::ParseOperator (bool peek) { + str oper; + if (peek) + oper += PeekNext (); + else + oper += token; + + if (-oper == "strlen") + return OPER_STRLEN; + + // Check one-char operators + bool equalsnext = ISNEXT ("="); + + int o = (oper == "=" && !equalsnext) ? OPER_ASSIGN : + (oper == ">" && !equalsnext && !ISNEXT (">")) ? OPER_GREATERTHAN : + (oper == "<" && !equalsnext && !ISNEXT ("<")) ? OPER_LESSTHAN : + (oper == "&" && !ISNEXT ("&")) ? OPER_BITWISEAND : + (oper == "|" && !ISNEXT ("|")) ? OPER_BITWISEOR : + (oper == "+" && !equalsnext) ? OPER_ADD : + (oper == "-" && !equalsnext) ? OPER_SUBTRACT : + (oper == "*" && !equalsnext) ? OPER_MULTIPLY : + (oper == "/" && !equalsnext) ? OPER_DIVIDE : + (oper == "%" && !equalsnext) ? OPER_MODULUS : + (oper == "^") ? OPER_BITWISEEOR : + (oper == "?") ? OPER_TERNARY : + -1; + + if (o != -1) { + return o; + } + + // Two-char operators + oper += PeekNext (peek ? 1 : 0); + equalsnext = PeekNext (peek ? 2 : 1) == ("="); + + o = (oper == "+=") ? OPER_ASSIGNADD : + (oper == "-=") ? OPER_ASSIGNSUB : + (oper == "*=") ? OPER_ASSIGNMUL : + (oper == "/=") ? OPER_ASSIGNDIV : + (oper == "%=") ? OPER_ASSIGNMOD : + (oper == "==") ? OPER_EQUALS : + (oper == "!=") ? OPER_NOTEQUALS : + (oper == ">=") ? OPER_GREATERTHANEQUALS : + (oper == "<=") ? OPER_LESSTHANEQUALS : + (oper == "&&") ? OPER_AND : + (oper == "||") ? OPER_OR : + (oper == "<<" && !equalsnext) ? OPER_LEFTSHIFT : + (oper == ">>" && !equalsnext) ? OPER_RIGHTSHIFT : + -1; + + if (o != -1) { + MustNext (); + return o; + } + + // Three-char opers + oper += PeekNext (peek ? 2 : 1); + o = oper == "<<=" ? OPER_ASSIGNLEFTSHIFT : + oper == ">>=" ? OPER_ASSIGNRIGHTSHIFT : + -1; + + if (o != -1) { + MustNext (); + MustNext (); + } + + return o; +} + +// ============================================================================ +str ScriptReader::ParseFloat () { + MustNumber (true); + str floatstring = token; + + // Go after the decimal point + if (PeekNext () == ".") { + Next ("."); + MustNumber (false); + floatstring += "."; + floatstring += token; + } + + return floatstring; +} + +// ============================================================================ +// 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 (type_e reqtype) { + DataBuffer* b = new DataBuffer(16); + + ScriptVar* g; + + // Prefixing "!" means negation. + bool negate = (token == "!"); + if (negate) // Jump past the "!" + Next (); + + // Handle strlen + if (token == "strlen") { + MustNext ("("); + MustNext (); + + // By this token we should get a string constant. + constinfo_t* constant = FindConstant (token); + if (!constant || constant->type != TYPE_STRING) + ParserError ("strlen only works with const str"); + + if (reqtype != TYPE_INT) + ParserError ("strlen returns int but %s is expected\n", (char*)GetTypeName (reqtype)); + + b->Write (DH_PUSHNUMBER); + b->Write (constant->val.len ()); + + MustNext (")"); + } else if (token == "(") { + // Expression + MustNext (); + DataBuffer* c = ParseExpression (reqtype); + b->Merge (c); + MustNext (")"); + } else if (CommandDef* comm = FindCommand (token)) { + delete b; + + // Command + if (reqtype && comm->returnvalue != reqtype) + ParserError ("%s returns an incompatible data type", comm->name.chars()); + b = ParseCommand (comm); + } else if (constinfo_t* constant = FindConstant (token)) { + // Type check + if (reqtype != constant->type) + ParserError ("constant `%s` is %s, expression requires %s\n", + (char*)constant->name, (char*)GetTypeName (constant->type), + (char*)GetTypeName (reqtype)); + + switch (constant->type) { + case TYPE_BOOL: + case TYPE_INT: + b->Write (DH_PUSHNUMBER); + b->Write (atoi (constant->val)); + break; + case TYPE_STRING: + b->WriteString (constant->val); + break; + case TYPE_VOID: + case TYPE_UNKNOWN: + break; + } + } else if ((g = FindGlobalVariable (token))) { + // Global variable + b->Write (DH_PUSHGLOBALVAR); + b->Write (g->index); + } else { + // If nothing else, check for literal + switch (reqtype) { + case TYPE_VOID: + case TYPE_UNKNOWN: + ParserError ("unknown identifier `%s` (expected keyword, function or variable)", token.chars()); + break; + case TYPE_BOOL: + case TYPE_INT: { + MustNumber (true); + + // All values are written unsigned - thus we need to write the value's + // absolute value, followed by an unary minus for negatives. + b->Write (DH_PUSHNUMBER); + + long v = atol (token); + b->Write (static_cast<word> (abs (v))); + if (v < 0) + b->Write (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. + MustString (true); + b->WriteString (token); + break; + } + } + + // Negate it now if desired + if (negate) + b->Write (DH_NEGATELOGICAL); + + return b; +} + +// ============================================================================ +// Parses an assignment. An assignment starts with a variable name, followed +// 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) { + bool global = !var->statename.len (); + + // Get an operator + MustNext (); + int oper = ParseOperator (); + if (!IsAssignmentOperator (oper)) + ParserError ("expected assignment operator"); + + if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction + ParserError ("can't alter variables at top level"); + + // Parse the right operand + MustNext (); + DataBuffer* retbuf = new DataBuffer; + DataBuffer* expr = ParseExpression (var->type); + + // <<= and >>= do not have data headers. Solution: expand them. + // a <<= b -> a = a << b + // a >>= b -> a = a >> b + if (oper == OPER_ASSIGNLEFTSHIFT || oper == OPER_ASSIGNRIGHTSHIFT) { + retbuf->Write (global ? DH_PUSHGLOBALVAR : DH_PUSHLOCALVAR); + retbuf->Write (var->index); + retbuf->Merge (expr); + retbuf->Write ((oper == OPER_ASSIGNLEFTSHIFT) ? DH_LSHIFT : DH_RSHIFT); + retbuf->Write (global ? DH_ASSIGNGLOBALVAR : DH_ASSIGNLOCALVAR); + retbuf->Write (var->index); + } else { + retbuf->Merge (expr); + long dh = DataHeaderByOperator (var, oper); + retbuf->Write (dh); + retbuf->Write (var->index); + } + + return retbuf; +} + +void ScriptReader::PushScope () { + g_ScopeCursor++; + if (g_ScopeCursor >= MAX_SCOPE) + ParserError ("too deep scope"); + + ScopeInfo* info = &SCOPE(0); + info->type = SCOPETYPE_UNKNOWN; + 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; + info->casenumbers[i] = -1; + } +} + +DataBuffer* ScriptReader::ParseStatement (ObjWriter* w) { + if (FindConstant (token)) // There should not be constants here. + ParserError ("invalid use for constant\n"); + + // If it's a variable, expect assignment. + if (ScriptVar* var = FindGlobalVariable (token)) + return ParseAssignment (var); + + return NULL; +} + +void ScriptReader::AddSwitchCase (ObjWriter* w, DataBuffer* b) { + ScopeInfo* info = &SCOPE(0); + + 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; +} + +constinfo_t* FindConstant (str token) { + for (uint i = 0; i < g_ConstInfo.size(); i++) + if (g_ConstInfo[i].name == token) + return &g_ConstInfo[i]; + return NULL; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/preprocessor.cxx Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,126 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define __PARSER_CXX__ + +#include <stdio.h> +#include <stdlib.h> +#include "common.h" +#include "str.h" +#include "scriptreader.h" + +/* Since the preprocessor is *called* from ReadChar and I don't want + * to worry about recursive preprocessing, the preprocessor uses its + * own bare-bones variant of the function for file reading. + */ +char ScriptReader::PPReadChar () { + char c; + if (!fread (&c, sizeof (char), 1, fp[fc])) + return 0; + curchar[fc]++; + return c; +} + +void ScriptReader::PPMustChar (char c) { + char d = PPReadChar (); + if (c != d) + ParserError ("expected `%c`, got `%d`", c, d); +} + +// ============================================================================ +// Reads a word until whitespace +str ScriptReader::PPReadWord (char &term) { + str word; + while (1) { + char c = PPReadChar(); + if (feof (fp[fc]) || (IsCharWhitespace (c) && word.len ())) { + term = c; + break; + } + word += c; + } + return word; +} + +// ============================================================================ +// Preprocess any directives found in the script file +void ScriptReader::PreprocessDirectives () { + size_t spos = ftell (fp[fc]); + if (!DoDirectivePreprocessing ()) + fseek (fp[fc], spos, SEEK_SET); +} + +/* ============================================================================ + * Returns true if the pre-processing was successful, false if not. + * If pre-processing was successful, the file pointer remains where + * it was, if not, it's pushed back to where it was before preprocessing + * took place and is parsed normally. + */ +bool ScriptReader::DoDirectivePreprocessing () { + char trash; + // Directives start with a pound sign + if (PPReadChar() != '#') + return false; + + // Read characters until next whitespace to + // build the name of the directive + str directive = PPReadWord (trash); + + // Now check the directive name against known names + if (directive == "include") { + // #include-directive + char terminator; + str file = PPReadWord (terminator); + + if (!file.len()) + ParserError ("expected file name for #include, got nothing instead"); + OpenFile (file); + return true; + } else if (directive == "neurosphere") { + // #neurosphere - activates neurosphere compatibility, aka stuff + // that is still WIP and what main zandronum does not yet support. + // Most end users should never need this. + g_Neurosphere = true; + return true; + } + + ParserError ("unknown directive `#%s`!", directive.chars()); + return false; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scriptreader.cxx Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,412 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include "string.h" +#include "str.h" +#include "common.h" +#include "scriptreader.h" + +#define STORE_POSITION \ + bool _atnewline = atnewline; \ + unsigned int _curline = curline[fc]; \ + unsigned int _curchar = curchar[fc]; + +#define RESTORE_POSITION \ + atnewline = _atnewline; \ + curline[fc] = _curline; \ + curchar[fc] = _curchar; + +// ============================================================================ +ScriptReader::ScriptReader (str path) { + token = ""; + prevtoken = ""; + prevpos = 0; + fc = -1; + + for (unsigned int u = 0; u < MAX_FILESTACK; u++) + fp[u] = NULL; + + OpenFile (path); + commentmode = 0; +} + +// ============================================================================ +ScriptReader::~ScriptReader () { + // If comment mode is 2 by the time the file ended, the + // comment was left unterminated. 1 is no problem, since + // it's terminated by newlines anyway. + if (commentmode == 2) + ParserError ("unterminated `/*`-style comment"); + + for (unsigned int u = 0; u < MAX_FILESTACK; u++) { + if (fp[u]) { + ParserWarning ("file idx %u remained open after parsing", u); + CloseFile (u); + } + } +} + +// ============================================================================ +// 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?", + path.chars()); + + // Save the position first. + if (fc != -1) { + savedpos[fc] = ftell (fp[fc]); + } + + fc++; + + fp[fc] = fopen (path, "r"); + if (!fp[fc]) { + ParserError ("couldn't open %s for reading!\n", path.chars ()); + exit (1); + } + + fseek (fp[fc], 0, SEEK_SET); + filepath[fc] = path.chars(); + curline[fc] = 1; + curchar[fc] = 1; + pos[fc] = 0; + atnewline = 0; +} + +// ============================================================================ +// Closes the current file +void ScriptReader::CloseFile (unsigned int u) { + if (u >= MAX_FILESTACK) + u = fc; + + if (!fp[u]) + return; + + fclose (fp[u]); + fp[u] = NULL; + fc--; + + if (fc != -1) + fseek (fp[fc], savedpos[fc], SEEK_SET); +} + +// ============================================================================ +char ScriptReader::ReadChar () { + if (feof (fp[fc])) + return 0; + + 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 + if (atnewline) { + atnewline = false; + curline[fc]++; + curchar[fc] = 0; // gets incremented to 1 + } + + if (c == '\n') { + atnewline = true; + + // Check for pre-processor directives + PreprocessDirectives (); + } + + curchar[fc]++; + return c; +} + +// ============================================================================ +// Peeks the next character +char ScriptReader::PeekChar (int offset) { + // Store current position + long curpos = ftell (fp[fc]); + STORE_POSITION + + // Forward by offset + fseek (fp[fc], offset, SEEK_CUR); + + // Read the character + char* c = (char*)malloc (sizeof (char)); + + if (!fread (c, sizeof (char), 1, fp[fc])) { + fseek (fp[fc], curpos, SEEK_SET); + return 0; + } + + // Rewind back + fseek (fp[fc], curpos, SEEK_SET); + RESTORE_POSITION + + return c[0]; +} + +// ============================================================================ +// 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 = ""; + + while (1) { + // Check end-of-file + if (feof (fp[fc])) { + // If we're just peeking, we shouldn't + // actually close anything.. + if (peek) + break; + + CloseFile (); + if (fc == -1) + break; + } + + // Check if the next token possibly starts a comment. + if (PeekChar () == '/' && !tmp.len()) { + char c2 = PeekChar (1); + // C++-style comment + if (c2 == '/') + commentmode = 1; + else if (c2 == '*') + commentmode = 2; + + // We don't need to actually read in the + // comment characters, since they will get + // ignored due to comment mode anyway. + } + + c = ReadChar (); + + // If this is a comment we're reading, check if this character + // gets the comment terminated, otherwise ignore it. + if (commentmode > 0) { + if (commentmode == 1 && c == '\n') { + // C++-style comments are terminated by a newline + commentmode = 0; + continue; + } else if (commentmode == 2 && c == '*') { + // C-style comments are terminated by a `*/` + if (PeekChar() == '/') { + commentmode = 0; + ReadChar (); + } + } + + // Otherwise, ignore it. + continue; + } + + // Non-alphanumber characters (sans underscore) break the word too. + // If there was prior data, the delimeter pushes the cursor back so + // that the next character will be the same delimeter. If there isn't, + // the delimeter itself is included (and thus becomes a token itself.) + if ((c >= 33 && c <= 47) || + (c >= 58 && c <= 64) || + (c >= 91 && c <= 96 && c != '_') || + (c >= 123 && c <= 126)) { + if (tmp.len()) + fseek (fp[fc], ftell (fp[fc]) - 1, SEEK_SET); + else + tmp += c; + break; + } + + if (IsCharWhitespace (c)) { + // Don't break if we haven't gathered anything yet. + if (tmp.len()) + break; + } else { + tmp += c; + } + } + + // If we got nothing here, read failed. This should + // only happen in the case of EOF. + if (!tmp.len()) { + token = ""; + return false; + } + + pos[fc]++; + prevtoken = token; + token = tmp; + return true; +} + +// ============================================================================ +// Returns the next token without advancing the cursor. +str ScriptReader::PeekNext (int offset) { + // Store current information + str storedtoken = token; + int cpos = ftell (fp[fc]); + STORE_POSITION + + // Advance on the token. + while (offset >= 0) { + if (!Next (true)) + return ""; + offset--; + } + + str tmp = token; + + // Restore position + fseek (fp[fc], cpos, SEEK_SET); + pos[fc]--; + token = storedtoken; + RESTORE_POSITION + return tmp; +} + +// ============================================================================ +void ScriptReader::Seek (unsigned int n, int origin) { + switch (origin) { + case SEEK_SET: + fseek (fp[fc], 0, SEEK_SET); + pos[fc] = 0; + break; + case SEEK_CUR: + break; + case SEEK_END: + printf ("ScriptReader::Seek: SEEK_END not yet supported.\n"); + break; + } + + for (unsigned int i = 0; i < n+1; i++) + Next(); +} + +// ============================================================================ +void ScriptReader::MustNext (const char* c) { + if (!Next()) { + if (strlen (c)) + ParserError ("expected `%s`, reached end of file instead\n", c); + else + ParserError ("expected a token, reached end of file instead\n"); + } + + if (strlen (c)) + MustThis (c); +} + +// ============================================================================ +void ScriptReader::MustThis (const char* c) { + if (token != c) + ParserError ("expected `%s`, got `%s` instead", c, token.chars()); +} + +// ============================================================================ +void ScriptReader::ParserError (const char* message, ...) { + PERFORM_FORMAT (message, outmessage); + ParserMessage ("\nError: ", outmessage); + exit (1); +} + +// ============================================================================ +void ScriptReader::ParserWarning (const char* message, ...) { + PERFORM_FORMAT (message, outmessage); + ParserMessage ("Warning: ", outmessage); +} + +// ============================================================================ +void ScriptReader::ParserMessage (const char* header, char* message) { + if (fc >= 0 && fc < MAX_FILESTACK) + fprintf (stderr, "%s%s:%u:%u: %s\n", + header, filepath[fc], curline[fc], curchar[fc], message); + else + fprintf (stderr, "%s%s\n", header, message); +} + +// ============================================================================ +// 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. + while (1) { + // can't end here! + if (feof (fp[fc])) + ParserError ("unterminated string"); + + char c = ReadChar (); + if (c == '"') + break; + + string += c; + } + + token = string; +} + +// ============================================================================ +void ScriptReader::MustNumber (bool fromthis) { + if (!fromthis) + MustNext (); + + str num = token; + if (num == "-") { + MustNext (); + num += token; + } + + // "true" and "false" are valid numbers + if (!token.icompare ("true")) + token = "1"; + else if (!token.icompare ("false")) + token = "0"; + else { + if (!token.isnumber()) + ParserError ("expected a number, got `%s`", num.chars()); + + str check; + check.appendformat ("%d", atoi (num)); + if (token != check) + ParserWarning ("integer too large: %s -> %s", num.chars(), check.chars()); + + token = num; + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scriptreader.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,211 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SCRIPTREADER_H__ +#define __SCRIPTREADER_H__ + +#include <stdio.h> +#include "str.h" +#include "commands.h" +#include "objwriter.h" + +#define MAX_FILESTACK 8 +#define MAX_SCOPE 32 +#define MAX_CASE 64 + +class ScriptVar; + +// Operators +enum operator_e { + OPER_ADD, + OPER_SUBTRACT, + OPER_MULTIPLY, + OPER_DIVIDE, + OPER_MODULUS, + OPER_ASSIGN, + OPER_ASSIGNADD, + OPER_ASSIGNSUB, + OPER_ASSIGNMUL, + OPER_ASSIGNDIV, + OPER_ASSIGNMOD, // -- 10 + OPER_EQUALS, + OPER_NOTEQUALS, + OPER_LESSTHAN, + OPER_GREATERTHAN, + OPER_LESSTHANEQUALS, + OPER_GREATERTHANEQUALS, + OPER_LEFTSHIFT, + OPER_RIGHTSHIFT, + OPER_ASSIGNLEFTSHIFT, + OPER_ASSIGNRIGHTSHIFT, // -- 20 + OPER_OR, + OPER_AND, + OPER_BITWISEOR, + OPER_BITWISEAND, + OPER_BITWISEEOR, + OPER_TERNARY, + OPER_STRLEN, +}; + +// Mark types +enum marktype_e { + MARKTYPE_LABEL, + MARKTYPE_IF, + MARKTYPE_INTERNAL, // internal structures +}; + +// Block types +enum scopetype_e { + SCOPETYPE_UNKNOWN, + SCOPETYPE_IF, + SCOPETYPE_WHILE, + SCOPETYPE_FOR, + SCOPETYPE_DO, + SCOPETYPE_SWITCH, + SCOPETYPE_ELSE, +}; + +// ============================================================================ +// Meta-data about blocks +struct ScopeInfo { + unsigned int mark1; + unsigned int mark2; + scopetype_e type; + DataBuffer* buffer1; + + // switch-related stuff + // Which case are we at? + short casecursor; + + // Marks to case-blocks + int casemarks[MAX_CASE]; + + // Numbers of the case labels + int casenumbers[MAX_CASE]; + + // actual case blocks + DataBuffer* casebuffers[MAX_CASE]; + + // What is the current buffer of the block? + DataBuffer* recordbuffer; +}; + +// ============================================================================ +typedef struct { + str name; + type_e type; + str val; +} constinfo_t; + +// ============================================================================ +// The script reader reads the script, parses it and tells the object writer +// the bytecode it needs to write to file. +class ScriptReader { +public: + // ==================================================================== + // MEMBERS + FILE* fp[MAX_FILESTACK]; + char* filepath[MAX_FILESTACK]; + int fc; + + unsigned int pos[MAX_FILESTACK]; + unsigned int curline[MAX_FILESTACK]; + unsigned int curchar[MAX_FILESTACK]; + ScopeInfo scopestack[MAX_SCOPE]; + long savedpos[MAX_FILESTACK]; // filepointer cursor position + str token; + int commentmode; + long prevpos; + str prevtoken; + + // ==================================================================== + // METHODS + // scriptreader.cxx: + ScriptReader (str path); + ~ScriptReader (); + void OpenFile (str path); + void CloseFile (unsigned int u = MAX_FILESTACK); + char ReadChar (); + char PeekChar (int offset = 0); + bool Next (bool peek = false); + void Prev (); + str PeekNext (int offset = 0); + void Seek (unsigned int n, int origin); + void MustNext (const char* c = ""); + void MustThis (const char* c); + void MustString (bool gotquote = false); + void MustNumber (bool fromthis = false); + void MustBool (); + bool BoolValue (); + + void ParserError (const char* message, ...); + void ParserWarning (const char* message, ...); + + // parser.cxx: + void ParseBotScript (ObjWriter* w); + DataBuffer* ParseCommand (CommandDef* comm); + DataBuffer* ParseExpression (type_e reqtype); + DataBuffer* ParseAssignment (ScriptVar* var); + int ParseOperator (bool peek = false); + DataBuffer* ParseExprValue (type_e reqtype); + str ParseFloat (); + void PushScope (); + + // preprocessor.cxx: + void PreprocessDirectives (); + void PreprocessMacros (); + DataBuffer* ParseStatement (ObjWriter* w); + void AddSwitchCase (ObjWriter* w, DataBuffer* b); + +private: + bool atnewline; + char c; + void ParserMessage (const char* header, char* message); + + bool DoDirectivePreprocessing (); + char PPReadChar (); + void PPMustChar (char c); + str PPReadWord (char &term); +}; + +constinfo_t* FindConstant (str token); +extern bool g_Neurosphere; + +#endif // __SCRIPTREADER_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/str.cxx Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,426 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include "array.h" +#include "str.h" +#include "common.h" + +#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 +// that fails, double the size and keep recursing until it works. +char* vdynformat (const char* c, va_list v, unsigned int size) { + char* buffer = new char[size]; + int r = vsnprintf (buffer, size-1, c, v); + if (r > (int)size-1 || r < 0) { + delete buffer; + buffer = vdynformat (c, v, size*2); + } + return buffer; +} + +// ============================================================================ +str::str () { + text = new char[1]; + clear(); + alloclen = strlen (text); +} + +str::str (const char* c) { + text = new char[1]; + clear (); + alloclen = strlen (text); + append (c); +} + +str::str (char c) { + text = new char[1]; + clear (); + alloclen = strlen (text); + append (c); +} + +// ============================================================================ +void str::clear () { + delete text; + text = new char[1]; + text[0] = '\0'; + curs = 0; +} + +unsigned int str::len () {return strlen(text);} + +// ============================================================================ +void str::resize (unsigned int len) { + unsigned int oldlen = strlen (text); + char* oldtext = new char[oldlen]; + strncpy (oldtext, text, oldlen); + + delete text; + text = new char[len+1]; + for (unsigned int u = 0; u < len+1; u++) + text[u] = 0; + strncpy (text, oldtext, len); + + alloclen = len; +} + +// ============================================================================ +void str::dump () { + for (unsigned int u = 0; u <= alloclen; u++) + printf ("\t%u. %u (%c)\n", u, text[u], text[u]); +} + +// ============================================================================ +// Adds a new character at the end of the string. +void str::append (char c) { + // Out of space, thus resize + if (curs == alloclen) + resize (alloclen+1); + text[curs] = c; + curs++; +} + +void str::append (const char* c) { + resize (alloclen + strlen (c)); + + for (unsigned int u = 0; u < strlen (c); u++) { + if (c[u] != 0) + append (c[u]); + } +} + +void str::append (str c) { + append (c.chars()); +} + +// ============================================================================ +void str::appendformat (const char* c, ...) { + va_list v; + + va_start (v, c); + char* buf = vdynformat (c, v, 256); + va_end (v); + + append (buf); +} + +void str::appendformat (str c, ...) { + va_list v; + + va_start (v, c); + char* buf = vdynformat (c.chars(), v, 256); + va_end (v); + + append (buf); +} + +// ============================================================================ +char* str::chars () { + return text; +} + +// ============================================================================ +unsigned int str::first (const char* c, unsigned int a) { + unsigned int r = 0; + unsigned int index = 0; + for (; a < alloclen; a++) { + if (text[a] == c[r]) { + if (r == 0) + index = a; + + r++; + if (r == strlen (c)) + return index; + } else { + if (r != 0) { + // If the string sequence broke at this point, we need to + // check this character again, for a new sequence just + // might start right here. + a--; + } + + r = 0; + } + } + + return len (); +} + +// ============================================================================ +unsigned int str::last (const char* c, int a) { + if (a == -1) + a = len(); + + int max = strlen (c)-1; + + int r = max; + for (; a >= 0; a--) { + if (text[a] == c[r]) { + r--; + if (r == -1) + return a; + } else { + if (r != max) + a++; + + r = max; + } + } + + return len (); +} + +// ============================================================================ +str str::substr (unsigned int a, unsigned int b) { + if (a > len()) a = len(); + if (b > len()) b = len(); + + if (b == a) + return ""; + + if (b < a) { + printf ("str::substr: indices %u and %u given, should be the other way around, swapping..\n", a, b); + + // Swap the variables + unsigned int c = a; + a = b; + b = c; + } + + char* s = new char[b-a]; + strncpy (s, text+a, b-a); + return str(s); +} + +// ============================================================================ +void str::remove (unsigned int idx, unsigned int dellen) { + str s1 = substr (0, idx); + str s2 = substr (idx + dellen, len()); + + clear(); + + append (s1); + append (s2); +} + +// ============================================================================ +void str::trim (int dellen) { + if (!dellen) + return; + + unsigned int delpos; + if (dellen > 0) { + delpos = len()-dellen; + text[delpos] = 0; + curs -= dellen; + } else { + str s = substr (-dellen, len()); + clear(); + append (s); + } +} + +// ============================================================================ +void str::replace (const char* o, const char* n, unsigned int a) { + unsigned int idx; + + while ((idx = first (o, a)) != len()) { + str s1 = substr (0, idx); + str s2 = substr (idx + strlen (o), len()); + + clear(); + + append (s1); + append (n); + append (s2); + } +} + +void str::insert (char* c, unsigned int pos) { + str s1 = substr (0, pos); + str s2 = substr (pos, len()); + + clear(); + append (s1); + append (c); + append (s2); +} + +void str::reverse () { + char* tmp = new char[alloclen]; + strcpy (tmp, text); + + clear(); + curs = 0; + resize (alloclen); + for (int i = alloclen-1; i >= 0; i--) + append (tmp[i]); +} + +void str::repeat (unsigned int n) { + char* tmp = new char[alloclen]; + strcpy (tmp, text); + + for (; n > 0; n--) + append (tmp); +} + +// ============================================================================ +bool str::isnumber () { + ITERATE_STRING (u) { + // Minus sign as the first character is allowed for negatives + if (!u && text[u] == '-') + continue; + + if (text[u] < '0' || text[u] > '9') + return false; + } + return true; +} + +// ============================================================================ +bool str::isword () { + ITERATE_STRING (u) { + // lowercase letters + if (text[u] >= 'a' || text[u] <= 'z') + continue; + + // uppercase letters + if (text[u] >= 'A' || text[u] <= 'Z') + continue; + + return false; + } + return true; +} + +// ============================================================================ +int str::compare (const char* c) { + return strcmp (text, c); +} + +int str::compare (str c) { + return compare (c.chars()); +} + +int str::icompare (const char* c) { + return icompare (str ((char*)c)); +} + +int str::icompare (str b) { + return strcmp (tolower().chars(), b.tolower().chars()); +} + +// ============================================================================ +str str::tolower () { + str n = text; + + for (uint u = 0; u < len(); u++) { + if (n[u] > 'A' && n[u] < 'Z') + n.text[u] += ('a' - 'A'); + } + + return n; +} + +// ============================================================================ +str str::toupper () { + str n = text; + + for (uint u = 0; u < len(); u++) { + if (n[u] > 'a' && n[u] < 'z') + n.text[u] -= ('A' - 'a'); + } + + return n; +} + +// ============================================================================ +unsigned int str::count (char* c) { + unsigned int r = 0; + unsigned int tmp = 0; + ITERATE_STRING (u) { + if (text[u] == c[r]) { + r++; + if (r == strlen (c)) { + r = 0; + tmp++; + } + } else { + if (r != 0) + u--; + r = 0; + } + } + + return tmp; +} + +// ============================================================================ +array<str> str::split (str del) { + array<str> res; + unsigned int a = 0; + + // Find all separators and store the text left to them. + while (1) { + unsigned int b = first (del, a); + + if (b == len()) + break; + + res.push (substr (a, b)); + a = b + strlen (del); + } + + // Add the string at the right of the last separator + res.push (substr (a, len())); + return res; +} + +array<str> str::operator/ (str splitstring) {return split(splitstring);} +array<str> str::operator/ (char* splitstring) {return split(splitstring);} +array<str> str::operator/ (const char* splitstring) {return split(splitstring);} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/str.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,233 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STR_H__ +#define __STR_H__ + +template<class T> class array; + +char* vdynformat (const char* c, va_list v, unsigned int size); + +#define SCCF_NUMBER 1<<0 +#define SCCF_WORD 1<<1 + +// Dynamic string object, allocates memory when needed and +// features a good bunch of manipulation methods +class str { +private: + // The actual message + char* text; + + // Where will append() place new characters? + unsigned int curs; + + // Allocated length + unsigned int alloclen; + + // Resize the text buffer to len characters + void resize (unsigned int len); +public: + // ====================================================================== + // CONSTRUCTORS + str (); + str (const char* c); + str (char c); + + // ====================================================================== + // METHODS + + // Empty the string + void clear (); + + // Length of the string + unsigned int len (); + + // The char* form of the string + char* chars (); + + // Dumps the character table of the string + void dump (); + + // Appends text to the string + void append (char c); + void append (const char* c); + void append (str c); + + // Appends formatted text to the string. + void appendformat (const char* c, ...); + void appendformat (str c, ...); + + // Returns the first occurrence of c in the string, optionally starting + // from a certain position rather than the start. + unsigned int first (const char* c, unsigned int a = 0); + + // Returns the last occurrence of c in the string, optionally starting + // from a certain position rather than the end. + unsigned int last (const char* c, int a = -1); + + // Returns a substring of the string, from a to b. + str substr (unsigned int a, unsigned int b); + + // Replace a substring with another substring. + void replace (const char* o, const char* n, unsigned int a = 0); + + // Removes a given index from the string, optionally more characters than just 1. + void remove (unsigned int idx, unsigned int dellen=1); + + void trim (int dellen); + + // Inserts a substring into a certain position. + void insert (char* c, unsigned int pos); + + // Reverses the string. + void reverse (); + + // Repeats the string a given amount of times. + void repeat (unsigned int n); + + // Is the string a number? + bool isnumber (); + + // Is the string a word, i.e consists only of alphabetic letters? + bool isword (); + + // Convert string to lower case + str tolower (); + + // Convert string to upper case + str toupper (); + + // Compare this string with another + int compare (const char* c); + int compare (str c); + int icompare (str c); + int icompare (const char* c); + + // Counts the amount of substrings in the string + unsigned int count (char* s); + + array<str> split (str del); + + // ====================================================================== + // OPERATORS + str operator+ (str& c) { + append (c); + return *this; + } + + str& operator+= (char c) { + append (c); + return *this; + } + + str& operator+= (const char* c) { + append (c); + return *this; + } + + str& operator+= (const str c) { + append (c); + return *this; + } + + str operator* (const int repcount) { + repeat (repcount); + return *this; + } + + str& operator*= (const int repcount) { + repeat (repcount); + return *this; + } + + str operator- (const int trimcount) { + trim (trimcount); + return *this; + } + + str& operator-= (const int trimcount) { + trim (trimcount); + return *this; + } + + array<str> operator/ (str splitstring); + array<str> operator/ (char* splitstring); + array<str> operator/ (const char* splitstring); + + str operator+ () { + return toupper (); + } + + str operator- () { + return tolower (); + } + + str operator! () { + reverse (); + return *this; + } + +#define DEFINE_OPERATOR_TYPE(OPER, TYPE) \ + bool operator OPER (TYPE other) {return compare(other) OPER 0;} +#define DEFINE_OPERATOR(OPER) \ + DEFINE_OPERATOR_TYPE (OPER, str) \ + DEFINE_OPERATOR_TYPE (OPER, char*) \ + DEFINE_OPERATOR_TYPE (OPER, const char*) + + DEFINE_OPERATOR (==) + DEFINE_OPERATOR (!=) + DEFINE_OPERATOR (>) + DEFINE_OPERATOR (<) + DEFINE_OPERATOR (>=) + DEFINE_OPERATOR (<=) + + char operator [] (unsigned int pos) { + return text[pos]; + } + + operator char* () const { + return text; + } + + operator int () const {return atoi(text);} + operator unsigned int () const {return atoi(text);} +}; + +#endif // __STR_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/stringtable.cxx Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,104 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STRINGTABLE_CXX__ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "common.h" +#include "bots.h" +#include "stringtable.h" + +// ============================================================================ +// Initializes the string table +void InitStringTable() { + // Zero out everything first. + for (unsigned int a = 0; a < MAX_LIST_STRINGS; a++) + for (unsigned int b = 0; b < MAX_STRING_LENGTH; b++) + g_StringTable[a][b] = 0; +} + +// ============================================================================ +// Potentially adds a string to the table and returns the index of it. +unsigned int PushToStringTable (char* s) { + // Must not be too long. + if (strlen (s) >= MAX_STRING_LENGTH) + error ("string `%s` too long (%d characters max)\n", s, strlen (s)); + + // Find a free slot in the table. + unsigned int a; + for (a = 0; a < MAX_LIST_STRINGS; a++) { + // String is already in the table, thus return it. + if (!strcmp (s, g_StringTable[a])) + return a; + + // String is empty, thus it's free. + if (!strlen (g_StringTable[a])) + break; + } + + // no free slots! + if (a == MAX_LIST_STRINGS) + error ("too many strings defined!\n"); + + // Determine the length + size_t l1 = strlen (s); + size_t l2 = MAX_LIST_STRINGS - 1; + size_t len = (l1 < l2) ? l1 : l2; + + // Now, dump the string into the slot + strncpy (g_StringTable[a], s, len); + g_StringTable[a][len] = 0; + + return a; +} + +// ============================================================================ +// Counts the amount of strings in the table. +unsigned int CountStringTable () { + unsigned int count = 0; + for (unsigned int a = 0; a < MAX_LIST_STRINGS; a++) { + if (!strlen (g_StringTable[a])) + break; + else + count++; + } + return count; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/stringtable.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,50 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "bots.h" + +void InitStringTable(); +unsigned int PushToStringTable (char* s); +unsigned int CountStringTable (); + +#ifndef __STRINGTABLE_CXX__ +extern +#endif +char g_StringTable[MAX_LIST_STRINGS][MAX_STRING_LENGTH]; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/variables.cxx Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,106 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define __VARIABLES_CXX__ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "common.h" +#include "array.h" +#include "bots.h" +#include "botcommands.h" +#include "objwriter.h" +#include "stringtable.h" +#include "variables.h" +#include "scriptreader.h" + +array<ScriptVar> g_GlobalVariables; +array<ScriptVar> g_LocalVariables; + +// ============================================================================ +// Tries to declare a new global-scope variable. Returns pointer +// to new global variable, NULL if declaration failed. +ScriptVar* DeclareGlobalVariable (ScriptReader* r, type_e type, str name) { + // Unfortunately the VM does not support string variables so yeah. + if (type == TYPE_STRING) + r->ParserError ("variables cannot be string\n"); + + // Check that the variable is valid + if (FindCommand (name)) + r->ParserError ("name of variable-to-be `%s` conflicts with that of a command", name.chars()); + + if (IsKeyword (name)) + r->ParserError ("name of variable-to-be `%s` is a keyword", name.chars()); + + if (g_GlobalVariables.size() >= MAX_SCRIPT_VARIABLES) + r->ParserError ("too many global variables!"); + + for (uint i = 0; i < g_GlobalVariables.size(); i++) + if (g_GlobalVariables[i].name == name) + r->ParserError ("attempted redeclaration of global variable `%s`", name.chars()); + + ScriptVar g; + g.index = g_GlobalVariables.size(); + g.name = name; + g.statename = ""; + g.value = 0; + g.type = type; + + g_GlobalVariables << g; + return &g_GlobalVariables[g.index]; +} + +// ============================================================================ +// Find a global variable by name +ScriptVar* FindGlobalVariable (str name) { + for (uint i = 0; i < g_GlobalVariables.size(); i++) { + ScriptVar* g = &g_GlobalVariables[i]; + if (g->name == name) + return g; + } + + return NULL; +} + +// ============================================================================ +// Count all declared global variables +uint CountGlobalVars () { + return g_GlobalVariables.size(); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/variables.h Fri Jan 10 16:11:49 2014 +0200 @@ -0,0 +1,66 @@ +/* + * botc source code + * Copyright (C) 2012 Santeri `Dusk` Piippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the developer nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * 4. Redistributions in any form must be accompanied by information on how to + * obtain complete source code for the software and any accompanying + * software that uses the software. The source code must either be included + * in the distribution or be available for no more than the cost of + * distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VARIABLES_H__ +#define __VARIABLES_H__ +#include "str.h" +#include "scriptreader.h" + +struct ScriptVar { + str name; + str statename; + type_e type; + int value; + unsigned int index; +}; + +extern array<ScriptVar> g_GlobalVariables; +extern array<ScriptVar> g_LocalVariables; + +#define ITERATE_GLOBAL_VARS(u) \ + for (u = 0; u < MAX_SCRIPT_VARIABLES; u++) +#define ITERATE_SCRIPT_VARS(g) \ + for (g = g_ScriptVariable; g != NULL; g = g->next) + +ScriptVar* DeclareGlobalVariable (ScriptReader* r, type_e type, str name); +deprecated unsigned int CountGlobalVars (); +ScriptVar* FindGlobalVariable (str name); + +#endif // __VARIABLES_H__ \ No newline at end of file
--- a/str.cxx Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,426 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> -#include "array.h" -#include "str.h" -#include "common.h" - -#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 -// that fails, double the size and keep recursing until it works. -char* vdynformat (const char* c, va_list v, unsigned int size) { - char* buffer = new char[size]; - int r = vsnprintf (buffer, size-1, c, v); - if (r > (int)size-1 || r < 0) { - delete buffer; - buffer = vdynformat (c, v, size*2); - } - return buffer; -} - -// ============================================================================ -str::str () { - text = new char[1]; - clear(); - alloclen = strlen (text); -} - -str::str (const char* c) { - text = new char[1]; - clear (); - alloclen = strlen (text); - append (c); -} - -str::str (char c) { - text = new char[1]; - clear (); - alloclen = strlen (text); - append (c); -} - -// ============================================================================ -void str::clear () { - delete text; - text = new char[1]; - text[0] = '\0'; - curs = 0; -} - -unsigned int str::len () {return strlen(text);} - -// ============================================================================ -void str::resize (unsigned int len) { - unsigned int oldlen = strlen (text); - char* oldtext = new char[oldlen]; - strncpy (oldtext, text, oldlen); - - delete text; - text = new char[len+1]; - for (unsigned int u = 0; u < len+1; u++) - text[u] = 0; - strncpy (text, oldtext, len); - - alloclen = len; -} - -// ============================================================================ -void str::dump () { - for (unsigned int u = 0; u <= alloclen; u++) - printf ("\t%u. %u (%c)\n", u, text[u], text[u]); -} - -// ============================================================================ -// Adds a new character at the end of the string. -void str::append (char c) { - // Out of space, thus resize - if (curs == alloclen) - resize (alloclen+1); - text[curs] = c; - curs++; -} - -void str::append (const char* c) { - resize (alloclen + strlen (c)); - - for (unsigned int u = 0; u < strlen (c); u++) { - if (c[u] != 0) - append (c[u]); - } -} - -void str::append (str c) { - append (c.chars()); -} - -// ============================================================================ -void str::appendformat (const char* c, ...) { - va_list v; - - va_start (v, c); - char* buf = vdynformat (c, v, 256); - va_end (v); - - append (buf); -} - -void str::appendformat (str c, ...) { - va_list v; - - va_start (v, c); - char* buf = vdynformat (c.chars(), v, 256); - va_end (v); - - append (buf); -} - -// ============================================================================ -char* str::chars () { - return text; -} - -// ============================================================================ -unsigned int str::first (const char* c, unsigned int a) { - unsigned int r = 0; - unsigned int index = 0; - for (; a < alloclen; a++) { - if (text[a] == c[r]) { - if (r == 0) - index = a; - - r++; - if (r == strlen (c)) - return index; - } else { - if (r != 0) { - // If the string sequence broke at this point, we need to - // check this character again, for a new sequence just - // might start right here. - a--; - } - - r = 0; - } - } - - return len (); -} - -// ============================================================================ -unsigned int str::last (const char* c, int a) { - if (a == -1) - a = len(); - - int max = strlen (c)-1; - - int r = max; - for (; a >= 0; a--) { - if (text[a] == c[r]) { - r--; - if (r == -1) - return a; - } else { - if (r != max) - a++; - - r = max; - } - } - - return len (); -} - -// ============================================================================ -str str::substr (unsigned int a, unsigned int b) { - if (a > len()) a = len(); - if (b > len()) b = len(); - - if (b == a) - return ""; - - if (b < a) { - printf ("str::substr: indices %u and %u given, should be the other way around, swapping..\n", a, b); - - // Swap the variables - unsigned int c = a; - a = b; - b = c; - } - - char* s = new char[b-a]; - strncpy (s, text+a, b-a); - return str(s); -} - -// ============================================================================ -void str::remove (unsigned int idx, unsigned int dellen) { - str s1 = substr (0, idx); - str s2 = substr (idx + dellen, len()); - - clear(); - - append (s1); - append (s2); -} - -// ============================================================================ -void str::trim (int dellen) { - if (!dellen) - return; - - unsigned int delpos; - if (dellen > 0) { - delpos = len()-dellen; - text[delpos] = 0; - curs -= dellen; - } else { - str s = substr (-dellen, len()); - clear(); - append (s); - } -} - -// ============================================================================ -void str::replace (const char* o, const char* n, unsigned int a) { - unsigned int idx; - - while ((idx = first (o, a)) != len()) { - str s1 = substr (0, idx); - str s2 = substr (idx + strlen (o), len()); - - clear(); - - append (s1); - append (n); - append (s2); - } -} - -void str::insert (char* c, unsigned int pos) { - str s1 = substr (0, pos); - str s2 = substr (pos, len()); - - clear(); - append (s1); - append (c); - append (s2); -} - -void str::reverse () { - char* tmp = new char[alloclen]; - strcpy (tmp, text); - - clear(); - curs = 0; - resize (alloclen); - for (int i = alloclen-1; i >= 0; i--) - append (tmp[i]); -} - -void str::repeat (unsigned int n) { - char* tmp = new char[alloclen]; - strcpy (tmp, text); - - for (; n > 0; n--) - append (tmp); -} - -// ============================================================================ -bool str::isnumber () { - ITERATE_STRING (u) { - // Minus sign as the first character is allowed for negatives - if (!u && text[u] == '-') - continue; - - if (text[u] < '0' || text[u] > '9') - return false; - } - return true; -} - -// ============================================================================ -bool str::isword () { - ITERATE_STRING (u) { - // lowercase letters - if (text[u] >= 'a' || text[u] <= 'z') - continue; - - // uppercase letters - if (text[u] >= 'A' || text[u] <= 'Z') - continue; - - return false; - } - return true; -} - -// ============================================================================ -int str::compare (const char* c) { - return strcmp (text, c); -} - -int str::compare (str c) { - return compare (c.chars()); -} - -int str::icompare (const char* c) { - return icompare (str ((char*)c)); -} - -int str::icompare (str b) { - return strcmp (tolower().chars(), b.tolower().chars()); -} - -// ============================================================================ -str str::tolower () { - str n = text; - - for (uint u = 0; u < len(); u++) { - if (n[u] > 'A' && n[u] < 'Z') - n.text[u] += ('a' - 'A'); - } - - return n; -} - -// ============================================================================ -str str::toupper () { - str n = text; - - for (uint u = 0; u < len(); u++) { - if (n[u] > 'a' && n[u] < 'z') - n.text[u] -= ('A' - 'a'); - } - - return n; -} - -// ============================================================================ -unsigned int str::count (char* c) { - unsigned int r = 0; - unsigned int tmp = 0; - ITERATE_STRING (u) { - if (text[u] == c[r]) { - r++; - if (r == strlen (c)) { - r = 0; - tmp++; - } - } else { - if (r != 0) - u--; - r = 0; - } - } - - return tmp; -} - -// ============================================================================ -array<str> str::split (str del) { - array<str> res; - unsigned int a = 0; - - // Find all separators and store the text left to them. - while (1) { - unsigned int b = first (del, a); - - if (b == len()) - break; - - res.push (substr (a, b)); - a = b + strlen (del); - } - - // Add the string at the right of the last separator - res.push (substr (a, len())); - return res; -} - -array<str> str::operator/ (str splitstring) {return split(splitstring);} -array<str> str::operator/ (char* splitstring) {return split(splitstring);} -array<str> str::operator/ (const char* splitstring) {return split(splitstring);} \ No newline at end of file
--- a/str.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,233 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __STR_H__ -#define __STR_H__ - -template<class T> class array; - -char* vdynformat (const char* c, va_list v, unsigned int size); - -#define SCCF_NUMBER 1<<0 -#define SCCF_WORD 1<<1 - -// Dynamic string object, allocates memory when needed and -// features a good bunch of manipulation methods -class str { -private: - // The actual message - char* text; - - // Where will append() place new characters? - unsigned int curs; - - // Allocated length - unsigned int alloclen; - - // Resize the text buffer to len characters - void resize (unsigned int len); -public: - // ====================================================================== - // CONSTRUCTORS - str (); - str (const char* c); - str (char c); - - // ====================================================================== - // METHODS - - // Empty the string - void clear (); - - // Length of the string - unsigned int len (); - - // The char* form of the string - char* chars (); - - // Dumps the character table of the string - void dump (); - - // Appends text to the string - void append (char c); - void append (const char* c); - void append (str c); - - // Appends formatted text to the string. - void appendformat (const char* c, ...); - void appendformat (str c, ...); - - // Returns the first occurrence of c in the string, optionally starting - // from a certain position rather than the start. - unsigned int first (const char* c, unsigned int a = 0); - - // Returns the last occurrence of c in the string, optionally starting - // from a certain position rather than the end. - unsigned int last (const char* c, int a = -1); - - // Returns a substring of the string, from a to b. - str substr (unsigned int a, unsigned int b); - - // Replace a substring with another substring. - void replace (const char* o, const char* n, unsigned int a = 0); - - // Removes a given index from the string, optionally more characters than just 1. - void remove (unsigned int idx, unsigned int dellen=1); - - void trim (int dellen); - - // Inserts a substring into a certain position. - void insert (char* c, unsigned int pos); - - // Reverses the string. - void reverse (); - - // Repeats the string a given amount of times. - void repeat (unsigned int n); - - // Is the string a number? - bool isnumber (); - - // Is the string a word, i.e consists only of alphabetic letters? - bool isword (); - - // Convert string to lower case - str tolower (); - - // Convert string to upper case - str toupper (); - - // Compare this string with another - int compare (const char* c); - int compare (str c); - int icompare (str c); - int icompare (const char* c); - - // Counts the amount of substrings in the string - unsigned int count (char* s); - - array<str> split (str del); - - // ====================================================================== - // OPERATORS - str operator+ (str& c) { - append (c); - return *this; - } - - str& operator+= (char c) { - append (c); - return *this; - } - - str& operator+= (const char* c) { - append (c); - return *this; - } - - str& operator+= (const str c) { - append (c); - return *this; - } - - str operator* (const int repcount) { - repeat (repcount); - return *this; - } - - str& operator*= (const int repcount) { - repeat (repcount); - return *this; - } - - str operator- (const int trimcount) { - trim (trimcount); - return *this; - } - - str& operator-= (const int trimcount) { - trim (trimcount); - return *this; - } - - array<str> operator/ (str splitstring); - array<str> operator/ (char* splitstring); - array<str> operator/ (const char* splitstring); - - str operator+ () { - return toupper (); - } - - str operator- () { - return tolower (); - } - - str operator! () { - reverse (); - return *this; - } - -#define DEFINE_OPERATOR_TYPE(OPER, TYPE) \ - bool operator OPER (TYPE other) {return compare(other) OPER 0;} -#define DEFINE_OPERATOR(OPER) \ - DEFINE_OPERATOR_TYPE (OPER, str) \ - DEFINE_OPERATOR_TYPE (OPER, char*) \ - DEFINE_OPERATOR_TYPE (OPER, const char*) - - DEFINE_OPERATOR (==) - DEFINE_OPERATOR (!=) - DEFINE_OPERATOR (>) - DEFINE_OPERATOR (<) - DEFINE_OPERATOR (>=) - DEFINE_OPERATOR (<=) - - char operator [] (unsigned int pos) { - return text[pos]; - } - - operator char* () const { - return text; - } - - operator int () const {return atoi(text);} - operator unsigned int () const {return atoi(text);} -}; - -#endif // __STR_H__ \ No newline at end of file
--- a/stringtable.cxx Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#define __STRINGTABLE_CXX__ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "common.h" -#include "bots.h" -#include "stringtable.h" - -// ============================================================================ -// Initializes the string table -void InitStringTable() { - // Zero out everything first. - for (unsigned int a = 0; a < MAX_LIST_STRINGS; a++) - for (unsigned int b = 0; b < MAX_STRING_LENGTH; b++) - g_StringTable[a][b] = 0; -} - -// ============================================================================ -// Potentially adds a string to the table and returns the index of it. -unsigned int PushToStringTable (char* s) { - // Must not be too long. - if (strlen (s) >= MAX_STRING_LENGTH) - error ("string `%s` too long (%d characters max)\n", s, strlen (s)); - - // Find a free slot in the table. - unsigned int a; - for (a = 0; a < MAX_LIST_STRINGS; a++) { - // String is already in the table, thus return it. - if (!strcmp (s, g_StringTable[a])) - return a; - - // String is empty, thus it's free. - if (!strlen (g_StringTable[a])) - break; - } - - // no free slots! - if (a == MAX_LIST_STRINGS) - error ("too many strings defined!\n"); - - // Determine the length - size_t l1 = strlen (s); - size_t l2 = MAX_LIST_STRINGS - 1; - size_t len = (l1 < l2) ? l1 : l2; - - // Now, dump the string into the slot - strncpy (g_StringTable[a], s, len); - g_StringTable[a][len] = 0; - - return a; -} - -// ============================================================================ -// Counts the amount of strings in the table. -unsigned int CountStringTable () { - unsigned int count = 0; - for (unsigned int a = 0; a < MAX_LIST_STRINGS; a++) { - if (!strlen (g_StringTable[a])) - break; - else - count++; - } - return count; -} \ No newline at end of file
--- a/stringtable.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "bots.h" - -void InitStringTable(); -unsigned int PushToStringTable (char* s); -unsigned int CountStringTable (); - -#ifndef __STRINGTABLE_CXX__ -extern -#endif -char g_StringTable[MAX_LIST_STRINGS][MAX_STRING_LENGTH]; \ No newline at end of file
--- a/variables.cxx Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#define __VARIABLES_CXX__ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "common.h" -#include "array.h" -#include "bots.h" -#include "botcommands.h" -#include "objwriter.h" -#include "stringtable.h" -#include "variables.h" -#include "scriptreader.h" - -array<ScriptVar> g_GlobalVariables; -array<ScriptVar> g_LocalVariables; - -// ============================================================================ -// Tries to declare a new global-scope variable. Returns pointer -// to new global variable, NULL if declaration failed. -ScriptVar* DeclareGlobalVariable (ScriptReader* r, type_e type, str name) { - // Unfortunately the VM does not support string variables so yeah. - if (type == TYPE_STRING) - r->ParserError ("variables cannot be string\n"); - - // Check that the variable is valid - if (FindCommand (name)) - r->ParserError ("name of variable-to-be `%s` conflicts with that of a command", name.chars()); - - if (IsKeyword (name)) - r->ParserError ("name of variable-to-be `%s` is a keyword", name.chars()); - - if (g_GlobalVariables.size() >= MAX_SCRIPT_VARIABLES) - r->ParserError ("too many global variables!"); - - for (uint i = 0; i < g_GlobalVariables.size(); i++) - if (g_GlobalVariables[i].name == name) - r->ParserError ("attempted redeclaration of global variable `%s`", name.chars()); - - ScriptVar g; - g.index = g_GlobalVariables.size(); - g.name = name; - g.statename = ""; - g.value = 0; - g.type = type; - - g_GlobalVariables << g; - return &g_GlobalVariables[g.index]; -} - -// ============================================================================ -// Find a global variable by name -ScriptVar* FindGlobalVariable (str name) { - for (uint i = 0; i < g_GlobalVariables.size(); i++) { - ScriptVar* g = &g_GlobalVariables[i]; - if (g->name == name) - return g; - } - - return NULL; -} - -// ============================================================================ -// Count all declared global variables -uint CountGlobalVars () { - return g_GlobalVariables.size(); -} \ No newline at end of file
--- a/variables.h Wed Jan 02 23:57:46 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * botc source code - * Copyright (C) 2012 Santeri `Dusk` Piippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the developer nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * 4. Redistributions in any form must be accompanied by information on how to - * obtain complete source code for the software and any accompanying - * software that uses the software. The source code must either be included - * in the distribution or be available for no more than the cost of - * distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __VARIABLES_H__ -#define __VARIABLES_H__ -#include "str.h" -#include "scriptreader.h" - -struct ScriptVar { - str name; - str statename; - type_e type; - int value; - unsigned int index; -}; - -extern array<ScriptVar> g_GlobalVariables; -extern array<ScriptVar> g_LocalVariables; - -#define ITERATE_GLOBAL_VARS(u) \ - for (u = 0; u < MAX_SCRIPT_VARIABLES; u++) -#define ITERATE_SCRIPT_VARS(g) \ - for (g = g_ScriptVariable; g != NULL; g = g->next) - -ScriptVar* DeclareGlobalVariable (ScriptReader* r, type_e type, str name); -deprecated unsigned int CountGlobalVars (); -ScriptVar* FindGlobalVariable (str name); - -#endif // __VARIABLES_H__ \ No newline at end of file