Sat, 11 Jan 2014 22:36:31 +0200
- *hot stuff coming through*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Sat Jan 11 22:36:31 2014 +0200 @@ -0,0 +1,5 @@ +syntax: glob +build +Makefile +*.bs +*.kdev4 \ No newline at end of file
--- a/CMakeLists.txt Fri Jan 10 21:58:42 2014 +0200 +++ b/CMakeLists.txt Sat Jan 11 22:36:31 2014 +0200 @@ -3,7 +3,7 @@ src/events.cxx src/format.cc src/main.cxx - src/objwriter.cxx + src/object_writer.cxx src/parser.cxx src/preprocessor.cxx src/scriptreader.cxx
--- a/src/botcommands.h Fri Jan 10 21:58:42 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,164 +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 BOTC_BOTCOMMANDS_H -#define BOTC_BOTCOMMANDS_H - -#include "bots.h" - -//***************************************************************************** -// DEFINES - -// 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 // BOTC_BOTCOMMANDS_H \ No newline at end of file
--- a/src/bots.h Fri Jan 10 21:58:42 2014 +0200 +++ b/src/bots.h Sat Jan 11 22:36:31 2014 +0200 @@ -1,272 +1,167 @@ -//----------------------------------------------------------------------------- -// -// 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. -// -//----------------------------------------------------------------------------- +// Macros and enums from zandronum bots.h #ifndef BOTC_BOTS_H #define BOTC_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 +static const int g_max_states = 256; +static const int g_max_events = 32; +static const int g_max_global_events = 32; +static const int g_max_global_vars = 128; +static const int g_max_global_arrays = 16; +static const int g_max_array_size = 65536; +static const int g_max_state_vars = 16; +static const int g_max_stringlist_size = 128; +static const int g_max_string_length = 256; +static const int g_max_reaction_time = 52; +static const int g_max_stored_events = 64; -// 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 +enum e_data_header { - 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; + dh_command, + dh_state_index, + dh_state_name, + dh_on_enter, + dh_main_loop, + dh_on_exit, + dh_event, + dh_end_on_enter, + dh_end_main_loop, + dh_end_on_exit, + dh_end_event, + dh_if_goto, + dh_if_not_goto, + dh_goto, + dh_or_logical, + dh_and_logical, + dh_or_bitwise, + dh_eor_bitwise, + dh_and_bitwise, + dh_equals, + dh_not_equals, + dh_less_than, + dh_at_most, + dh_greater_than, + dh_at_least, + dh_negate_logical, + dh_left_shift, + dh_right_shift, + dh_add, + dh_subtract, + dh_unary_minus, + dh_multiply, + dh_divide, + dh_modulus, + dh_push_number, + dh_push_string_index, + dh_push_global_var, + dh_push_local_var, + dh_drop_stack_position, + dh_script_var_list, + dh_string_list, + dh_increase_global_var, + dh_decrease_global_var, + dh_assign_global_var, + dh_add_global_var, + dh_subtract_global_var, + dh_multiply_global_var, + dh_divide_global_var, + dh_mod_global_var, + dh_increase_local_var, + dh_decrease_local_var, + dh_assign_local_var, + dh_add_local_var, + dh_subtract_local_var, + dh_multiply_local_var, + dh_divide_local_var, + dh_mod_local_var, + dh_CASEGOTO, + dh_DROP, + dh_increase_global_array, + dh_decrease_global_array, + dh_assign_global_array, + dh_add_global_array, + dh_subtract_global_array, + dh_multiply_global_array, + dh_divide_global_array, + dh_mod_global_array, + dh_push_global_array, + dh_swap, + dh_dup, + dh_array_set, + num_data_headers +}; //***************************************************************************** // These are the different bot events that can be posted to a bot's state. -typedef enum +enum e_event { - 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, + ev_killed_by_enemy, + ev_killed_by_player, + ev_killed_by_self, + ev_killed_by_environment, + ev_reached_goal, + ev_goal_removed, + ev_damaged_by_player, + ev_player_say, + ev_enemy_killed, + ev_respawned, + ev_intermission, + ev_new_maps, + ev_enemy_used_fist, + ev_enemy_used_chainsaw, + ev_enemy_fired_pistol, + ev_enemy_fired_shotgun, + ev_enemy_fired_ssg, + ev_enemy_fired_chaingun, + ev_enemy_fired_minigun, + ev_enemy_fired_rocket, + ev_enemy_fired_grenade, + ev_enemy_fired_railgun, + ev_enemy_fired_plasma, + ev_enemy_fired_bfg, + ev_enemy_fired_bfg10k, + ev_player_used_fist, + ev_player_used_chainsaw, + ev_player_fired_pistol, + ev_player_fired_shotgun, + ev_player_fired_ssg, + ev_player_fired_chaingun, + ev_player_fired_minigun, + ev_player_fired_rocket, + ev_player_fired_grenade, + ev_player_fired_railgun, + ev_player_fired_plasma, + ev_player_fired_bfg, + ev_player_fired_bfg10k, + ev_used_fist, + ev_used_chainsaw, + ev_fired_pistol, + ev_fired_shotgun, + ev_fired_ssg, + ev_fired_chaingun, + ev_fired_minigun, + ev_fired_rocket, + ev_fired_grenade, + ev_fired_railgun, + ev_fired_plasma, + ev_fired_bfg, + ev_fired_bfg10k, + ev_player_joined_game, + ev_joined_game, + ev_duel_starting_countdown, + ev_duel_fight, + ev_duel_win_sequence, + ev_spectating, + ev_lms_starting_countdown, + ev_lms_fight, + ev_lms_win_sequence, + ev_weapon_change, + ev_enemy_bfg_explode, + ev_player_bfg_explode, + ev_bfg_explode, + ev_recieved_medal, - NUM_BOTEVENTS - -} BOTEVENT_e; + num_bot_events +}; #endif // BOTC_BOTS_H
--- a/src/commands.cxx Fri Jan 10 21:58:42 2014 +0200 +++ b/src/commands.cxx Sat Jan 11 22:36:31 2014 +0200 @@ -1,42 +1,32 @@ /* - * 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. - */ + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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> @@ -54,7 +44,7 @@ ScriptReader* r = new ScriptReader ("commands.def"); g_CommDef = null; CommandDef* curdef = g_CommDef; - unsigned int numCommDefs = 0; + int numCommDefs = 0; while (r->PeekNext().len()) { CommandDef* comm = new CommandDef;
--- a/src/commands.h Fri Jan 10 21:58:42 2014 +0200 +++ b/src/commands.h Sat Jan 11 22:36:31 2014 +0200 @@ -1,41 +1,31 @@ /* - * 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. + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 BOTC_COMMANDS_H @@ -46,7 +36,6 @@ #include "main.h" #include "str.h" -#include "botcommands.h" #define ITERATE_COMMANDS(comm) \ for (comm = g_CommDef; comm->next != null; comm = comm->next)
--- a/src/containers.h Fri Jan 10 21:58:42 2014 +0200 +++ b/src/containers.h Sat Jan 11 22:36:31 2014 +0200 @@ -1,5 +1,35 @@ -#ifndef LIBCOBALT_CONTAINERS_H -#define LIBCOBALT_CONTAINERS_H +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 BOTC_CONTAINERS_H +#define BOTC_CONTAINERS_H #include <cassert> #include <algorithm> @@ -8,220 +38,311 @@ template<class T> class list { - public: - typedef typename ::std::deque<T> list_type; - typedef typename list_type::iterator it; - typedef typename list_type::const_iterator c_it; - typedef typename list_type::reverse_iterator r_it; - typedef typename list_type::const_reverse_iterator cr_it; - typedef T element_type; - typedef list<T> self_type; + public: + typedef typename ::std::deque<T> list_type; + typedef typename list_type::iterator it; + typedef typename list_type::const_iterator c_it; + typedef typename list_type::reverse_iterator r_it; + typedef typename list_type::const_reverse_iterator cr_it; + typedef T element_type; + typedef list<T> self_type; + + // ===================================================================== + // + list() {} - list() {} - list (std::initializer_list<element_type> vals) - { - m_data = vals; - } + // ===================================================================== + // + list (std::initializer_list<element_type> vals) + { + m_data = vals; + } - list (const list_type& a) : m_data (a) {} + // ===================================================================== + // + list (const list_type& a) : + m_data (a) {} - it begin() - { - return m_data.begin(); - } + // ===================================================================== + // + it begin() + { + return m_data.begin(); + } - c_it begin() const - { - return m_data.cbegin(); - } + // ===================================================================== + // + c_it begin() const + { + return m_data.cbegin(); + } - it end() - { - return m_data.end(); - } + // ===================================================================== + // + it end() + { + return m_data.end(); + } - c_it end() const - { - return m_data.cend(); - } + // ===================================================================== + // + c_it end() const + { + return m_data.cend(); + } - r_it rbegin() - { - return m_data.rbegin(); - } + // ===================================================================== + // + r_it rbegin() + { + return m_data.rbegin(); + } - cr_it crbegin() const - { - return m_data.crbegin(); - } + // ===================================================================== + // + cr_it crbegin() const + { + return m_data.crbegin(); + } - r_it rend() - { - return m_data.rend(); - } + // ===================================================================== + // + r_it rend() + { + return m_data.rend(); + } - cr_it crend() const - { - return m_data.crend(); - } + // ===================================================================== + // + cr_it crend() const + { + return m_data.crend(); + } - void erase (int pos) - { - assert (pos < size()); - m_data.erase (m_data.begin() + pos); - } + // ===================================================================== + // + inline void erase (int pos) + { + assert (pos < size()); + m_data.erase (m_data.begin() + pos); + } - element_type& push_front (const element_type& value) - { - m_data.push_front (value); - return m_data[0]; - } + // ===================================================================== + // + element_type& push_front (const element_type& value) + { + m_data.push_front (value); + return m_data[0]; + } + + // ===================================================================== + // + element_type& push_back (const element_type& value) + { + m_data.push_back (value); + return m_data[m_data.size() - 1]; + } - element_type& push_back (const element_type& value) - { - m_data.push_back (value); - return m_data[m_data.size() - 1]; - } + // ===================================================================== + // + void push_back (const self_type& vals) + { + for (const T & val : vals) + push_back (val); + } - void push_back (const self_type& vals) - { - for (const T & val : vals) - push_back (val); - } + // ===================================================================== + // + bool pop (T& val) + { + if (is_empty()) + return false; + + val = m_data[size() - 1]; + m_data.erase (m_data.end() - 1); + return true; + } - bool pop (T& val) - { - if (size() == 0) - return false; - - val = m_data[size() - 1]; - erase (size() - 1); - return true; - } + // ===================================================================== + // + T& operator<< (const T& value) + { + return push_back (value); + } - T& operator<< (const T& value) - { - return push_back (value); - } + // ===================================================================== + // + void operator<< (const self_type& vals) + { + push_back (vals); + } - void operator<< (const self_type& vals) - { - push_back (vals); - } + // ===================================================================== + // + bool operator>> (T& value) + { + return pop (value); + } - bool operator>> (T& value) - { - return pop (value); - } + // ===================================================================== + // + self_type reverse() const + { + self_type rev; - self_type reverse() const - { - self_type rev; + for (const T & val : *this) + val >> rev; - for (const T & val : *this) - val >> rev; + return rev; + } - return rev; - } + // ===================================================================== + // + void clear() + { + m_data.clear(); + } - void clear() - { - m_data.clear(); - } + // ===================================================================== + // + void insert (int pos, const element_type& value) + { + m_data.insert (m_data.begin() + pos, value); + } - void insert (int pos, const element_type& value) - { - m_data.insert (m_data.begin() + pos, value); - } + // ===================================================================== + // + void makeUnique() + { + // Remove duplicate entries. For this to be effective, the vector must be + // sorted first. + sort(); + it pos = std::unique (begin(), end()); + resize (std::distance (begin(), pos)); + } - void makeUnique() - { - // Remove duplicate entries. For this to be effective, the vector must be - // sorted first. - sort(); - it pos = std::unique (begin(), end()); - resize (std::distance (begin(), pos)); - } + // ===================================================================== + // + int size() const + { + return m_data.size(); + } + + // ===================================================================== + // + element_type& operator[] (int n) + { + assert (n < size()); + return m_data[n]; + } - int size() const - { - return m_data.size(); - } + // ===================================================================== + // + const element_type& operator[] (int n) const + { + assert (n < size()); + return m_data[n]; + } - element_type& operator[] (int n) - { - assert (n < size()); - return m_data[n]; - } + // ===================================================================== + // + void resize (int size) + { + m_data.resize (size); + } - const element_type& operator[] (int n) const - { - assert (n < size()); - return m_data[n]; - } + // ===================================================================== + // + void sort() + { + std::sort (begin(), end()); + } - void resize (std::ptrdiff_t size) - { - m_data.resize (size); - } + // ===================================================================== + // + int find (const element_type& needle) + { + int i = 0; - void sort() - { - std::sort (begin(), end()); - } + for (const element_type & hay : *this) + { + if (hay == needle) + return i; + + i++; + } + + return -1; + } - int find (const element_type& needle) - { - int i = 0; + // ===================================================================== + // + void remove (const element_type& it) + { + int idx; - for (const element_type & hay : *this) - { - if (hay == needle) - return i; + if ((idx = find (it)) != -1) + erase (idx); + } - i++; - } - - return -1; - } + // ===================================================================== + // + inline bool is_empty() const + { + return size() == 0; + } - void remove (const element_type& it) - { - int idx; + // ===================================================================== + // + self_type mid (int a, int b) const + { + assert (a >= 0 && b >= 0 && a < size() && b < size() && a <= b); + self_type result; + + for (int i = a; i <= b; ++i) + result << operator[] (i); - if ( (idx = find (it)) != -1) - erase (idx); - } + return result; + } - inline bool is_empty() const - { - return size() == 0; - } + // ===================================================================== + // + inline const list_type& std_deque() const + { + return m_data; + } - self_type mid (int a, int b) const - { - assert (a >= 0 && b >= 0 && a < size() && b < size() && a <= b); - self_type result; - - for (int i = a; i <= b; ++i) - result << operator[] (i); + // ===================================================================== + // + const element_type& first() const + { + return *(m_data.begin()); + } - return result; - } + // ===================================================================== + // + const element_type& last() const + { + return *(m_data.end()); + } - inline const list_type& std_deque() const - { - return m_data; - } + // ===================================================================== + // + bool contains (const element_type& a) const + { + return find (a) != -1; + } - private: - list_type m_data; + private: + list_type m_data; }; +// ============================================================================= +// template<class T> list<T>& operator>> (const T& value, list<T>& haystack) { - haystack.push_front (value); - return haystack; + haystack.push_front (value); + return haystack; } -#endif // LIBCOBALT_CONTAINERS_H +#endif // BOTC_CONTAINERS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/data_buffer.cc Sat Jan 11 22:36:31 2014 +0200 @@ -0,0 +1,260 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 "data_buffer.h" + +// ============================================================================ +// +void data_buffer::merge (data_buffer* other) +{ + if (!other) + return; + + int oldsize = writesize; + + for (int x = 0; x < other->writesize; x++) + write (* (other->buffer + x)); + + // Merge its marks and references + 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! + int r = AddMarkReference (other->refs[u]->num, false); + refs[r]->pos = other->refs[u]->pos + oldsize; + } + } + + delete other; +} + +// ============================================================================ +// +data_buffer* data_buffer::clone() +{ + data_buffer* other = new data_buffer; + + for (int x = 0; x < writesize; x++) + other->write (* (buffer + x)); + + return other; +} + +// ============================================================================ +// +int data_buffer::add_mark (string name) +{ + // Find a free slot for the mark + 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; +} + +// ============================================================================ +// +int data_buffer::AddMarkReference (int marknum, bool placeholder) +{ + 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; +} + +// ============================================================================ +// +void data_buffer::delete_mark (int marknum) +{ + if (!marks[marknum]) + return; + + // Delete the mark + delete marks[marknum]; + marks[marknum] = null; + + // Delete its references + for (int u = 0; u < MAX_MARKS; u++) + { + if (refs[u]->num == marknum) + { + delete refs[u]; + refs[u] = null; + } + } +} + +// ============================================================================ +// +void data_buffer::move_mark (int i) +{ + if (!marks[i]) + return; + + marks[i]->pos = writesize; +} + +// ============================================================================ +// +void data_buffer::offset_mark (int mark, int offset) +{ + if (!marks[mark]) + return; + + marks[mark]->pos += offset; +} + +// ============================================================================ +// +int data_buffer::count_marks() +{ + int count = 0; + + for (int u = 0; u < MAX_MARKS; u++) + count += !!marks[u]; + + return count; +} + +// ============================================================================ +// +int data_buffer::count_references() +{ + int count = 0; + + for (int u = 0; u < MAX_MARKS; u++) + count += !!refs[u]; + + return count; +} + +// ============================================================================ +// +void data_buffer::write_float (string 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 data_buffer::write_string (string a) +{ + write (DH_PUSHSTRINGINDEX); + write (get_string_table_index (a)); +} + +// ============================================================================ +// +void data_buffer::dump() +{ + for (int x = 0; x < writesize; x++) + printf ("%d. [%d]\n", x, * (buffer + x)); +} + +// ============================================================================ +// +data_buffer::~data_buffer() +{ + delete buffer; + + // Delete any marks and references + for (int i = 0; i < MAX_MARKS; i++) + { + delete marks[i]; + delete refs[i]; + } +} + +// ============================================================================ +// +data_buffer::data_buffer (int size) +{ + writesize = 0; + + buffer = new unsigned char[size]; + allocsize = size; + + // Clear the marks table out + for (int u = 0; u < MAX_MARKS; u++) + { + marks[u] = null; + refs[u] = null; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/data_buffer.h Sat Jan 11 22:36:31 2014 +0200 @@ -0,0 +1,149 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 BOTC_DATABUFFER_H +#define BOTC_DATABUFFER_H + +#include <stdio.h> +#include <string.h> +#include "main.h" +#include "stringtable.h" + +#define MAX_MARKS 512 + +extern int g_NextMark; + +// ============================================================================ +// DataBuffer: A dynamic data buffer. +class data_buffer +{ +public: + // The actual buffer + byte* buffer; + + // Allocated size of the buffer + int allocsize; + + // Written size of the buffer + int writesize; + + // Marks and references + ScriptMark* marks[MAX_MARKS]; + ScriptMarkReference* refs[MAX_MARKS]; + + data_buffer (int size = 128); + ~data_buffer (); + + // ==================================================================== + // Write stuff to the buffer + // TODO: un-template and remove the union, move to source file + template<class T> void write (T stuff) + { + 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. + generic_union<T> uni; + uni.as_t = stuff; + + for (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 (data_buffer* other); + + // Clones this databuffer to a new one and returns it. + data_buffer* clone (); + + // ==================================================================== + // 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. + int add_mark (string name); + + // ==================================================================== + // 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 + int add_reference (int marknum, bool placeholder = true); + + // Delete a mark and all references to it. + void delete_mark (int marknum); + + // Adjusts a mark to the current position + void move_mark(int i); + + void offset_mark (int mark, int offset); + + // Dump the buffer (for debugging purposes) + void dump(); + + // Count the amount of marks + int count_marks (); + + // Count the amount of refs + int count_references (); + + // Write a float into the buffer + void write_float (string floatstring); + + void write_string (string a); +}; + +#endif // BOTC_DATABUFFER_H
--- a/src/databuffer.h Fri Jan 10 21:58:42 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,264 +0,0 @@ -#ifndef BOTC_DATABUFFER_H -#define BOTC_DATABUFFER_H -#include <stdio.h> -#include <string.h> -#include "main.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 Write (T stuff) { - 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 (string 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) { - if (!marks[mark]) - return; - - marks[mark]->pos = writesize; - } - - void OffsetMark (unsigned int mark, int 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 (string 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 (string a) { - Write (DH_PUSHSTRINGINDEX); - Write (get_string_table_index (a)); - } -}; - -#endif // BOTC_DATABUFFER_H \ No newline at end of file
--- a/src/events.cxx Fri Jan 10 21:58:42 2014 +0200 +++ b/src/events.cxx Sat Jan 11 22:36:31 2014 +0200 @@ -1,42 +1,32 @@ /* - * 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. - */ + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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> @@ -44,68 +34,57 @@ #include "scriptreader.h" #include "str.h" #include "events.h" +#include "lexer.h" -EventDef* g_EventDef; +static void unlink_events(); +static list<event_info*> g_events; // ============================================================================ // 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++; +void init_events() +{ + lexer lx; + lx.process_file ("events.def"); + int num_events = 0; + + while (lx.get_next (tk_symbol)) + { + event_info* e = new event_info; + e->name = lx.get_token()->text; + e->number = num_events++; + g_events << e; } - - delete r; - printf ("%d event definitions read.\n", numEventDefs); + + printf ("%d event definitions read.\n", num_events); + atexit (&unlink_events); } // ============================================================================ // Delete event definitions recursively -void UnlinkEvents (EventDef* e) { - if (e->next) - UnlinkEvents (e->next); - delete e; +static void unlink_events() +{ + print ("Freeing event information.\n"); + + for (event_info* e : g_events) + delete e; + + g_events.clear(); } // ============================================================================ // 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; +event_info* find_event_by_index (int idx) +{ + return g_events[idx]; } // ============================================================================ // Finds an event definition by name -EventDef* FindEventByName (string a) { - EventDef* e; - for (e = g_EventDef; e->next != null; e = e->next) { +event_info* find_event_by_name (string a) +{ + for (event_info* e : g_events) if (a.to_uppercase() == e->name.to_uppercase()) return e; - } - + return null; -} \ No newline at end of file +}
--- a/src/events.h Fri Jan 10 21:58:42 2014 +0200 +++ b/src/events.h Sat Jan 11 22:36:31 2014 +0200 @@ -1,57 +1,46 @@ /* - * 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. - */ + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 BOTC_EVENTS_H #define BOTC_EVENTS_H #include "str.h" -struct EventDef { +struct event_info +{ string name; int number; - EventDef* next; }; -void ReadEvents (); -void UnlinkEvents (EventDef* e); -EventDef* FindEventByIdx (unsigned int idx); -EventDef* FindEventByName (string a); +void init_events(); +event_info* find_event_by_index (int idx); +event_info* find_event_by_name (string a); -#endif // BOTC_EVENTS_H \ No newline at end of file +#endif // BOTC_EVENTS_H
--- a/src/format.cc Fri Jan 10 21:58:42 2014 +0200 +++ b/src/format.cc Sat Jan 11 22:36:31 2014 +0200 @@ -1,3 +1,33 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 <cstdio> #include "main.h" #include "format.h" @@ -41,13 +71,13 @@ char mod = '\0'; // handle modifiers - if (fmt[ pos + ofs ] == 's' || fmt[ pos + ofs ] == 'x') + if (fmt[pos + ofs] == 's' || fmt[pos + ofs] == 'x') { mod = fmt[ pos + ofs ]; ofs++; } - if (!isdigit (fmt[ pos + ofs ])) + if (!isdigit (fmt[pos + ofs])) { fprintf (stderr, "bad format string, expected digit with optional " "modifier after '%%':\n"); @@ -55,7 +85,7 @@ return fmt; } - int i = fmt[ pos + ofs ] - '0'; + int i = fmt[pos + ofs] - '0'; if (i >= args.size()) { @@ -91,4 +121,4 @@ { string out = format_args (args); fprintf (fp, "%s", out.chars()); -} +} \ No newline at end of file
--- a/src/format.h Fri Jan 10 21:58:42 2014 +0200 +++ b/src/format.h Sat Jan 11 22:36:31 2014 +0200 @@ -1,3 +1,33 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 BOTC_FORMAT_H #define BOTC_FORMAT_H @@ -7,35 +37,10 @@ class format_arg { public: - format_arg (const string& a) - { - m_string = a; - } - - format_arg (int a) - { - m_string.sprintf ("%d", a); - } - - format_arg (long a) - { - m_string.sprintf ("%ld", a); - } - - format_arg (uint a) - { - m_string.sprintf ("%u", a); - } - - format_arg (ulong a) - { - m_string.sprintf ("%lu", a); - } - - format_arg (const char* a) - { - m_string = a; - } + format_arg (const string& a) : m_string (a) {} + format_arg (int a) : m_string (a) {} + format_arg (long a) : m_string (a) {} + format_arg (const char* a) : m_string (a) {} format_arg (void* a) { @@ -68,30 +73,6 @@ m_string += " }"; } -/* - template<class T, class R> format_arg (const std::map<T, R>& a) - { - if (a.size() == 0) - { - m_string = "{}"; - return; - } - - m_string = "{ "; - - for (std::pair<T, R> it : a) - { - if (m_string != "{ ") - m_string += ", "; - - m_string += format_arg (it.first).as_string() + "=" + - format_arg (it.second).as_string(); - } - - m_string += " }"; - } -*/ - inline const string& as_string() const { return m_string; @@ -123,27 +104,28 @@ void do_fatal (const list<format_arg>& args); #ifndef IN_IDE_PARSER -#define format(...) format_args({ __VA_ARGS__ }) -#define fprint(A, ...) print_args( A, { __VA_ARGS__ }) -#define print(...) print_args( stdout, { __VA_ARGS__ }) -#define fatal(...) do_fatal({ __VA_ARGS__ }) +# define format(...) format_args({ __VA_ARGS__ }) +# define fprint(A, ...) print_args( A, { __VA_ARGS__ }) +# define print(...) print_args( stdout, { __VA_ARGS__ }) #else string format (void, ...); void fprint (FILE* fp, ...); void print (void, ...); -void fatal (void, ...); #endif #ifndef IN_IDE_PARSER # ifdef DEBUG -# define devf(...) fprint( stderr, __VA_ARGS__ ) -# define dvalof( A ) fprint( stderr, "value of '%1' = %2\n", #A, A ) +# define devf(...) fprint (stderr, __VA_ARGS__) +# define dvalof( A ) fprint (stderr, "value of '%1' = %2\n", #A, A) # else # define devf(...) # define dvalof( A ) # endif // DEBUG #else +// print something in debug builds void devf (void, ...); + +// print the value of @a void dvalof (void a); #endif // IN_IDE_PARSER
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lexer.cc Sat Jan 11 22:36:31 2014 +0200 @@ -0,0 +1,253 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 <cstring> +#include "lexer.h" + +static string_list g_file_name_stack; +static lexer* g_main_lexer = null; + +lexer::lexer() +{ + assert (g_main_lexer == null); + g_main_lexer = this; + devf ("Lexer initialized\n"); +} + +lexer::~lexer() +{ + g_main_lexer = null; + devf ("Lexer de-initialized\n"); +} + +void lexer::process_file (string file_name) +{ + devf ("Lexer: processing %1\n", file_name); + FILE* fp = fopen (file_name, "r"); + + if (fp == null) + error ("couldn't open %1 for reading: %2", file_name, strerror (errno)); + + lexer_scanner sc (fp); + + while (sc.get_next_token()) + { + // Preprocessor commands: + if (sc.get_e_token() == tk_hash) + { + must_get_next_from_scanner (sc, tk_symbol); + + if (sc.get_token_text() == "include") + { + devf ("Lexer: encountered #include\n"); + + must_get_next_from_scanner (sc, tk_string); + string file_name = sc.get_token_text(); + + if (g_file_name_stack.contains (file_name)) + error ("attempted to #include %1 recursively", sc.get_token_text()); + + process_file (file_name); + } + else + error ("unknown preprocessor directive \"#%1\"", sc.get_token_text()); + } + else + { + token tok; + tok.file = file_name; + tok.line = sc.get_line(); + tok.column = sc.get_column(); + tok.type = sc.get_e_token(); + tok.text = sc.get_token_text(); + m_tokens << tok; + devf ("Lexer: added %1 (%2)\n", describe_e_token (tok.type), + describe_token (&tok)); + } + } + + devf ("Lexer: File %1 processed.\n", file_name); + m_token_position = m_tokens.begin() - 1; +} + +// ============================================================================= +// +bool lexer::get_next (e_token req) +{ + iterator pos = m_token_position; + devf ("Lexer: Requested next token, requirement: %1\n", describe_e_token (req)); + + if (is_at_end()) + { + devf ("Lexer: at end of tokens. Failed.\n"); + return false; + } + + m_token_position++; + + if (req != tk_any && get_token() != req) + { + devf ("Lexer: Token %1 does not meet the requirement\n", describe_token (get_token())); + m_token_position = pos; + return false; + } + + devf ("Lexer: Get successful: %1\n", describe_token (get_token())); + return true; +} + +// ============================================================================= +// +void lexer::must_get_next (e_token tok) +{ + if (!get_next()) + error ("unexpected EOF"); + + must_be (tok); +} + +// ============================================================================= +// eugh.. +// +void lexer::must_get_next_from_scanner (lexer_scanner& sc, e_token tok) +{ + if (!sc.get_next_token()) + error ("unexpected EOF"); + + if (tok != tk_any && sc.get_e_token() != tok) + error ("expected %1, got %2", describe_e_token (tok), + describe_token (get_token())); +} + +// ============================================================================= +// +void lexer::must_get_any_of (const list<e_token>& toks) +{ + devf ("Lexer: need to get a token that is any of: %1\n", toks); + + if (!get_next()) + error ("unexpected EOF"); + + for (e_token tok : toks) + if (get_token() == tok) + return; + + string toknames; + + for (const e_token& tok_type : toks) + { + if (&tok_type == &toks.last()) + toknames += " or "; + elif (toknames.is_empty() == false) + toknames += ", "; + + toknames += describe_e_token (tok_type); + } + + error ("expected %1, got %2", toknames, describe_token (get_token())); +} + +// ============================================================================= +// +int lexer::get_one_symbol (const string_list& syms) +{ + if (!get_next()) + error ("unexpected EOF"); + + if (get_token() == tk_symbol) + { + for (int i = 0; i < syms.size(); ++i) + { + if (syms[i] == get_token()->text) + return i; + } + } + + error ("expected one of %1, got %2", syms, describe_token (get_token())); + return -1; +} + +// ============================================================================= +// +void lexer::must_be (e_token tok) +{ + if (get_token() != tok) + error ("expected %1, got %2", describe_e_token (tok), + describe_token (get_token())); +} + +// ============================================================================= +// +string lexer::describe_token_private (e_token tok_type, lexer::token* tok) +{ + if ( (int) tok_type < (int) last_named_token) + return "\"" + lexer_scanner::get_token_string (tok_type) + "\""; + + switch (tok_type) + { + case tk_symbol: + return tok ? tok->text : "a symbol"; + + case tk_number: + return tok ? tok->text : "a number"; + + case tk_string: + return tok ? ("\"" + tok->text + "\"") : "a string"; + + case tk_any: + return tok ? tok->text : "any token"; + + default: + break; + } + + return ""; +} + +// ============================================================================= +// +bool lexer::peek_next (lexer::token* tk) +{ + iterator pos = m_token_position; + bool r = get_next(); + + if (r && tk != null) + *tk = *m_token_position; + + m_token_position = pos; + return r; +} + +// ============================================================================= +// +lexer* lexer::get_main_lexer() +{ + return g_main_lexer; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lexer.h Sat Jan 11 22:36:31 2014 +0200 @@ -0,0 +1,104 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 LEXER_H +#define LEXER_H + +#include "main.h" +#include "lexer_scanner.h" + +class lexer +{ +types: + struct token + { + e_token type; + string text; + string file; + int line; + int column; + }; + + using token_list = list<token>; + using iterator = token_list::iterator; + +public: + lexer(); + ~lexer(); + + void process_file (string file_name); + bool get_next (e_token req = tk_any); + void must_get_next (e_token tok); + void must_get_any_of (const list<e_token>& toks); + int get_one_symbol (const string_list& syms); + void must_be (e_token tok); + bool peek_next (token* tk = null); + + inline token* get_token() const + { + assert (is_at_end() == false); + return & (*m_token_position); + } + + inline bool is_at_end() const + { + return m_token_position == m_tokens.end(); + } + + inline token get_token_type() const + { + return get_token()->type; + } + + // If @tok is given, describes the token. If not, describes @tok_type. + static inline string describe_token_type (e_token tok_type) + { + return describe_token_private (tok_type, null); + } + + static inline string describe_token (token* tok) + { + return describe_token_private (tok->type, tok); + } + + static lexer* get_main_lexer(); + void skip(); + +private: + token_list m_tokens; + iterator m_token_position; + + // read a mandatory token from scanner + void must_get_next_from_scanner (lexer_scanner& sc, e_token tok = tk_any); + + static string describe_token_private (e_token tok_type, token* tok); +}; + +#endif // LEXER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lexer_scanner.cc Sat Jan 11 22:36:31 2014 +0200 @@ -0,0 +1,249 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 <cstdio> +#include <cstdlib> +#include <cassert> +#include <cstring> +#include <string> +#include "lexer_scanner.h" + +static const string g_token_strings[] = +{ + "==", + "[]", + "+=", + "-=", + "*=", + "/=", + "%=", + "'", + "$", + "(", + ")", + "[", + "]", + "{", + "}", + "=", + "+", + "-", + "*", + "/", + "%", + ",", + "<", + ">", + ".", + ":", + ";", + "#", + "!", + "->", + "bool", + "break", + "case", + "continue", + "const", + "default", + "do", + "else", + "event", + "for", + "goto", + "if", + "int", + "mainloop", + "onenter", + "onexit", + "state", + "switch", + "str" + "void", + "while", + "enum", + "func", + "return", +}; + +static_assert (countof (g_token_strings) == (int) last_named_token + 1, + "Count of g_token_strings is not the same as the amount of named token identifiers."); + +// ============================================================================= +// +lexer_scanner::lexer_scanner (FILE* fp) : + m_line (1) +{ + long fsize, bytes; + + fseek (fp, 0l, SEEK_END); + fsize = ftell (fp); + rewind (fp); + m_data = new char[fsize]; + m_ptr = m_line_break_pos = &m_data[0]; + bytes = fread (m_data, 1, fsize, fp); + assert (bytes >= fsize); +} + +// ============================================================================= +// +lexer_scanner::~lexer_scanner() +{ + delete m_data; +} + +// ============================================================================= +// +bool lexer_scanner::check_string (const char* c, int flags) +{ + bool r = strncmp (m_ptr, c, strlen (c)) == 0; + + // There is to be whitespace after words + if (r && (flags & f_check_word) && !isspace (m_ptr[strlen (c)])) + r = false; + + // Advance the cursor unless we want to just peek + if (r && ! (flags & f_check_peek)) + m_ptr += strlen (c); + + return r; +} + +// ============================================================================= +// +bool lexer_scanner::get_next_token() +{ + m_token_text = ""; + + while (isspace (*m_ptr) == true) + { + if (*m_ptr == '\n') + { + m_line++; + m_line_break_pos = m_ptr; + } + + m_ptr++; + } + + if (*m_ptr == '\0') + return false; + + // Check tokens + for (int i = 0; i < (int) (sizeof g_token_strings / sizeof * g_token_strings); ++i) + { + if (check_string (g_token_strings[i], f_check_word)) + { + m_token_text = g_token_strings[i]; + m_e_token = (e_token) i; + return true; + } + } + + // Check and parse string + if (*m_ptr == '\"') + { + m_ptr++; + + while (*m_ptr != '\"') + { + if (!*m_ptr) + return false; + + if (check_string ("\\n")) + { + m_token_text += '\n'; + continue; + } + elif (check_string ("\\t")) + { + m_token_text += '\t'; + continue; + } + elif (check_string ("\\\"")) + { + m_token_text += '"'; + continue; + } + + m_token_text += *m_ptr++; + } + + m_e_token = tk_string; + m_ptr++; // skip the final quote + return true; + } + + m_e_token = tk_symbol; + + if (isdigit (*m_ptr)) + { + while (isdigit (*m_ptr)) + m_token_text += *m_ptr++; + + m_e_token = tk_number; + return true; + } + + if (is_symbol_char (*m_ptr)) + { + while (m_ptr != '\0') + { + if (!is_symbol_char (*m_ptr)) + break; + + bool stop_here = false; + + for (string i : g_token_strings) + { + if (check_string (i, f_check_peek | f_check_word)) + { + stop_here = true; + break; + } + } + + if (stop_here) + break; + + m_token_text += *m_ptr++; + } + + return true; + } + + return false; +} + +string lexer_scanner::get_token_string (e_token a) +{ + assert ((int) a <= (int) last_named_token); + return g_token_strings[a]; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lexer_scanner.h Sat Jan 11 22:36:31 2014 +0200 @@ -0,0 +1,107 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 IRIS_SCANNER_H +#define IRIS_SCANNER_H + +#include <climits> +#include "main.h" + +class lexer_scanner +{ + types: + struct position_info + { + char* pos; + char* line_break_pos; + int line; + }; + + // Flags for check_string() + enum + { + f_check_word = (1 << 0), // must be followed by whitespace + f_check_peek = (1 << 1), // don't advance cursor + }; + + public: + static inline bool is_symbol_char (char c) + { + return (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c == '_'); + } + + lexer_scanner (FILE* fp); + ~lexer_scanner(); + bool get_next_token(); + + inline const string& get_token_text() const + { + return m_token_text; + } + + inline int get_line() const + { + return m_line; + } + + inline int get_column() const + { + return m_ptr - m_line_break_pos; + } + + inline e_token get_e_token() const + { + return m_e_token; + } + + static string get_token_string (e_token a); + + private: + char* m_data, + * m_ptr, + * m_line_break_pos; + string m_token_text, + m_last_token; + e_token m_e_token; + int m_line; + + bool check_string (const char* c, int flags = 0); + + // Yields a copy of the current position information. + position_info get_position() const; + + // Sets the current position based on given data. + void set_position (const position_info& a); +}; + +#endif // IRIS_SCANNER_H +
--- a/src/main.cxx Fri Jan 10 21:58:42 2014 +0200 +++ b/src/main.cxx Sat Jan 11 22:36:31 2014 +0200 @@ -1,64 +1,49 @@ /* - * 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. - */ + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. -#include <stdio.h> -#include <stdlib.h> -#include <string.h> + 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 <COPYRIGHT HOLDER> 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 "main.h" - -#include "str.h" #include "scriptreader.h" -#include "objwriter.h" +#include "object_writer.h" #include "events.h" #include "commands.h" #include "stringtable.h" #include "variables.h" #include "containers.h" -#include "databuffer.h" - +#include "data_buffer.h" #include "bots.h" -#include "botcommands.h" +#include "object_writer.h" +#include "parser.h" // List of keywords -const string_list g_Keywords = { +const string_list g_Keywords = +{ "bool", "break", "case", @@ -80,7 +65,7 @@ "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. @@ -91,24 +76,26 @@ // databuffer global variable int g_NextMark = 0; -int main (int argc, char** argv) { +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 (); + if (argc == 2 && !strcmp (argv[1], "-l")) + { + ReadCommands(); printf ("Begin list of commands:\n"); printf ("------------------------------------------------------\n"); - + CommandDef* comm; ITERATE_COMMANDS (comm) print ("%1\n", GetCommandPrototype (comm)); - + printf ("------------------------------------------------------\n"); printf ("End of command list\n"); exit (0); } - + // Print header string header; string headerline; @@ -119,82 +106,83 @@ headerline += '-'; print ("%1\n%2\n", header, headerline); - - if (argc < 2) { + + 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)); - + string 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)) { + if (fexists (outfile)) + { // Additional warning if the paths are the same string warning; #ifdef FILE_CASEINSENSITIVE - if (!outfile.icompare (argv[1])) + + if (+outfile == +string (argv[1])) #else - if (!outfile.compare (argv[1])) + if (outfile == 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') { + fgets (&ans, 1, stdin); + + if (ans != 'y') + { printf ("abort\n"); exit (1); } } - + // Read definitions printf ("Reading definitions...\n"); - ReadEvents (); - ReadCommands (); - + ReadEvents(); + ReadCommands(); + // Prepare reader and writer - ScriptReader* r = new ScriptReader (argv[1]); - ObjWriter* w = new ObjWriter (outfile); - + botscript_parser* r = new botscript_parser; + object_writer* w = new object_writer; + // We're set, begin parsing :) printf ("Parsing script...\n"); - r->ParseBotScript (w); + r->parse_botscript (argv[1], w); printf ("Script parsed successfully.\n"); - + // Parse done, print statistics and write to file - unsigned int globalcount = g_GlobalVariables.size(); - unsigned int stringcount = num_strings_in_table (); - 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 (); - + int globalcount = g_GlobalVariables.size(); + int stringcount = num_strings_in_table(); + int NumMarks = w->MainBuffer->count_marks(); + int NumRefs = w->MainBuffer->count_references(); + print ("%1 / %2 strings written\n", stringcount, MAX_LIST_STRINGS); + print ("%1 / %2 global variables\n", globalcount, MAX_SCRIPT_VARIABLES); + print ("%1 / %2 bytecode marks\n", NumMarks, MAX_MARKS); // TODO: nuke + print ("%1 / %2 bytecode references\n", NumRefs, MAX_MARKS); // TODO: nuke + print ("%1 / %2 events\n", g_NumEvents, MAX_NUM_EVENTS); + print ("%1 state%s1\n", g_NumStates); + + w->write_to_file (outfile); + // Clear out the junk delete r; delete w; - + // Done! exit (0); } @@ -204,8 +192,10 @@ // ============================================================================ // Does the given file exist? -bool fexists (string path) { - if (FILE* test = fopen (path, "r")) { +bool fexists (string path) +{ + if (FILE* test = fopen (path, "r")) + { fclose (test); return true; } @@ -215,7 +205,8 @@ // ============================================================================ // Generic error -void error (const char* text, ...) { +void error (const char* text, ...) +{ va_list va; va_start (va, text); fprintf (stderr, "error: "); @@ -226,32 +217,37 @@ // ============================================================================ // Mutates given filename to an object filename -string ObjectFileName (string s) { +string ObjectFileName (string s) +{ // Locate the extension and chop it out int extdot = s.last ("."); if (extdot >= s.len() - 4) s -= (s.len() - extdot); - + s += ".o"; return s; } // ============================================================================ // Is the given argument a reserved keyword? -bool IsKeyword (string s) { - for (unsigned int u = 0; u < NumKeywords (); u++) +bool IsKeyword (string s) +{ + for (int u = 0; u < NumKeywords(); u++) if (s.to_uppercase() == g_Keywords[u].to_uppercase()) return true; + return false; } -unsigned int NumKeywords () { +int NumKeywords() +{ return sizeof (g_Keywords) / sizeof (const char*); } // ============================================================================ -type_e GetTypeByName (string t) { +type_e GetTypeByName (string t) +{ t = t.to_lowercase(); return (t == "int") ? TYPE_INT : (t == "str") ? TYPE_STRING : @@ -263,14 +259,20 @@ // ============================================================================ // Inverse operation - type name by value -string GetTypeName (type_e type) { - switch (type) { +string 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/src/main.h Fri Jan 10 21:58:42 2014 +0200 +++ b/src/main.h Sat Jan 11 22:36:31 2014 +0200 @@ -1,3 +1,33 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 BOTC_COMMON_H #define BOTC_COMMON_H @@ -13,6 +43,7 @@ #include "str.h" #include "containers.h" #include "format.h" +#include "tokens.h" // Application name and version #define APPNAME "botc" @@ -49,13 +80,14 @@ exit (1); \ } -// Plural expression -#define PLURAL(n) (n != 1) ? "s" : "" +#define types public +#define countof(A) ((int) (sizeof A / sizeof *A)) +#define autocast(A) (decltype(A)) +#define assign_autocast(A,B) a = autocast(A) b // Shortcut for zeroing something #define ZERO(obj) memset (&obj, 0, sizeof (obj)); -void error (const char* text, ...); string ObjectFileName (string s); bool fexists (string path); type_e GetTypeByName (string token); @@ -76,7 +108,7 @@ #define deprecated __attribute__ ((deprecated)) // Power function -template<class T> T pow (T a, unsigned int b) { +template<class T> T pow (T a, int b) { if (!b) return 1; @@ -97,7 +129,7 @@ extern const char** g_Keywords; bool IsKeyword (string s); -unsigned int NumKeywords (); +int NumKeywords (); // Script mark and reference struct ScriptMark { @@ -106,25 +138,25 @@ }; struct ScriptMarkReference { - unsigned int num; + 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; +template <class T> union generic_union { + T as_t; + byte as_bytes[sizeof (T)]; + char as_chars[sizeof (T)]; + double as_double; + float as_float; + int as_int; + word as_word; }; // ==================================================================== // Finds a byte in the given value. -template <class T> inline unsigned char GetByteIndex (T a, unsigned int b) { +template <class T> inline unsigned char GetByteIndex (T a, int b) { union_t<T> uni; uni.val = a; return uni.b[b];
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/object_writer.cxx Sat Jan 11 22:36:31 2014 +0200 @@ -0,0 +1,193 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 "main.h" +#include "str.h" +#include "object_writer.h" +#include "data_buffer.h" +#include "stringtable.h" +#include "bots.h" + +extern bool g_GotMainLoop; + +object_writer::ObjectWriter (string path) +{ + MainBuffer = new data_buffer; + MainLoopBuffer = new data_buffer; + OnEnterBuffer = new data_buffer; + SwitchBuffer = null; // created on demand + numWrittenBytes = 0; + numWrittenReferences = 0; +} + +void object_writer::write_string (string a) +{ + write (a.length()); + + for (int i = 0; i < a.length(); i++) + write (a[i]); +} + +void object_writer::write_buffer (data_buffer* buf) +{ + get_current_buffer()->merge (buf); +} + +void object_writer::write_member_buffers() +{ + // 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++) + { + data_buffer** buf = (!i) ? &OnEnterBuffer : &MainLoopBuffer; + write_buffer (*buf); + + // Clear the buffer afterwards for potential next state + *buf = new data_buffer; + } + + // Next state definitely has no mainloop yet + g_GotMainLoop = false; +} + +// Write string table +void object_writer::write_string_table() +{ + int stringcount = num_strings_in_table(); + + if (!stringcount) + return; + + // Write header + write (DH_STRINGLIST); + write (stringcount); + + // Write all strings + for (int a = 0; a < stringcount; a++) + write_string (get_string_table() [a]); +} + +// Write main buffer to file +void object_writer::write_to_file (string filepath) +{ + fp = fopen (filepath, "w"); + CHECK_FILE (fp, filepath, "writing"); + + // First, resolve references + numWrittenReferences = 0; + + for (int u = 0; u < MAX_MARKS; u++) + { + ScriptMarkReference* ref = MainBuffer->refs[u]; + + if (!ref) + continue; + + // Substitute the placeholder with the mark position + generic_union<word> uni; + uni.as_word = static_cast<word> (MainBuffer->marks[ref->num]->pos); + + for (int v = 0; v < sizeof (word); v++) + memset (MainBuffer->buffer + ref->pos + v, uni.as_bytes[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 (int x = 0; x < MainBuffer->writesize; x++) + write_data_to_file<byte> (*(MainBuffer->buffer + x)); + + print ("-- %1 byte%s1 written to %2\n", numWrittenBytes, filepath); + fclose (fp); +} + +data_buffer* object_writer::get_current_buffer() +{ + return SwitchBuffer ? SwitchBuffer : + (g_CurMode == MODE_MAINLOOP) ? MainLoopBuffer : + (g_CurMode == MODE_ONENTER) ? OnEnterBuffer : + MainBuffer; +} + +ScriptMark* g_ScriptMark = null; + +// Adds a mark +int object_writer::add_mark (string name) +{ + return get_current_buffer()->add_mark (name); +} + +// Adds a reference +int object_writer::add_reference (int mark) +{ + data_buffer* b = get_current_buffer(); + return b->add_reference (mark); +} + +// Finds a mark +int object_writer::find_byte_mark (string name) +{ + data_buffer* b = get_current_buffer(); + + for (int u = 0; u < MAX_MARKS; u++) + { + if (b->marks[u] && b->marks[u]->name.to_uppercase() == name.to_uppercase()) + return u; + } + + return MAX_MARKS; +} + +// Moves a mark to the current position +void object_writer::move_mark (int mark) +{ + get_current_buffer()->move_mark (mark); +} + +// Deletes a mark +void object_writer::delete_mark (int mark) +{ + get_current_buffer()->delete_mark (mark); +} + +void object_writer::offset_mark (int mark, int offset) +{ + get_current_buffer()->offset_mark (mark, offset); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/object_writer.h Sat Jan 11 22:36:31 2014 +0200 @@ -0,0 +1,110 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 BOTC_OBJWRITER_H +#define BOTC_OBJWRITER_H + +#include <stdio.h> +#include <typeinfo> +#include <string.h> +#include "main.h" +#include "str.h" +#include "data_buffer.h" + +class object_writer +{ +public: + // ==================================================================== + // MEMBERS + + // Pointer to the file we're writing to + FILE* fp; + + // The main buffer - the contents of this is what we + // write to file after parsing is complete + data_buffer* MainBuffer; + + // onenter buffer - the contents of the onenter{} block + // is buffered here and is merged further at the end of state + data_buffer* OnEnterBuffer; + + // Mainloop buffer - the contents of the mainloop{} block + // is buffered here and is merged further at the end of state + data_buffer* MainLoopBuffer; + + // Switch buffer - switch case data is recorded to this + // buffer initially, instead of into main buffer. + data_buffer* SwitchBuffer; + + // How many bytes have we written to file? + int numWrittenBytes; + + // How many references did we resolve in the main buffer? + int numWrittenReferences; + + // ==================================================================== + // METHODS + object_writer(); + void write_string (string s); + void write_buffer (data_buffer* buf); + void write_member_buffers(); + void write_string_table(); + void write_to_file (string filepath); + data_buffer* get_current_buffer(); + + int add_mark (string name); + int find_byte_mark (string name); + int add_reference (int mark); + void move_mark (int mark); + void offset_mark (int mark, int offset); + void delete_mark (int mark); + + template <class T> void write (T stuff) + { + get_current_buffer()->write (stuff); + } + +private: + // Write given data to file. + template <class T> void write_data_to_file (T stuff) + { + // One byte at a time + generic_union<T> uni; + uni.as_t = stuff; + + for (int x = 0; x < sizeof (T); x++) + { + fwrite (&uni.as_bytes[x], 1, 1, fp); + numWrittenBytes++; + } + } +}; + +#endif // BOTC_OBJWRITER_H
--- a/src/objwriter.cxx Fri Jan 10 21:58:42 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,152 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "main.h" -#include "str.h" -#include "objwriter.h" -#include "databuffer.h" -#include "stringtable.h" - -#include "bots.h" - -extern bool g_GotMainLoop; - -ObjWriter::ObjWriter (string 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 (string 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 = num_strings_in_table (); - if (!stringcount) - return; - - // Write header - Write (DH_STRINGLIST); - Write (stringcount); - - // Write all strings - for (unsigned int a = 0; a < stringcount; a++) - WriteString (get_string_table()[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 (string 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 (string name) { - DataBuffer* b = GetCurrentBuffer(); - for (unsigned int u = 0; u < MAX_MARKS; u++) { - if (b->marks[u] && b->marks[u]->name.to_uppercase() == name.to_uppercase()) - 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/src/objwriter.h Fri Jan 10 21:58:42 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,89 +0,0 @@ -#ifndef BOTC_OBJWRITER_H -#define BOTC_OBJWRITER_H - -#include <stdio.h> -#include <typeinfo> -#include <string.h> -#include "main.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 - string 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 (string path); - void WriteString (char* s); - void WriteString (const char* s); - void WriteString (string s); - void WriteBuffer (DataBuffer* buf); - void WriteBuffers (); - void WriteStringTable (); - void WriteToFile (); - DataBuffer* GetCurrentBuffer (); - - unsigned int AddMark (string name); - unsigned int FindMark (string 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 Write (T stuff) { - GetCurrentBuffer ()->Write (stuff); - } - - // Default to word - void Write (word stuff) { - Write<word> (stuff); - } - - void DoWrite (byte stuff) { - Write<byte> (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 // BOTC_OBJWRITER_H \ No newline at end of file
--- a/src/parser.cxx Fri Jan 10 21:58:42 2014 +0200 +++ b/src/parser.cxx Sat Jan 11 22:36:31 2014 +0200 @@ -1,792 +1,943 @@ -#include "objwriter.h" -#include "scriptreader.h" +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 "object_writer.h" +#include "parser.h" #include "events.h" #include "commands.h" #include "stringtable.h" #include "variables.h" #include "containers.h" - -#define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \ - ParserError ("%s-statements may only be defined at top level!", token.chars()); +#include "lexer.h" -#define MUST_NOT_TOPLEVEL if (g_CurMode == MODE_TOPLEVEL) \ - ParserError ("%s-statements may not be defined at top level!", token.chars()); - +#define TOKEN (string (m_lx->get_token()->string)) #define SCOPE(n) scopestack[g_ScopeCursor - n] +// TODO: make these static int g_NumStates = 0; int g_NumEvents = 0; parsermode_e g_CurMode = MODE_TOPLEVEL; string g_CurState = ""; bool g_stateSpawnDefined = false; bool g_GotMainLoop = false; -unsigned int g_ScopeCursor = 0; -DataBuffer* g_IfExpression = null; +int g_ScopeCursor = 0; +data_buffer* g_IfExpression = null; bool g_CanElse = false; string* g_UndefinedLabels[MAX_MARKS]; -bool g_Neurosphere = false; // neurosphere-compat -list<constinfo_t> g_ConstInfo; +list<constant_info> g_ConstInfo; + +static botscript_parser* g_current_parser = null; + +// ============================================================================ +// +botscript_parser::botscript_parser() : + m_lx (new lexer) {} + +// ============================================================================ +// +botscript_parser::~botscript_parser() +{ + delete m_lx; +} + +// ============================================================================ +// +void botscript_parser::check_toplevel() +{ + if (g_CurMode != MODE_TOPLEVEL) + error ("%1-statements may only be defined at top level!", TOKEN.chars()); +} + +// ============================================================================ +// +void botscript_parser::check_not_toplevel() +{ + if (g_CurMode == MODE_TOPLEVEL) + error ("%1-statements must not be defined at top level!", TOKEN.chars()); +} // ============================================================================ // 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 +// 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) { +void botscript_parser::parse_botscript (string file_name, object_writer* w) +{ + // Lex and preprocess the file + m_lx->process_file (file_name); + // Zero the entire block stack first for (int i = 0; i < MAX_SCOPE; i++) - ZERO(scopestack[i]); - + ZERO (scopestack[i]); + for (int i = 0; i < MAX_MARKS; i++) g_UndefinedLabels[i] = null; - - while (Next()) { + + while (m_lx->get_next()) + { // Check if else is potentically valid - if (token == "else" && !g_CanElse) - ParserError ("else without preceding if"); - if (token != "else") + if (TOKEN == "else" && !g_CanElse) + error ("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()); - string 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.is_numeric()) - ParserError ("variable name must not be a number"); - - string 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 string (token); + + switch (m_lx->get_token()) + { + case tk_state: + { + check_toplevel(); + m_lx->must_get_next (tk_string); + + // State name must be a word. + if (TOKEN.first (" ") != -1) + error ("state name must be a single word, got `%1`", TOKEN); + + string 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 + m_lx->must_get_next (tk_colon); + + // write the previous state's onenter and + // mainloop buffers to file now + if (g_CurState.len()) + w->write (Buffers(); + + w->write (DH_STATENAME); + w->write_string (statename); + w->write (DH_STATEIDX); + w->write (g_NumStates); + + g_NumStates++; + g_CurState = TOKEN; + g_GotMainLoop = false; } - - // 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 + break; + + // ============================================================ + // + case tk_event: + { + check_toplevel(); + + // Event definition + m_lx->must_get_next (tk_string); + + event_info* e = find_event_by_name (token_string()); + + if (!e) + error ("bad event, got `%1`\n", token_string()); + + m_lx->must_get_next (tk_brace_start); + g_CurMode = MODE_EVENT; + w->write (DH_EVENT); + w->write (e->number); + g_NumEvents++; + continue; + } break; + + // ============================================================ + // + case tk_mainloop: + { + check_toplevel(); + m_lx->must_get_next (tk_brace_start); + + // Mode must be set before dataheader is written here! + g_CurMode = MODE_MAINLOOP; + w->write (DH_MAINLOOP); + } + break; + + // ============================================================ // - // 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; - } + case tk_onenter: + case tk_onexit: + { + check_toplevel(); + bool onenter = (m_lx->get_token() == "onenter"); + m_lx->must_get_next (tk_brace_start); + + // 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); } - - // 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", token.c_str()); - - 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; + break; + + // ============================================================ + // + case tk_int: + case tk_str: + case tk_void: + { + // For now, only globals are supported + if (g_CurMode != MODE_TOPLEVEL || g_CurState.len()) + error ("variables must only be global for now"); + + type_e type = (TOKEN == "int") ? TYPE_INT : + (TOKEN == "str") ? TYPE_STRING : + TYPE_BOOL; + + m_lx->must_get_next(); + + // Var name must not be a number + if (TOKEN.is_numeric()) + error ("variable name must not be a number"); + + string varname = TOKEN; + script_variable* var = DeclareGlobalVariable (this, type, varname); + m_lx->must_get_next (tk_semicolon); } - - 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; + break; + + // ============================================================ + // + case tk_goto: + { + check_not_toplevel(); + + // Get the name of the label + m_lx->must_get_next(); + + // Find the mark this goto statement points to + int m = w->find_byte_mark (TOKEN); + + // If not set, define it + if (m == MAX_MARKS) + { + m = w->add_mark (TOKEN); + g_UndefinedLabels[m] = new string (TOKEN); } - 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--; + + // Add a reference to the mark. + w->write (DH_GOTO); + w->add_reference (m); + m_lx->must_get_next (tk_semicolon); 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; + + // ============================================================ + // + case tk_if: + { + check_not_toplevel(); + push_scope(); + + // Condition + m_lx->must_get_next (tk_paren_start); + + // Read the expression and write it. + m_lx->must_get_next(); + data_buffer* c = parse_expression (TYPE_INT); + w->write_buffer (c); + + m_lx->must_get_next (tk_paren_end); + m_lx->must_get_next (tk_brace_start); + + // Add a mark - to here temporarily - and add a reference to it. + // Upon a closing brace, the mark will be adjusted. + int marknum = w->add_mark (""); + + // 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->add_reference (marknum); + + // Store it + SCOPE (0).mark1 = marknum; + SCOPE (0).type = SCOPETYPE_IF; + } break; + + // ============================================================ + // + case tk_else: + { + check_not_toplevel(); + m_lx->must_get_next (tk_brace_start); + + // Don't use PushScope as it resets the scope + g_ScopeCursor++; + + if (g_ScopeCursor >= MAX_SCOPE) + error ("too deep scope"); + + if (SCOPE (0).type != SCOPETYPE_IF) + error ("else without preceding if"); + + // write down to jump to the end of the else statement + // Otherwise we have fall-throughs + SCOPE (0).mark2 = w->add_mark (""); + + // Instruction to jump to the end after if block is complete + w->write (DH_GOTO); + w->add_reference (SCOPE (0).mark2); + + // Move the ifnot mark here and set type to else + w->move_mark (SCOPE (0).mark1); + SCOPE (0).type = SCOPETYPE_ELSE; + } + break; + + // ============================================================ + // + case tk_while: + { + check_not_toplevel(); + push_scope(); + + // 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. + int mark1 = w->add_mark (""); // start + int mark2 = w->add_mark (""); // end + + // Condition + m_lx->must_get_next (tk_paren_start); + m_lx->must_get_next(); + data_buffer* expr = parse_expression (TYPE_INT); + m_lx->must_get_next (tk_paren_end); + m_lx->must_get_next (tk_brace_start); + + // write condition + w->write_buffer (expr); + + // Instruction to go to the end if it fails + w->write (DH_IFNOTGOTO); + w->add_reference (mark2); + + // Store the needed stuff + SCOPE (0).mark1 = mark1; + SCOPE (0).mark2 = mark2; + SCOPE (0).type = SCOPETYPE_WHILE; + } + break; + + // ============================================================ + // + case tk_for: + { + check_not_toplevel(); + push_scope(); + + // Initializer + m_lx->must_get_next (tk_paren_start); + m_lx->must_get_next(); + data_buffer* init = parse_statement (w); + + if (!init) + error ("bad statement for initializer of for"); + + m_lx->must_get_next (tk_semicolon); + + // Condition + m_lx->must_get_next(); + data_buffer* cond = parse_expression (TYPE_INT); + + if (!cond) + error ("bad statement for condition of for"); + + m_lx->must_get_next (tk_semicolon); + + // Incrementor + m_lx->must_get_next(); + data_buffer* incr = parse_statement (w); + + if (!incr) + error ("bad statement for incrementor of for"); + + m_lx->must_get_next (tk_paren_end); + m_lx->must_get_next (tk_brace_start); + + // First, write out the initializer + w->write_buffer (init); + + // Init two marks + int mark1 = w->add_mark (""); + int mark2 = w->add_mark (""); + + // Add the condition + w->write_buffer (cond); + w->write (DH_IFNOTGOTO); + w->add_reference (mark2); + + // Store the marks and incrementor + SCOPE (0).mark1 = mark1; + SCOPE (0).mark2 = mark2; + SCOPE (0).buffer1 = incr; + SCOPE (0).type = SCOPETYPE_FOR; + } + break; + + // ============================================================ + // + case tk_do: + { + check_not_toplevel(); + push_scope(); + m_lx->must_get_next (tk_brace_start); + SCOPE (0).mark1 = w->add_mark (""); + SCOPE (0).type = SCOPETYPE_DO; + } + break; + + // ============================================================ + // + case tk_switch: + { + // This gets 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 + + check_not_toplevel(); + push_scope(); + m_lx->must_get_next (tk_paren_start); + m_lx->must_get_next(); + w->write_buffer (parse_expression (TYPE_INT)); + m_lx->must_get_next (tk_paren_end); + m_lx->must_get_next (tk_brace_start); + SCOPE (0).type = SCOPETYPE_SWITCH; + SCOPE (0).mark1 = w->add_mark (""); // end mark + SCOPE (0).buffer1 = null; // default header + } + break; + + // ============================================================ + // + case tk_case: + { + // case is only allowed inside switch + if (SCOPE (0).type != SCOPETYPE_SWITCH) + error ("case label outside switch"); + + // Get the literal (Zandronum does not support expressions here) + m_lx->must_get_next (tk_number); + int num = m_lx->get_token()->text.to_long(); + m_lx->must_get_next (tk_colon); + + for (int i = 0; i < MAX_CASE; i++) + if (SCOPE (0).casenumbers[i] == num) + error ("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); + add_switch_case (w, null); + SCOPE (0).casenumbers[SCOPE (0).casecursor] = num; + } + break; + + // ============================================================ + // + case tk_default: + { + if (SCOPE (0).type != SCOPETYPE_SWITCH) + error ("default label outside switch"); + + if (SCOPE (0).buffer1) + error ("multiple default labels in one switch"); + + m_lx->must_get_next (tk_colon); + + // 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. + data_buffer* b = new data_buffer; + SCOPE (0).buffer1 = b; + b->write (DH_DROP); + b->write (DH_GOTO); + add_switch_case (w, b); + } + break; + + // ============================================================ + // + case tk_break: + { + if (!g_ScopeCursor) + error ("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->add_reference (SCOPE (0).mark1); + } break; + + case SCOPETYPE_FOR: + case SCOPETYPE_WHILE: + { + w->add_reference (SCOPE (0).mark2); + } break; + + default: + { + error ("unexpected `break`"); + } break; + } + + m_lx->must_get_next (tk_semicolon); + } + break; + + // ============================================================ + // + case tk_continue: + { + m_lx->must_get_next (tk_semicolon); + + int curs; + bool found = false; + + // Fall 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->add_reference (scopestack[curs].mark1); + found = true; + } break; + + default: + break; + } + } + + // No loop blocks + if (!found) + error ("`continue`-statement not inside a loop"); + } + break; + + case tk_brace_end: + { + // 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->move_mark (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->move_mark (SCOPE (0).mark2); + break; + + case SCOPETYPE_FOR: + // write the incrementor at the end of the loop block + w->write_buffer (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->add_reference (SCOPE (0).mark1); + + // Move the closing mark here since we're at the end of the while loop + w->move_mark (SCOPE (0).mark2); + break; + + case SCOPETYPE_DO: + { + must_get_next (tk_while); + m_lx->must_get_next (tk_paren_start); + m_lx->must_get_next(); + data_buffer* expr = parse_expression (TYPE_INT); + m_lx->must_get_next (tk_paren_end); + m_lx->must_get_next (tk_semicolon); + + // If the condition runs true, go back to the start. + w->write_buffer (expr); + w->write (DH_IFGOTO); + w->add_reference (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->write_buffer (SCOPE (0).buffer1); + else + { + w->write (DH_DROP); + w->write (DH_GOTO); + w->add_reference (SCOPE (0).mark1); + } + + // Go through all of the buffers we + // recorded down and write them. + for (int u = 0; u < MAX_CASE; u++) + { + if (!SCOPE (0).casebuffers[u]) + continue; + + w->move_mark (SCOPE (0).casemarks[u]); + w->write_buffer (SCOPE (0).casebuffers[u]); + } + + // Move the closing mark here + w->move_mark (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) + error ("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() == ";") + m_lx->must_get_next (tk_semicolon); + } + break; + + // ============================================================ + case tk_const: + { + constant_info info; + + // Get the type + m_lx->must_get_next(); + info.type = GetTypeByName (TOKEN); + + if (info.type == TYPE_UNKNOWN || info.type == TYPE_VOID) + error ("unknown type `%s` for constant", TOKEN.c_str()); + + m_lx->must_get_next(); + info.name = TOKEN; + + m_lx->must_get_next (tk_assign); + + switch (info.type) + { + case TYPE_BOOL: + case TYPE_INT: + { + m_lx->must_get_next (tk_number); + info.val = m_lx->get_token()->text.to_long(); + } break; + + case TYPE_STRING: + { + m_lx->must_get_next (tk_string); + info.val = m_lx->get_token()->text; + } break; + + case TYPE_UNKNOWN: + case TYPE_VOID: + break; + } + + g_ConstInfo << info; + + m_lx->must_get_next (tk_semicolon); + continue; + } + + default: + { + // ============================================================ + // Label + lexer::token* next; + if (m_lx->get_token() == tk_symbol && + m_lx->peek_next (next) && + next->type == tk_colon) + { + check_not_toplevel(); + string label_name = token_string(); + + // want no conflicts.. + if (FindCommand (label_name)) + error ("label name `%s` conflicts with command name\n", label_name); + + if (FindGlobalVariable (label_name)) + error ("label name `%s` conflicts with variable\n", label_name); + + // 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] == label_name) + { + mark = i; + w->move_mark (i); + + // No longer undefinde + delete g_UndefinedLabels[i]; + g_UndefinedLabels[i] = null; + } + } + + // Not found in unmarked lists, define it now + if (mark == -1) + w->add_mark (label_name); + + m_lx->must_get_next (tk_colon); + continue; + } + + // Check if it's a command + CommandDef* comm = FindCommand (TOKEN); + + if (comm) + { + w->get_current_buffer()->merge (ParseCommand (comm)); + m_lx->must_get_next (tk_semicolon); + continue; + } + + // ============================================================ + // If nothing else, parse it as a statement + data_buffer* b = parse_statement (w); + + if (!b) + error ("unknown TOKEN `%s`", TOKEN.chars()); + + w->write_buffer (b); + m_lx->must_get_next (tk_semicolon); + } + break; } - - // 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 `}`?"); - + error ("script did not end at top level; a `}` is missing somewhere"); + // stateSpawn must be defined! if (!g_stateSpawnDefined) - ParserError ("script must have a state named `stateSpawn`!"); - + error ("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()); - + error ("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 (); - + w->write_member_buffers(); + // String table - w->WriteStringTable (); + w->write_string_table(); } // ============================================================================ // Parses a command call -DataBuffer* ScriptReader::ParseCommand (CommandDef* comm) { - DataBuffer* r = new DataBuffer(64); +data_buffer* botscript_parser::ParseCommand (CommandDef* comm) +{ + data_buffer* r = new data_buffer (64); + if (g_CurMode == MODE_TOPLEVEL) - ParserError ("command call at top level"); - - MustNext ("("); - MustNext (); - + error ("command call at top level"); + + m_lx->must_get_next (tk_paren_start); + m_lx->must_get_next(); + int curarg = 0; - while (1) { - if (token == ")") { + + while (1) + { + if (m_lx->get_token() == tk_paren_end) + { if (curarg < comm->numargs) - ParserError ("too few arguments passed to %s\n\tprototype: %s", + error ("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", + error ("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) { + + r->merge (parse_expression (comm->argtypes[curarg])); + m_lx->must_get_next(); + + if (curarg < comm->numargs - 1) + { + m_lx->must_be (tk_comma); + m_lx->must_get_next(); + } + else if (curarg < comm->maxargs - 1) + { // Can continue, but can terminate as well. - if (token == ")") { + if (m_lx->get_token() == tk_paren_end) + { curarg++; break; - } else { - MustThis (","); - MustNext (); + } + else + { + m_lx->must_be (tk_comma); + m_lx->must_get_next(); } } - + curarg++; } - + // If the script skipped any optional arguments, fill in defaults. - while (curarg < comm->maxargs) { - r->Write (DH_PUSHNUMBER); - r->Write (comm->defvals[curarg]); + 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); - + + 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; +static bool is_assignment_operator (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)) { +static word get_data_header_by_operator (script_variable* var, int oper) +{ + if (is_assignment_operator (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_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) { + + switch (oper) + { case OPER_ADD: return DH_ADD; case OPER_SUBTRACT: return DH_SUBTRACT; case OPER_MULTIPLY: return DH_MULTIPLY; @@ -806,100 +957,109 @@ 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); - +data_buffer* botscript_parser::parse_expression (type_e reqtype) +{ + data_buffer* retbuf = new data_buffer (64); + // Parse first operand - retbuf->Merge (ParseExprValue (reqtype)); - + retbuf->merge (parse_expr_value (reqtype)); + // Parse any and all operators we get int oper; - while ((oper = ParseOperator (true)) != -1) { + + while ( (oper = parse_operator (true)) != -1) + { // We peeked the operator, move forward now - Next (); - + Next(); + // Can't be an assignement operator, those belong in assignments. - if (IsAssignmentOperator (oper)) - ParserError ("assignment operator inside expression"); - + if (is_assignment_operator (oper)) + error ("assignment operator inside expression"); + // Parse the right operand. - MustNext (); - DataBuffer* rb = ParseExprValue (reqtype); - - if (oper == OPER_TERNARY) { + m_lx->must_get_next(); + data_buffer* rb = parse_expr_value (reqtype); + + if (oper == OPER_TERNARY) + { // Ternary operator requires - naturally - a third operand. - MustNext (":"); - MustNext (); - DataBuffer* tb = ParseExprValue (reqtype); - + m_lx->must_get_next (tk_colon); + m_lx->must_get_next(); + data_buffer* tb = parse_expr_value (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)); + int mark1 = retbuf->add_mark (""); // start of "else" case + int mark2 = retbuf->add_mark (""); // end of expression + retbuf->write (DH_IFNOTGOTO); // if the first operand (condition) + retbuf->add_reference (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->add_reference (mark2); // marked by mark2. + retbuf->move_mark (mark1); // move mark1 at the end of the true case + retbuf->merge (tb); // perform third operand (false case) + retbuf->move_mark (mark2); // move the ending mark2 here + } + else + { + // write to buffer + retbuf->merge (rb); + retbuf->write (get_data_header_by_operator (null, oper)); } } - + return retbuf; } // ============================================================================ // Parses an operator string. Returns the operator number code. #define ISNEXT(C) (PeekNext (peek ? 1 : 0) == C) -int ScriptReader::ParseOperator (bool peek) { +int botscript_parser::parse_operator (bool peek) +{ string oper; + if (peek) - oper += PeekNext (); + oper += PeekNext(); else - oper += token; - + 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) { + (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 : @@ -914,39 +1074,44 @@ (oper == "<<" && !equalsnext) ? OPER_LEFTSHIFT : (oper == ">>" && !equalsnext) ? OPER_RIGHTSHIFT : -1; - - if (o != -1) { - MustNext (); + + if (o != -1) + { + m_lx->must_get_next(); return o; } - + // Three-char opers oper += PeekNext (peek ? 2 : 1); o = oper == "<<=" ? OPER_ASSIGNLEFTSHIFT : oper == ">>=" ? OPER_ASSIGNRIGHTSHIFT : -1; - - if (o != -1) { - MustNext (); - MustNext (); + + if (o != -1) + { + m_lx->must_get_next(); + m_lx->must_get_next(); } - + return o; } // ============================================================================ -string ScriptReader::ParseFloat () { - MustNumber (true); - string floatstring = token; - +string botscript_parser::parse_float() +{ + m_lx->must_be (tk_number); + string floatstring = TOKEN; + lexer::token* tok; + // Go after the decimal point - if (PeekNext () == ".") { - Next ("."); - MustNumber (false); + if (m_lx->peek_next (tok) && tok->type == tk_dot) + { + m_lx->skip(); + m_lx->must_get_next (tk_number); floatstring += "."; - floatstring += token; + floatstring += token_string(); } - + return floatstring; } @@ -954,204 +1119,264 @@ // 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; - +data_buffer* botscript_parser::parse_expr_value (type_e reqtype) +{ + data_buffer* b = new data_buffer (16); + + script_variable* g; + // Prefixing "!" means negation. - bool negate = (token == "!"); + bool negate = (m_lx->get_token() == tk_exclamation_mark); + if (negate) // Jump past the "!" - Next (); - + m_lx->skip(); + // Handle strlen - if (token == "strlen") { - MustNext ("("); - MustNext (); - - // By this token we should get a string constant. - constinfo_t* constant = FindConstant (token); + if (TOKEN == "strlen") + { + m_lx->must_get_next (tk_paren_start); + m_lx->must_get_next(); + + // By this TOKEN we should get a string constant. + constant_info* constant = find_constant (TOKEN); + if (!constant || constant->type != TYPE_STRING) - ParserError ("strlen only works with const str"); - + error ("strlen only works with const str"); + if (reqtype != TYPE_INT) - ParserError ("strlen returns int but %s is expected\n", GetTypeName (reqtype).c_str()); - - b->Write (DH_PUSHNUMBER); - b->Write (constant->val.len ()); - - MustNext (")"); - } else if (token == "(") { + error ("strlen returns int but %s is expected\n", GetTypeName (reqtype).c_str()); + + b->write (DH_PUSHNUMBER); + b->write (constant->val.len()); + + m_lx->must_get_next (tk_paren_end); + } + else if (TOKEN == "(") + { // Expression - MustNext (); - DataBuffer* c = ParseExpression (reqtype); - b->Merge (c); - MustNext (")"); - } else if (CommandDef* comm = FindCommand (token)) { + m_lx->must_get_next(); + data_buffer* c = parse_expression (reqtype); + b->merge (c); + m_lx->must_get_next (tk_paren_end); + } + else if (CommandDef* comm = FindCommand (TOKEN)) + { delete b; - + // Command if (reqtype && comm->returnvalue != reqtype) - ParserError ("%s returns an incompatible data type", comm->name.chars()); + error ("%s returns an incompatible data type", comm->name.chars()); + b = ParseCommand (comm); - } else if (constinfo_t* constant = FindConstant (token)) { + } + else if (constant_info* constant = find_constant (TOKEN)) + { // Type check if (reqtype != constant->type) - ParserError ("constant `%s` is %s, expression requires %s\n", + error ("constant `%s` is %s, expression requires %s\n", constant->name.c_str(), GetTypeName (constant->type).c_str(), GetTypeName (reqtype).c_str()); - - switch (constant->type) { + + switch (constant->type) + { + case TYPE_BOOL: + case TYPE_INT: + b->write (DH_PUSHNUMBER); + b->write (atoi (constant->val)); + break; + + case TYPE_STRING: + b->write_string (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: + error ("unknown identifier `%s` (expected keyword, function or variable)", TOKEN.chars()); + break; + 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: + { + m_lx->must_be (tk_number); + + // 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; } - } 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); + b->write_string (TOKEN); break; } } - + // Negate it now if desired if (negate) - b->Write (DH_NEGATELOGICAL); - + 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 (); - +// TOKEN to be the name of the variable, and expects the variable to be given. +data_buffer* botscript_parser::ParseAssignment (script_variable* var) +{ + bool global = !var->statename.len(); + // Get an operator - MustNext (); - int oper = ParseOperator (); - if (!IsAssignmentOperator (oper)) - ParserError ("expected assignment operator"); - + m_lx->must_get_next(); + int oper = parse_operator(); + + if (!is_assignment_operator (oper)) + error ("expected assignment operator"); + if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction - ParserError ("can't alter variables at top level"); - + error ("can't alter variables at top level"); + // Parse the right operand - MustNext (); - DataBuffer* retbuf = new DataBuffer; - DataBuffer* expr = ParseExpression (var->type); - + m_lx->must_get_next(); + data_buffer* retbuf = new data_buffer; + data_buffer* expr = parse_expression (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); + 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 = get_data_header_by_operator (var, oper); + retbuf->write (dh); + retbuf->write (var->index); + } + return retbuf; } -void ScriptReader::PushScope () { +void botscript_parser::push_scope() +{ g_ScopeCursor++; + if (g_ScopeCursor >= MAX_SCOPE) - ParserError ("too deep scope"); - - ScopeInfo* info = &SCOPE(0); + error ("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++) { + + 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"); - +data_buffer* botscript_parser::parse_statement (object_writer* w) +{ + if (find_constant (TOKEN)) // There should not be constants here. + error ("invalid use for constant\n"); + // If it's a variable, expect assignment. - if (ScriptVar* var = FindGlobalVariable (token)) + if (script_variable* var = FindGlobalVariable (TOKEN)) return ParseAssignment (var); - + return null; } -void ScriptReader::AddSwitchCase (ObjWriter* w, DataBuffer* b) { - ScopeInfo* info = &SCOPE(0); - +void botscript_parser::add_switch_case (object_writer* w, data_buffer* b) +{ + ScopeInfo* info = &SCOPE (0); + info->casecursor++; + if (info->casecursor >= MAX_CASE) - ParserError ("too many cases in one switch"); - + error ("too many cases in one switch"); + // Init a mark for the case buffer - int m = w->AddMark (""); + int m = w->add_mark (""); 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); + b->add_reference (m); else - w->AddReference (m); - + w->add_reference (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; + info->casebuffers[info->casecursor] = w->SwitchBuffer = new data_buffer; +} + +constant_info* find_constant (string TOKEN) +{ + for (int i = 0; i < g_ConstInfo.size(); i++) + if (g_ConstInfo[i].name == TOKEN) + return &g_ConstInfo[i]; + + return null; } -constinfo_t* FindConstant (string token) { - for (uint i = 0; i < g_ConstInfo.size(); i++) - if (g_ConstInfo[i].name == token) - return &g_ConstInfo[i]; - return null; +// ============================================================================ +// +bool botscript_parser::token_is (e_token a) +{ + return (m_lx->get_token() == a); +} + +// ============================================================================ +// +string botscript_parser::token_string() +{ + return m_lx->get_token()->text; +} + +// ============================================================================ +// +string botscript_parser::describe_position() const +{ + lexer::token* tok = m_lx->get_token(); + return tok->file + ":" + string (tok->line) + ":" + string (tok->column); } \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser.h Sat Jan 11 22:36:31 2014 +0200 @@ -0,0 +1,189 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 BOTC_PARSER_H +#define BOTC_PARSER_H + +#include <stdio.h> +#include "main.h" +#include "commands.h" +#include "object_writer.h" +#include "lexer_scanner.h" +#include "tokens.h" + +#define MAX_FILESTACK 8 +#define MAX_SCOPE 32 +#define MAX_CASE 64 + +class lexer; +class script_variable; + +// 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, +}; + +struct operator_info +{ + operator_e opercode; + DATAHEADERS_e dataheader; + e_token token; +}; + +// 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 +{ + int mark1; + int mark2; + scopetype_e type; + data_buffer* 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 + data_buffer* casebuffers[MAX_CASE]; + + // What is the current buffer of the block? + data_buffer* recordbuffer; +}; + +// ============================================================================ +struct constant_info +{ + string name; + type_e type; + string val; +}; + +// ============================================================================ +// The script reader reads the script, parses it and tells the object writer +// the bytecode it needs to write to file. +class botscript_parser +{ + public: + // ==================================================================== + // TODO: make private + FILE* fp[MAX_FILESTACK]; + string filepath[MAX_FILESTACK]; + int fc; + + int pos[MAX_FILESTACK]; + int curline[MAX_FILESTACK]; + int curchar[MAX_FILESTACK]; + ScopeInfo scopestack[MAX_SCOPE]; + long savedpos[MAX_FILESTACK]; // filepointer cursor position + int commentmode; + long prevpos; + string prevtoken; + + // ==================================================================== + // METHODS + botscript_parser(); + ~botscript_parser(); + void parse_botscript (string file_name, object_writer* w); + data_buffer* ParseCommand (CommandDef* comm); + data_buffer* parse_expression (type_e reqtype); + data_buffer* ParseAssignment (script_variable* var); + int parse_operator (bool peek = false); + data_buffer* parse_expr_value (type_e reqtype); + string parse_float (); + void push_scope (); + data_buffer* parse_statement (object_writer* w); + void add_switch_case (object_writer* w, data_buffer* b); + void check_toplevel(); + void check_not_toplevel(); + bool token_is (e_token a); + string token_string(); + string describe_position() const; + + private: + lexer* m_lx; +}; + +constant_info* find_constant_by_name (string token); + +#endif // BOTC_PARSER_H
--- a/src/preprocessor.cxx Fri Jan 10 21:58:42 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include "main.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 -string ScriptReader::PPReadWord (char &term) { - string word; - while (1) { - char c = PPReadChar(); - if (feof (fp[fc]) || (isspace (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 - string directive = PPReadWord (trash); - - // Now check the directive name against known names - if (directive == "include") { - // #include-directive - char terminator; - string 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/src/scriptreader.cxx Fri Jan 10 21:58:42 2014 +0200 +++ b/src/scriptreader.cxx Sat Jan 11 22:36:31 2014 +0200 @@ -1,374 +0,0 @@ -#include "main.h" -#include "scriptreader.h" - -#define STORE_POSITION \ - bool stored_atnewline = atnewline; \ - unsigned int stored_curline = curline[fc]; \ - unsigned int stored_curchar = curchar[fc]; - -#define RESTORE_POSITION \ - atnewline = stored_atnewline; \ - curline[fc] = stored_curline; \ - curchar[fc] = stored_curchar; - -// ============================================================================ -ScriptReader::ScriptReader (string 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 (string 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]); - string 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 (isspace (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. -string ScriptReader::PeekNext (int offset) { - // Store current information - string storedtoken = token; - int cpos = ftell (fp[fc]); - STORE_POSITION - - // Advance on the token. - while (offset >= 0) { - if (!Next (true)) - return ""; - offset--; - } - - string 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, ...) { - char buf[512]; - va_list va; - va_start (va, message); - sprintf (buf, message, va); - va_end (va); - ParserMessage ("\nError: ", buf); - exit (1); -} - -// ============================================================================ -void ScriptReader::ParserWarning (const char* message, ...) { - char buf[512]; - va_list va; - va_start (va, message); - sprintf (buf, message, va); - va_end (va); - ParserMessage ("Warning: ", buf); -} - -// ============================================================================ -void ScriptReader::ParserMessage (const char* header, string message) { - if (fc >= 0 && fc < MAX_FILESTACK) - fprintf (stderr, "%s%s:%u:%u: %s\n", - header, filepath[fc].c_str(), curline[fc], curchar[fc], message.c_str()); - else - fprintf (stderr, "%s%s\n", header, message.c_str()); -} - -// ============================================================================ -// if gotquote == 1, the current token already holds the quotation mark. -void ScriptReader::MustString (bool gotquote) { - if (gotquote) - MustThis ("\""); - else - MustNext ("\""); - - string 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 (); - - string num = token; - if (num == "-") { - MustNext (); - num += token; - } - - // "true" and "false" are valid numbers - if (-token == "true") - token = "1"; - else if (-token == "false") - token = "0"; - else { - bool ok; - int num = token.to_long (&ok); - - if (!ok) - ParserError ("expected a number, got `%s`", token.chars()); - - token = num; - } -} \ No newline at end of file
--- a/src/scriptreader.h Fri Jan 10 21:58:42 2014 +0200 +++ b/src/scriptreader.h Sat Jan 11 22:36:31 2014 +0200 @@ -1,171 +0,0 @@ -#ifndef BOTC_SCRIPTREADER_H -#define BOTC_SCRIPTREADER_H - -#include <stdio.h> -#include "main.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 { - string name; - type_e type; - string 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]; - string 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 - string token; - int commentmode; - long prevpos; - string prevtoken; - - // ==================================================================== - // METHODS - // scriptreader.cxx: - ScriptReader (string path); - ~ScriptReader (); - void OpenFile (string path); - void CloseFile (unsigned int u = MAX_FILESTACK); - char ReadChar (); - char PeekChar (int offset = 0); - bool Next (bool peek = false); - void Prev (); - string 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); - string 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, string message); - - bool DoDirectivePreprocessing (); - char PPReadChar (); - void PPMustChar (char c); - string PPReadWord (char &term); -}; - -constinfo_t* FindConstant (string token); -extern bool g_Neurosphere; - -#endif // BOTC_SCRIPTREADER_H \ No newline at end of file
--- a/src/str.cxx Fri Jan 10 21:58:42 2014 +0200 +++ b/src/str.cxx Sat Jan 11 22:36:31 2014 +0200 @@ -1,3 +1,33 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 <cstring> #include "main.h" #include "str.h"
--- a/src/str.h Fri Jan 10 21:58:42 2014 +0200 +++ b/src/str.h Sat Jan 11 22:36:31 2014 +0200 @@ -1,3 +1,33 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 STRING_H #define STRING_H
--- a/src/stringtable.cxx Fri Jan 10 21:58:42 2014 +0200 +++ b/src/stringtable.cxx Sat Jan 11 22:36:31 2014 +0200 @@ -1,3 +1,33 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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>
--- a/src/stringtable.h Fri Jan 10 21:58:42 2014 +0200 +++ b/src/stringtable.h Sat Jan 11 22:36:31 2014 +0200 @@ -1,3 +1,33 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 BOTC_STRINGTABLE_H #define BOTC_STRINGTABLE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tokens.h Sat Jan 11 22:36:31 2014 +0200 @@ -0,0 +1,109 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 TOKENS_H +#define TOKENS_H + +// ======================================================= +enum e_token +{ + // Non-word tokens + tk_equals, // ----- 0 + tk_brackets, // - 1 + tk_add_assign, // - 2 + tk_sub_assign, // - 3 + tk_multiply_assign, // - 4 + tk_divide_assign, // ----- 5 + tk_modulus_assign, // - 6 + tk_single_quote, // - 7 + tk_dollar_sign, // - 8 + tk_paren_start, // - 9 + tk_paren_end, // ----- 10 + tk_bracket_start, // - 11 + tk_bracket_end, // - 12 + tk_brace_start, // - 13 + tk_brace_end, // - 14 + tk_assign, // ----- 15 + tk_plus, // - 16 + tk_minus, // - 17 + tk_multiply, // - 18 + tk_divide, // - 19 + tk_modulus, // ----- 20 + tk_comma, // - 21 + tk_lesser, // - 22 + tk_greater, // - 23 + tk_dot, // - 24 + tk_colon, // ----- 25 + tk_semicolon, // - 26 + tk_hash, // - 27 + tk_exclamation_mark, // - 28 + tk_arrow, // - 29 + + // -------------- + // Named tokens + tk_bool, // ----- 30 + tk_break, // - 31 + tk_case, // - 32 + tk_continue, // - 33 + tk_const, // - 34 + tk_default, // ----- 35 + tk_do, // - 36 + tk_else, // - 37 + tk_event, // - 38 + tk_for, // - 39 + tk_goto, // ----- 40 + tk_if, // - 41 + tk_int, // - 42 + tk_mainloop, // - 43 + tk_onenter, // - 44 + tk_onexit, // ----- 45 + tk_state, // - 46 + tk_switch, // - 47 + tk_str, // - 48 + tk_void, // - 49 + tk_while, // ----- 50 + + // 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 + tk_enum, // - 51 + tk_func, // - 52 + tk_return, // - 53 + + // -------------- + // Generic tokens + tk_symbol, // - 54 + tk_number, // ----- 55 + tk_string, // - 56 + + last_named_token = (int) tk_symbol - 1, + tk_any = INT_MAX +}; + +#endif \ No newline at end of file
--- a/src/types.h Fri Jan 10 21:58:42 2014 +0200 +++ b/src/types.h Sat Jan 11 22:36:31 2014 +0200 @@ -1,8 +1,60 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 BOTC_TYPES_H #define BOTC_TYPES_H #include <cstdlib> +#include <stdexcept> +#include "str.h" static const std::nullptr_t null = nullptr; +class script_error : public std::exception +{ + public: + script_error (const string& msg) : m_msg (msg) {} + + inline const char* what() const throw() + { + return m_msg.c_str(); + } + + private: + string m_msg; +}; + +#ifndef IN_IDE_PARSER +# define error(...) throw script_error (format (__VA_ARGS__)) +#else +void error (void, ...); +#endif + #endif // BOTC_TYPES_H \ No newline at end of file
--- a/src/variables.cxx Fri Jan 10 21:58:42 2014 +0200 +++ b/src/variables.cxx Sat Jan 11 22:36:31 2014 +0200 @@ -1,42 +1,32 @@ /* - * 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. - */ + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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> @@ -44,62 +34,66 @@ #include "main.h" #include "containers.h" #include "bots.h" -#include "botcommands.h" -#include "objwriter.h" +#include "object_writer.h" #include "stringtable.h" #include "variables.h" -#include "scriptreader.h" +#include "parser.h" -list<ScriptVar> g_GlobalVariables; -list<ScriptVar> g_LocalVariables; +list<script_variable> g_GlobalVariables; +list<script_variable> 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, string name) { +script_variable* DeclareGlobalVariable (botscript_parser* r, type_e type, string name) +{ // Unfortunately the VM does not support string variables so yeah. if (type == TYPE_STRING) - r->ParserError ("variables cannot be string\n"); - + error ("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()); - + error ("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()); - + error ("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++) + error ("too many global variables!"); + + for (int 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; + error ("attempted redeclaration of global variable `%s`", name.chars()); + + script_variable 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 (string name) { - for (uint i = 0; i < g_GlobalVariables.size(); i++) { - ScriptVar* g = &g_GlobalVariables[i]; +script_variable* FindGlobalVariable (string name) +{ + for (int i = 0; i < g_GlobalVariables.size(); i++) + { + script_variable* g = &g_GlobalVariables[i]; + if (g->name == name) return g; } - + return null; } // ============================================================================ // Count all declared global variables -uint CountGlobalVars () { +int CountGlobalVars () +{ return g_GlobalVariables.size(); -} \ No newline at end of file +}
--- a/src/variables.h Fri Jan 10 21:58:42 2014 +0200 +++ b/src/variables.h Sat Jan 11 22:36:31 2014 +0200 @@ -1,27 +1,59 @@ +/* + Copyright (c) 2013-2014, Santeri 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + 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 <COPYRIGHT HOLDER> 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 BOTC_VARIABLES_H #define BOTC_VARIABLES_H -#include "str.h" -#include "scriptreader.h" +#include "main.h" -struct ScriptVar { +class botscript_parser; + +struct script_variable +{ string name; string statename; type_e type; int value; - unsigned int index; + int index; }; -extern list<ScriptVar> g_GlobalVariables; -extern list<ScriptVar> g_LocalVariables; +extern list<script_variable> g_GlobalVariables; +extern list<script_variable> 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, string name); -deprecated unsigned int CountGlobalVars (); -ScriptVar* FindGlobalVariable (string name); +script_variable* DeclareGlobalVariable (botscript_parser* r, type_e type, string name); +deprecated int CountGlobalVars (); +script_variable* FindGlobalVariable (string name); -#endif // BOTC_VARIABLES_H \ No newline at end of file +#endif // BOTC_VARIABLES_H