warzone2100/src/scriptvals_parser.ypp

893 lines
20 KiB
Plaintext

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2013 Warzone 2100 Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 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 Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
%{
/*
* ScriptVals.y
*
* yacc grammar for loading script variable values
*
*/
#include "lib/framework/frame.h"
#include "lib/framework/frameresource.h"
#include "lib/framework/strres.h"
#include "lib/gamelib/gtime.h"
#include "lib/script/script.h"
#include "lib/sound/audio.h"
#include "src/scriptvals.h"
#include "lib/framework/lexer_input.h"
#if defined (WZ_CC_MSVC)
#include "scriptvals_parser.hpp"
#else
#include "scriptvals_parser.h"
#endif
#include "src/scripttabs.h"
#include "src/objects.h"
#include "src/droid.h"
#include "src/structure.h"
#include "src/message.h"
#include "src/levels.h"
#include "src/research.h"
#include "src/text.h"
#include "src/template.h"
// The current script code
static SCRIPT_CODE *psCurrScript;
extern int scrv_lex(void);
extern void scrv_set_extra(YY_EXTRA_TYPE user_defined);
extern int scrv_lex_destroy(void);
extern int scrv_get_lineno(void);
extern char* scrv_get_text(void);
// The current script context
static SCRIPT_CONTEXT *psCurrContext;
// the current array indexes
static ARRAY_INDEXES sCurrArrayIndexes;
// check that an array index is valid
static bool scrvCheckArrayIndex(SDWORD base, ARRAY_INDEXES *psIndexes, UDWORD *pIndex)
{
SDWORD i, size;
if (!psCurrScript || psCurrScript->psDebug == NULL)
{
return false;
}
if (base < 0 || base >= psCurrScript->numArrays)
{
yyerror("Array index out of range");
return false;
}
if (psIndexes->dimensions != psCurrScript->psArrayInfo[base].dimensions)
{
yyerror("Invalid number of dimensions for array initialiser");
return false;
}
for(i=0; i<psCurrScript->psArrayInfo[base].dimensions; i++)
{
if ((psIndexes->elements[i] < 0) ||
(psIndexes->elements[i] >= psCurrScript->psArrayInfo[base].elements[i]))
{
yyerror("Invalid index for dimension %d", i);
return false;
}
}
*pIndex = 0;
size = 1;
for(i = psCurrScript->psArrayInfo[base].dimensions-1; i >= 0; i--)
{
*pIndex += psIndexes->elements[i] * size;
size *= psCurrScript->psArrayInfo[base].elements[i];
}
*pIndex += psCurrScript->psArrayInfo[base].base;
return true;
}
%}
%name-prefix="scrv_"
%defines
%error-verbose
%union {
bool bval;
INTERP_TYPE tval;
char *sval;
UDWORD vindex;
SDWORD ival;
VAR_INIT sInit;
ARRAY_INDEXES *arrayIndex;
}
/* value tokens */
%token <bval> BOOLEAN_T
%token <ival> INTEGER
%token <sval> IDENT
%token <sval> QTEXT /* Text with double quotes surrounding it */
%token <tval> TYPE
%token <vindex> VAR
%token <vindex> ARRAY
/* keywords */
%token SCRIPT
%token STORE
%token RUN
/* rule types */
%type <sval> script_name
%type <sInit> var_value
%type <vindex> var_entry
%type <arrayIndex> array_index
%type <arrayIndex> array_index_list
%%
val_file: script_entry
| val_file script_entry
;
script_entry: script_name RUN
{
if (!eventNewContext(psCurrScript, CR_RELEASE, &psCurrContext))
{
yyerror("Couldn't create context");
YYABORT;
}
if (!scrvAddContext($1, psCurrContext, SCRV_EXEC))
{
yyerror("Couldn't store context");
YYABORT;
}
}
'{' var_init_list '}'
{
if (!eventRunContext(psCurrContext, gameTime/SCR_TICKRATE))
{
YYABORT;
}
}
| script_name STORE QTEXT
{
if (!eventNewContext(psCurrScript, CR_NORELEASE, &psCurrContext))
{
yyerror("Couldn't create context");
YYABORT;
}
if (!scrvAddContext($3, psCurrContext, SCRV_NOEXEC))
{
yyerror("Couldn't store context");
YYABORT;
}
}
'{' var_init_list '}'
;
script_name: SCRIPT QTEXT
{
int namelen,extpos;
char *stringname;
stringname=$2;
namelen=strlen( stringname);
extpos=namelen-3;
if (strncmp(&stringname[extpos],"blo",3)==0)
{
if (resPresent("BLO",stringname)==true)
{
psCurrScript = (SCRIPT_CODE*)resGetData("BLO",stringname);
}
else
{
// change extension to "slo"
stringname[extpos]='s';
psCurrScript = (SCRIPT_CODE*)resGetData("SCRIPT",stringname);
}
}
else if (strncmp(&stringname[extpos],"slo",3)==0)
{
if (resPresent("SCRIPT",stringname)==true)
{
psCurrScript = (SCRIPT_CODE*)resGetData("SCRIPT",stringname);
}
}
if (!psCurrScript)
{
yyerror("Script file %s not found", stringname);
YYABORT;
}
$$ = $2;
}
;
var_init_list: /* NULL token */
| var_init
| var_init_list var_init
;
var_init: var_entry TYPE var_value
{
INTERP_VAL data; /* structure to to hold all types */
BASE_OBJECT *psObj;
SDWORD compIndex;
/* set type */
data.type = $2;
switch ((unsigned)$2) // Unsigned cast to suppress compiler warnings due to enum abuse.
{
case VAL_INT:
data.v.ival = $3.index; //index = integer value of the variable, not var index
if ($3.type != IT_INDEX ||
!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
break;
case ST_DROID:
if ($3.type != IT_INDEX)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
psObj = getBaseObjFromId((UDWORD)$3.index);
if (!psObj)
{
yyerror("Droid id %d not found", (UDWORD)$3.index);
YYABORT;
}
data.v.oval = psObj; /* store as pointer */
if (psObj->type != OBJ_DROID)
{
yyerror("Object id %d is not a droid", (UDWORD)$3.index);
YYABORT;
}
else
{
if(!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
}
break;
case ST_STRUCTURE:
if ($3.type != IT_INDEX)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
psObj = getBaseObjFromId((UDWORD)$3.index);
if (!psObj)
{
yyerror("Structure id %d not found", (UDWORD)$3.index);
YYABORT;
}
data.v.oval = psObj;
if (psObj->type != OBJ_STRUCTURE)
{
yyerror("Object id %d is not a structure", (UDWORD)$3.index);
YYABORT;
}
else
{
if(!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
}
break;
case ST_FEATURE:
if ($3.type != IT_INDEX)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
psObj = getBaseObjFromId((UDWORD)$3.index);
if (!psObj)
{
yyerror("Feature id %d not found", (UDWORD)$3.index);
YYABORT;
}
data.v.oval = psObj;
if (psObj->type != OBJ_FEATURE)
{
yyerror("Object id %d is not a feature", (UDWORD)$3.index);
YYABORT;
}
else
{
if(!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
}
break;
case ST_FEATURESTAT:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.ival = getFeatureStatFromName($3.pString);
if (data.v.ival == -1)
{
yyerror("Feature Stat %s not found", $3.pString);
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case VAL_BOOL:
data.v.bval = $3.index; //index = boolean value, not var index
if ($3.type != IT_BOOL ||
!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
break;
case ST_BODY:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.ival = getCompFromName(COMP_BODY, $3.pString);
if (data.v.ival == -1)
{
yyerror("body component %s not found", $3.pString);
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_PROPULSION:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.ival = getCompFromName(COMP_PROPULSION, $3.pString);
if (data.v.ival == -1)
{
yyerror("Propulsion component %s not found", $3.pString);
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_ECM:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.ival = getCompFromName(COMP_ECM, $3.pString);
if (data.v.ival == -1)
{
yyerror("ECM component %s not found", $3.pString);
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_SENSOR:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.ival = getCompFromName(COMP_SENSOR, $3.pString);
if (data.v.ival == -1)
{
yyerror("Sensor component %s not found", $3.pString);
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_CONSTRUCT:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.ival = getCompFromName(COMP_CONSTRUCT, $3.pString);
if (data.v.ival == -1)
{
yyerror("Construct component %s not found", $3.pString);
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_REPAIR:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.ival = getCompFromName(COMP_REPAIRUNIT, $3.pString);
if (data.v.ival == -1)
{
yyerror("Repair component %s not found", $3.pString);
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_BRAIN:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.ival = getCompFromName(COMP_BRAIN, $3.pString);
if (data.v.ival == -1)
{
yyerror("Brain component %s not found", $3.pString);
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_WEAPON:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.ival = getCompFromName(COMP_WEAPON, $3.pString);
if (data.v.ival == -1)
{
yyerror("Weapon component %s not found", $3.pString);
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_TEMPLATE:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.oval = getTemplateFromTranslatedNameNoPlayer($3.pString); /* store pointer to the template */
if (data.v.oval == NULL)
{
yyerror("Template %s not found", $3.pString);
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_STRUCTURESTAT:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.ival = getStructStatFromName($3.pString);
if (data.v.ival == -1)
{
yyerror("Structure Stat %s not found", $3.pString);
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_STRUCTUREID:
if ($3.type != IT_INDEX)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
psObj = getBaseObjFromId((UDWORD)$3.index);
if (!psObj)
{
yyerror("Structure id %d not found", (UDWORD)$3.index);
YYABORT;
}
data.v.ival = $3.index; /* store structure id */
if (psObj->type != OBJ_STRUCTURE)
{
yyerror("Object id %d is not a structure", (UDWORD)$3.index);
YYABORT;
}
else
{
if(!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
}
break;
case ST_DROIDID:
if ($3.type != IT_INDEX)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
psObj = getBaseObjFromId((UDWORD)$3.index);
if (!psObj)
{
yyerror("Droid id %d not found", (UDWORD)$3.index);
YYABORT;
}
data.v.ival = $3.index; /* store id*/
if (psObj->type != OBJ_DROID)
{
yyerror("Object id %d is not a droid", (UDWORD)$3.index);
YYABORT;
}
else
{
if(!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
}
break;
case ST_INTMESSAGE:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.oval = getViewData($3.pString); /* store pointer to the intelligence message */
if (data.v.oval == NULL)
{
yyerror("Message %s not found", $3.pString);
YYABORT;
}
if(!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_TEXTSTRING:
{
const char* pString;
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
pString = strresGetString(psStringRes, $3.pString);
if (!pString)
{
yyerror("Cannot find the string for id \"%s\"", $3.pString);
YYABORT;
}
data.v.sval = strdup(pString);
if (!data.v.sval)
{
debug(LOG_ERROR, "Out of memory");
abort();
YYABORT;
}
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
}
case ST_LEVEL:
{
LEVEL_DATASET *psLevel;
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
// just check the level exists
psLevel = levFindDataSet($3.pString);
if (psLevel == NULL)
{
yyerror("Level %s not found", $3.pString);
YYABORT;
}
data.v.sval = psLevel->pName; /* store string pointer */
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
}
break;
case ST_SOUND:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
/* find audio id */
compIndex = audio_GetTrackID( $3.pString );
if (compIndex == SAMPLE_NOT_FOUND)
{
/* set track vals */
compIndex = audio_SetTrackVals($3.pString, false, 100, 1800);
}
/* save track ID */
data.v.ival = compIndex;
if (!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
case ST_RESEARCH:
if ($3.type != IT_STRING)
{
yyerror("Typemismatch for variable %d", $1);
YYABORT;
}
data.v.oval = getResearch($3.pString); /* store pointer */
if (data.v.oval == NULL)
{
yyerror("Research %s not found", $3.pString);
// YYABORT;
}
if(!eventSetContextVar(psCurrContext, $1, &data))
{
yyerror("Set Value Failed for %u", $1);
YYABORT;
}
break;
default:
yyerror("Unknown type: %s", asTypeTable[$2].pIdent);
YYABORT;
break;
}
}
;
array_index: '[' INTEGER ']'
{
sCurrArrayIndexes.dimensions = 1;
sCurrArrayIndexes.elements[0] = $2;
$$ = &sCurrArrayIndexes;
}
;
array_index_list: array_index
{
$$ = $1;
}
| array_index_list '[' INTEGER ']'
{
if ($1->dimensions >= VAR_MAX_DIMENSIONS)
{
yyerror("Too many dimensions for array");
YYABORT;
}
$1->elements[$1->dimensions] = $3;
$1->dimensions += 1;
}
;
var_entry: VAR
{
$$ = $1;
}
| ARRAY array_index_list
{
UDWORD index;
if (!scrvCheckArrayIndex($1, $2, &index))
{
YYABORT;
}
$$ = index;
}
;
var_value: BOOLEAN_T
{
$$.type = IT_BOOL;
$$.index = $1;
}
| INTEGER
{
$$.type = IT_INDEX;
$$.index = $1;
}
| QTEXT
{
$$.type = IT_STRING;
$$.pString = $1;
}
;
%%
// Lookup a type
bool scrvLookUpType(const char *pIdent, INTERP_TYPE *pType)
{
TYPE_SYMBOL *psCurr;
for(psCurr = asTypeTable; psCurr->typeID != 0; psCurr++)
{
if (strcmp(psCurr->pIdent, pIdent) == 0)
{
*pType = psCurr->typeID;
return true;
}
}
return false;
}
// Lookup a variable identifier
bool scrvLookUpVar(const char *pIdent, UDWORD *pIndex)
{
UDWORD i;
if (!psCurrScript || psCurrScript->psDebug == NULL)
{
return false;
}
for(i=0; i<psCurrScript->numGlobals; i++)
{
if (psCurrScript->psVarDebug[i].pIdent != NULL &&
strcmp(psCurrScript->psVarDebug[i].pIdent, pIdent) == 0)
{
*pIndex = i;
return true;
}
}
return false;
}
// Lookup an array identifier
bool scrvLookUpArray(const char *pIdent, UDWORD *pIndex)
{
UDWORD i;
if (!psCurrScript || psCurrScript->psDebug == NULL)
{
return false;
}
for(i=0; i<psCurrScript->numArrays; i++)
{
if (psCurrScript->psArrayDebug[i].pIdent != NULL &&
strcmp(psCurrScript->psArrayDebug[i].pIdent, pIdent) == 0)
{
*pIndex = i;
return true;
}
}
return false;
}
// Load a script value file
bool scrvLoad(PHYSFS_file* fileHandle)
{
bool retval;
lexerinput_t input;
input.type = LEXINPUT_PHYSFS;
input.input.physfsfile = fileHandle;
scrv_set_extra(&input);
retval = (scrv_parse() == 0);
scrv_lex_destroy();
return retval;
}
/* A simple error reporting routine */
void yyerror(const char* fmt, ...)
{
char* txtBuf;
size_t size;
va_list args;
va_start(args, fmt);
size = vsnprintf(NULL, 0, fmt, args);
va_end(args);
txtBuf = (char *)alloca(size + 1);
va_start(args, fmt);
vsprintf(txtBuf, fmt, args);
va_end(args);
debug(LOG_ERROR, "VLO parse error: %s at line %d, text: '%s'",
txtBuf, scrv_get_lineno(), scrv_get_text());
}