//------------------------------------------------------------------------- /* Copyright (C) 2010 EDuke32 developers and contributors This file is part of EDuke32. EDuke32 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //------------------------------------------------------------------------- #include "m32script.h" #include "m32def.h" #include "cache1d.h" #include "sounds_mapster32.h" //#include "osd.h" #include "keys.h" char g_szScriptFileName[BMAX_PATH] = "(none)"; // file we're currently compiling static char g_szCurrentBlockName[256] = "(none)", g_szLastBlockName[256] = "NULL"; ////// compiler state vvv static const char *textptr; int32_t g_totalLines, g_lineNumber; int32_t g_numCompilerErrors, g_numCompilerWarnings; int32_t g_didDefineSomething; typedef struct { int32_t currentStateIdx; ofstype currentStateOfs; // the offset to the start of the currently parsed states' code int32_t currentEvent; ofstype parsingEventOfs; int32_t checkingSwitch; int32_t numCases; instype *caseScriptPtr; // the pointer to the start of the case table in a switch statement // first entry is 'default' code. instype *caseCodePtr; // the pointer to the start of the different cases' code int32_t labelsOnly; int32_t numBraces; int32_t checkingIfElse, ifElseAborted; } compilerstate_t; static compilerstate_t cs; static compilerstate_t cs_default = {-1, -1, -1, -1, 0, 0, NULL, NULL, 0, 0, 0, 0}; ////// ------------------- instype *script = NULL; instype *g_scriptPtr; int32_t g_scriptSize = 65536; int32_t *constants, constants_allocsize=1024; int32_t g_numSavedConstants=0; static int32_t g_wasConstant=0; char *label; int32_t *labelval; uint8_t *labeltype; int32_t g_numLabels=0, g_numDefaultLabels=0; static int32_t label_allocsize = 512; int32_t g_stateCount = 0; statesinfo_t *statesinfo = NULL; static int32_t statesinfo_allocsize = 512; static int32_t interactive_compilation = 0; static char tempbuf[2048]; static char tlabel[MAXLABELLEN]; int32_t g_iReturnVar=0; int32_t m32_sortvar1, m32_sortvar2; char *ScriptQuotes[MAXQUOTES+1], *ScriptQuoteRedefinitions[MAXQUOTES+1]; int32_t g_numQuoteRedefinitions = 0; ofstype aEventOffsets[MAXEVENTS]; int32_t aEventSizes[MAXEVENTS]; gamevar_t aGameVars[MAXGAMEVARS]; gamearray_t aGameArrays[MAXGAMEARRAYS]; int32_t g_gameVarCount=0, g_systemVarCount=0; int32_t g_gameArrayCount=0, g_systemArrayCount=0; // "magic" number for { and }, overrides line number in compiled code for later detection #define IFELSE_MAGIC 31337 void C_ReportError(int32_t iError); enum ScriptLabel_t { LABEL_ANY = -1, LABEL_DEFINE = 1, LABEL_EVENT = 2 }; static const char *LabelTypeText[] = { "define", "event" }; static const char *C_GetLabelType(int32_t type) { uint32_t i; char x[64]; x[0] = 0; for (i=0; i" }; const memberlabel_t SectorLabels[]= { { "wallptr", SECTOR_WALLPTR, 1, 0, 0 }, { "wallnum", SECTOR_WALLNUM, 1, 0, 0 }, { "ceilingz", SECTOR_CEILINGZ, 0, 0, 0 }, { "floorz", SECTOR_FLOORZ, 0, 0, 0 }, { "ceilingstat", SECTOR_CEILINGSTAT, 0, 0, 0 }, { "floorstat", SECTOR_FLOORSTAT, 0, 0, 0 }, { "ceilingpicnum", SECTOR_CEILINGPICNUM, 0, 0, MAXTILES-1 }, { "ceilingslope", SECTOR_CEILINGSLOPE, 0, 0, 0}, { "ceilingshade", SECTOR_CEILINGSHADE, 0, 0, 0 }, { "ceilingpal", SECTOR_CEILINGPAL, 0, 0, 0 }, { "ceilingxpanning", SECTOR_CEILINGXPANNING, 0, 0, 0 }, { "ceilingypanning", SECTOR_CEILINGYPANNING, 0, 0, 0 }, { "floorpicnum", SECTOR_FLOORPICNUM, 0, 0, MAXTILES-1 }, { "floorslope", SECTOR_FLOORSLOPE, 0, 0, 0 }, { "floorshade", SECTOR_FLOORSHADE, 0, 0, 0 }, { "floorpal", SECTOR_FLOORPAL, 0, 0, 0 }, { "floorxpanning", SECTOR_FLOORXPANNING, 0, 0, 0 }, { "floorypanning", SECTOR_FLOORYPANNING, 0, 0, 0 }, { "visibility", SECTOR_VISIBILITY, 0, 0, 0 }, { "alignto", SECTOR_ALIGNTO, 0, 0, 0 }, // aka filler, not used { "lotag", SECTOR_LOTAG, 0, 0, 0 }, { "hitag", SECTOR_HITAG, 0, 0, 0 }, { "extra", SECTOR_EXTRA, 0, 0, 0 }, { "", -1, 0, 0, 0 } // END OF LIST }; const memberlabel_t WallLabels[]= { { "x", WALL_X, 0, -524288, 524288 }, { "y", WALL_Y, 0, -524288, 524288 }, { "point2", WALL_POINT2, 1, 0, 0 }, { "nextwall", WALL_NEXTWALL, 1, 0, 0 }, { "nextsector", WALL_NEXTSECTOR, 1, 0, 0 }, { "cstat", WALL_CSTAT, 0, 0, 0 }, { "picnum", WALL_PICNUM, 0, 0, MAXTILES-1 }, { "overpicnum", WALL_OVERPICNUM, 0, 0, MAXTILES-1 }, { "shade", WALL_SHADE, 0, 0, 0 }, { "pal", WALL_PAL, 0, 0, 0 }, { "xrepeat", WALL_XREPEAT, 0, 0, 0 }, { "yrepeat", WALL_YREPEAT, 0, 0, 0 }, { "xpanning", WALL_XPANNING, 0, 0, 0 }, { "ypanning", WALL_YPANNING, 0, 0, 0 }, { "lotag", WALL_LOTAG, 0, 0, 0 }, { "hitag", WALL_HITAG, 0, 0, 0 }, { "extra", WALL_EXTRA, 0, 0, 0 }, { "", -1, 0, 0, 0 } // END OF LIST }; const memberlabel_t SpriteLabels[]= { { "x", SPRITE_X, 0, -524288, 524288 }, { "y", SPRITE_Y, 0, -524288, 524288 }, { "z", SPRITE_Z, 0, 0, 0 }, { "cstat", SPRITE_CSTAT, 0, 0, 0 }, { "picnum", SPRITE_PICNUM, 0, 0, MAXTILES-1 }, { "shade", SPRITE_SHADE, 0, 0, 0 }, { "pal", SPRITE_PAL, 0, 0, 0 }, { "clipdist", SPRITE_CLIPDIST, 0, 0, 0 }, { "detail", SPRITE_DETAIL, 1, 0, 0 }, // aka filler, not used { "xrepeat", SPRITE_XREPEAT, 0, 0, 0 }, { "yrepeat", SPRITE_YREPEAT, 0, 0, 0 }, { "xoffset", SPRITE_XOFFSET, 0, 0, 0 }, { "yoffset", SPRITE_YOFFSET, 0, 0, 0 }, { "sectnum", SPRITE_SECTNUM, 1, 0, 0 }, { "statnum", SPRITE_STATNUM, 1, 0, 0 }, { "ang", SPRITE_ANG, 0, 0, 0 }, { "owner", SPRITE_OWNER, 0, 0, 0 }, { "xvel", SPRITE_XVEL, 0, 0, 0 }, { "yvel", SPRITE_YVEL, 0, 0, 0 }, { "zvel", SPRITE_ZVEL, 0, 0, 0 }, { "lotag", SPRITE_LOTAG, 0, 0, 0 }, { "hitag", SPRITE_HITAG, 0, 0, 0 }, { "extra", SPRITE_EXTRA, 0, 0, 0 }, { "", -1, 0, 0, 0 } // END OF LIST }; const tokenmap_t iter_tokens[] = { { "allsprites", ITER_ALLSPRITES }, { "allsectors", ITER_ALLSECTORS }, { "allwalls", ITER_ALLWALLS }, { "selsprites", ITER_SELSPRITES }, { "selsectors", ITER_SELSECTORS }, { "selwalls", ITER_SELWALLS }, { "drawnsprites", ITER_DRAWNSPRITES }, { "spritesofsector", ITER_SPRITESOFSECTOR }, { "loopofwall", ITER_LOOPOFWALL }, { "wallsofsector", ITER_WALLSOFSECTOR }, { "range", ITER_RANGE }, // vvv alternatives go here vvv { "selspr", ITER_SELSPRITES }, { "selsec", ITER_SELSECTORS }, { "sprofsec", ITER_SPRITESOFSECTOR }, { "walofsec", ITER_WALLSOFSECTOR }, { "", -1 } // END OF LIST }; hashtable_t h_gamevars = { MAXGAMEVARS>>1, NULL }; hashtable_t h_arrays = { MAXGAMEARRAYS>>1, NULL }; hashtable_t h_labels = { 11262>>1, NULL }; static hashtable_t h_states = { 1264>>1, NULL }; static hashtable_t h_keywords = { CON_END>>1, NULL }; static hashtable_t h_iter = { ITER_END, NULL }; static hashtable_t h_sector = { SECTOR_END>>1, NULL }; static hashtable_t h_wall = { WALL_END>>1, NULL }; static hashtable_t h_sprite = { SPRITE_END>>1, NULL }; static void C_InitHashes() { int32_t i; hash_init(&h_gamevars); hash_init(&h_arrays); hash_init(&h_labels); hash_init(&h_states); hash_init(&h_keywords); for (i=NUMKEYWORDS-1; i>=0; i--) hash_add(&h_keywords, keyw[i], i, 0); for (i=0; i=0; i++) hash_add(&h_sector,SectorLabels[i].name,i, 0); hash_add(&h_sector,"filler", SECTOR_ALIGNTO, 0); hash_init(&h_wall); for (i=0; WallLabels[i].lId >=0; i++) hash_add(&h_wall,WallLabels[i].name,i, 0); hash_init(&h_sprite); for (i=0; SpriteLabels[i].lId >=0; i++) hash_add(&h_sprite,SpriteLabels[i].name,i, 0); hash_add(&h_sprite,"filler", SPRITE_DETAIL, 0); hash_init(&h_iter); for (i=0; iter_tokens[i].val >=0; i++) hash_add(&h_iter, iter_tokens[i].token, iter_tokens[i].val, 0); } static int32_t C_SetScriptSize(int32_t size) { ofstype oscriptOfs = (unsigned)(g_scriptPtr-script); ofstype ocaseScriptOfs = (unsigned)(cs.caseScriptPtr-script); ofstype ocaseCodeOfs = (unsigned)(cs.caseCodePtr-script); instype *newscript; int32_t osize = g_scriptSize; if (g_scriptSize >= size) return 0; //initprintf("offset: %d\n",(unsigned)(g_scriptPtr-script)); g_scriptSize = size; initprintf("Resizing code buffer to %d*%d bytes\n", g_scriptSize, sizeof(instype)); newscript = (instype *)Brealloc(script, g_scriptSize * sizeof(instype)); if (newscript == NULL) { C_ReportError(-1); initprintf("%s:%d: out of memory: Aborted (%ud)\n", g_szScriptFileName, g_lineNumber, (unsigned)(g_scriptPtr-script)); g_numCompilerErrors++; // initprintf(tempbuf); return 1; } if (size >= osize) Bmemset(&newscript[osize], 0, (size-osize) * sizeof(instype)); if (script != newscript) { initprintf("Relocating compiled code from to 0x%x to 0x%x\n", script, newscript); script = newscript; } g_scriptPtr = (instype *)(script+oscriptOfs); // initprintf("script: %d, \n",script); initprintf("offset: %d\n",(unsigned)(g_scriptPtr-script)); if (cs.caseScriptPtr != NULL) cs.caseScriptPtr = (instype *)(script+ocaseScriptOfs); if (cs.caseCodePtr != NULL) cs.caseCodePtr = (instype *)(script+ocaseCodeOfs); return 0; } static inline int32_t ispecial(char c) { return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='(' || c==')' || c==',' || c==';'; } static inline int32_t isaltok(char c) { return isalnum(c) || c == '#' || c == '{' || c == '}' || c == '/' || c == '\\' || c == '*' || c == '-' || c == '_' || c == '.'; } static int32_t C_SkipComments(void) { char c = *textptr; do { if (ispecial(c)) { textptr++; g_lineNumber += (c=='\n'); } else if (c == '/' && textptr[1] == '/') { while (*textptr && *textptr != 0x0a && *textptr != 0x0d) textptr++; } else if (c == '/' && textptr[1] == '*') { textptr += 2; while (*textptr && !(textptr[0] == '*' && textptr[1] == '/')) { if (*textptr == '\n') g_lineNumber++; textptr++; } if (!*textptr) { C_CUSTOMERROR("found `/*' with no `*/'."); cs.numBraces = 0; cs.currentStateIdx = -1; break; } textptr += 2; } else break; } while ((c = *textptr)); if ((unsigned)(g_scriptPtr-script) > (unsigned)(g_scriptSize-32)) return C_SetScriptSize(g_scriptSize<<1); return 0; } static inline int32_t C_GetLabelNameID(const memberlabel_t *pLabel, hashtable_t *tH, const char *psz) { // find the label psz in the table pLabel. // returns the ID for the label, or -1 int32_t l = hash_findcase(tH, psz); if (l>=0) l = pLabel[l].lId; return l; } static void C_GetNextLabelName(void) { int32_t i; C_SkipComments(); if (*textptr == 0) { C_CUSTOMERROR("unexpected EOF where label was expected."); return; } while (!isalnum(*textptr)) { g_lineNumber += (*textptr == 0x0a); textptr++; if (!*textptr) return; } i = 0; while (!ispecial(*textptr) && *textptr!='['&& *textptr!=']') { if (i < MAXLABELLEN-1) tlabel[i++] = *(textptr++); else textptr++; } tlabel[i] = 0; // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug > 1) // initprintf("%s:%d: debug: got label `%s'.\n",g_szScriptFileName,g_lineNumber,tlabel); } static int32_t C_CopyLabel(void) { if (g_numLabels >= label_allocsize) { label = Brealloc(label, 2*label_allocsize*MAXLABELLEN*sizeof(char)); labelval = Brealloc(labelval, 2*label_allocsize*sizeof(labelval[0])); labeltype = Brealloc(labeltype, 2*label_allocsize*sizeof(labeltype[0])); if (label==NULL || labelval==NULL || labeltype==NULL) { label_allocsize = 0; initprintf("Out of memory!"); return 1; } label_allocsize *= 2; } Bmemcpy(label+(g_numLabels*MAXLABELLEN), tlabel, MAXLABELLEN); return 0; } static int32_t C_GetKeyword(void) { int32_t i; const char *temptextptr; C_SkipComments(); if (*textptr == 0) return -1; temptextptr = textptr; while (!isaltok(*temptextptr)) { temptextptr++; if (!*temptextptr) return 0; } i = 0; while (isaltok(*temptextptr)) tempbuf[i++] = *(temptextptr++); tempbuf[i] = 0; return hash_find(&h_keywords, tempbuf); } //Returns its code # if keyword, -1 on eof or non-keyword static int32_t C_GetNextKeyword(void) { int32_t i, l, olinenum = g_lineNumber, havequickstate=0; const char *otextptr = textptr; C_SkipComments(); if (*textptr == 0) return -1; while (!isaltok(*textptr)) { g_lineNumber += (*textptr == 0x0a); if (!*textptr) return -1; textptr++; } l = 0; while (isaltok(*(textptr+l)) && !(*(textptr + l) == '.')) { tempbuf[l] = textptr[l]; l++; } while (isaltok(*(textptr+l))) { tempbuf[l] = textptr[l]; l++; } tempbuf[l] = 0; textptr += l; i = hash_find(&h_keywords, tempbuf); if (i<0) { if (tempbuf[0]=='{' && tempbuf[1]) { C_CUSTOMERROR("expected whitespace between `{' and `%s'", tempbuf+1); return -1; } else if (tempbuf[0]=='}' && tempbuf[1]) { C_CUSTOMERROR("expected whitespace between `}' and `%s'", tempbuf+1); return -1; } else { // if compiling from OSD, try state name if (interactive_compilation) i = hash_find(&h_states, tempbuf); if (i<0) { C_ReportError(ERROR_EXPECTEDKEYWORD); g_numCompilerErrors++; return -1; } else { havequickstate = 1; i = CON_STATE; } } } if (i == CON_LEFTBRACE || i == CON_RIGHTBRACE || i == CON_NULLOP) *g_scriptPtr = i + (IFELSE_MAGIC<<12); else *g_scriptPtr = i + (g_lineNumber<<12); if (havequickstate) { g_lineNumber = olinenum; // reset textptr so that case CON_STATE in C_ParseCommand() can insert the state number textptr = otextptr; } g_scriptPtr++; // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug) // initprintf("%s:%d: debug: translating keyword `%s'.\n",g_szScriptFileName,g_lineNumber,keyw[i]); return i; } #define GetGamevarID(szGameLabel) hash_find(&h_gamevars, szGameLabel) #define GetGamearrayID(szGameLabel) hash_find(&h_arrays, szGameLabel) static void C_GetNextVarType(int32_t type) { int32_t i, id=0, flags=0, num, indirect=0; C_SkipComments(); if (*textptr == 0) { C_CUSTOMERROR("unexpected EOF where variable was expected."); return; } g_wasConstant = 0; // constant where gamevar expected if ((type==0 || type==GAMEVAR_SPECIAL) && !cs.labelsOnly && (isdigit(textptr[0]) || (textptr[0]=='-' && isdigit(textptr[1])))) { // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug) // initprintf("%s:%d: debug: accepted constant %d in place of gamevar.\n",g_szScriptFileName,g_lineNumber,atol(textptr)); if (tolower(textptr[1])=='x') sscanf(textptr+2, "%" SCNx32, &num); else num = atoi(textptr); if (type==GAMEVAR_SPECIAL && (num<0 || num>=65536)) C_CUSTOMERROR("array index %d out of bounds. (max: 65535)", num); if (g_numCompilerErrors==0 && type!=GAMEVAR_SPECIAL && num != (int16_t)num) { indirect = 1; for (i=g_numSavedConstants-1; i>=0; i--) if (constants[i] == num) break; if (i<0) { i = g_numSavedConstants; if (i>=constants_allocsize) { constants_allocsize *= 2; constants = Brealloc(constants, constants_allocsize * sizeof(constants[0])); if (!constants) { initprintf("C_GetNextVarType(): ERROR: out of memory!\n"); g_numCompilerErrors++; return; } } constants[i] = num; num = i; g_numSavedConstants++; } } if (type!=GAMEVAR_SPECIAL) g_wasConstant = 1; *g_scriptPtr++ = MAXGAMEVARS | (num<<16) | indirect; while (!ispecial(*textptr) && *textptr != ']') textptr++; return; } else if (textptr[0]=='-' && textptr[1] != '.') // && !isdigit(*(textptr+1)) { if (type==0) { // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug) // initprintf("%s:%d: debug: flagging gamevar as negative.\n",g_szScriptFileName,g_lineNumber,atol(textptr)); flags = M32_FLAG_NEGATE; } else { C_CUSTOMERROR("error: syntax error. Tried negating written-to variable."); C_GetNextLabelName(); return; } } else if (type != GAMEVAR_SPECIAL && (textptr[0]=='.' || (textptr[0]=='-' && textptr[1]=='.'))) { int32_t lLabelID = -1, aridx = M32_THISACTOR_VAR_ID; flags |= M32_FLAG_SPECIAL; if (*textptr=='-') { flags |= M32_FLAG_NEGATE; textptr++; } textptr++; /// now pointing at 'xxx' C_GetNextLabelName(); lLabelID = C_GetLabelNameID(SpriteLabels, &h_sprite, Bstrtolower(tlabel)); if (lLabelID == -1) { g_numCompilerErrors++; C_ReportError(ERROR_SYMBOLNOTRECOGNIZED); return; } id = M32_SPRITE_VAR_ID; *g_scriptPtr++ = ((aridx<<16) | id | (lLabelID<<2) | flags); return; } C_GetNextLabelName(); if (hash_find(&h_keywords, tlabel)>=0) { g_numCompilerErrors++; C_ReportError(ERROR_ISAKEYWORD); return; } C_SkipComments(); //skip comments and whitespace if (*textptr == '[') //read of array as a gamevar { int32_t lLabelID = -1, aridx; textptr++; flags |= M32_FLAG_ARRAY; if (type & GAMEVAR_SPECIAL) { g_numCompilerErrors++; C_ReportError(ERROR_EXPECTEDSIMPLEVAR); return; } id = GetGamearrayID(tlabel); if (id < 0) { id = GetGamevarID(tlabel); if (id >= 4) id = -1; if (id < 0) { g_numCompilerErrors++; C_ReportError(ERROR_NOTAGAMEARRAY); return; } flags &= ~M32_FLAG_ARRAY; // not an array flags |= M32_FLAG_SPECIAL; } else { if ((aGameArrays[id].dwFlags & GAMEARRAY_READONLY) && (type&GAMEVAR_READONLY)) { C_ReportError(ERROR_ARRAYREADONLY); g_numCompilerErrors++; } } C_GetNextVarType(GAMEVAR_SPECIAL); // _SPECIAL signifies that we want only simple vars or a constant g_scriptPtr--; aridx = *g_scriptPtr; C_SkipComments(); if (*textptr != ']') { g_numCompilerErrors++; C_ReportError(ERROR_GAMEARRAYBNC); return; } textptr++; // writing arrays in this way is not supported because it would // require too many changes to other code // if (type) // { // g_numCompilerErrors++; // C_ReportError(ERROR_INVALIDARRAYWRITE); // return; // } if (flags & M32_FLAG_SPECIAL) { while (*textptr && *textptr != 0x0a && *textptr != '.') textptr++; if (*textptr != '.') { static const char *types[4] = {"sprite","sector","wall","tsprite"}; C_CUSTOMERROR("error: syntax error. Expected `.