mirror of
https://github.com/ZDoom/acc.git
synced 2024-11-14 16:41:01 +00:00
Add script/function arrays to ACC
- Script arrays are semantically identical to map arrays: They have fixed sizes and can be multidimensional. They can also be initialized, although this is just syntactic sugar. e.g. int init_array[2] = { 10, 20 } produces code identical to: int init_array[2]; init_array[0] = 10; init_array[1] = 20; Script arrays are also implicitly initialized to 0, just like any other variable in ACS. - Unlike map arrays, they only exist for the life of the script, and each script gets its own copy of the array. - Script arrays exists in a separate space from script variables. I did not repeat the bullshittery of map arrays here. (I still have no idea what I was thinking when I designed them that way.) - Each script and function that uses local arrays is recorded with a new chunk: SARY for scripts and FARY for functions. The first two bytes are the script/function number, and the rest of the chunk consists of four-byte integers describing the size (in ints) of each array used by that script/function. To determine how many arrays a script/function uses, take the chunk length, subtract two, and divide by four.
This commit is contained in:
parent
bede8930a8
commit
ac678ff7b8
8 changed files with 371 additions and 97 deletions
2
common.h
2
common.h
|
@ -56,6 +56,8 @@
|
|||
|
||||
#define MAX_IMPORTS 256
|
||||
|
||||
#define MAX_SCRIPT_ARRAYS 255
|
||||
|
||||
// Max number of include paths the user can specify
|
||||
// This includes the "working directory"!
|
||||
#define MAX_INCLUDE_PATHS 16
|
||||
|
|
2
error.c
2
error.c
|
@ -80,6 +80,8 @@ static struct
|
|||
{ ERR_TOO_MANY_MAP_VARS, "Too many map variables." },
|
||||
{ ERR_TOO_MANY_SCRIPT_VARS, "Too many script variables." },
|
||||
{ ERR_TOO_MANY_FUNCTION_VARS, "Too many function variables." },
|
||||
{ ERR_TOO_MANY_SCRIPT_ARRAYS, "Too many script arrays." },
|
||||
{ ERR_TOO_MANY_FUNCTION_ARRAYS, "Too many function arrays." },
|
||||
{ ERR_MISSING_WVAR_INDEX, "Missing index in world variable declaration." },
|
||||
{ ERR_MISSING_GVAR_INDEX, "Missing index in global variable declaration." },
|
||||
{ ERR_BAD_WVAR_INDEX, "World variable index out of range." },
|
||||
|
|
2
error.h
2
error.h
|
@ -60,6 +60,8 @@ typedef enum
|
|||
ERR_STRING_LIT_NOT_FOUND,
|
||||
ERR_TOO_MANY_SCRIPT_VARS,
|
||||
ERR_TOO_MANY_FUNCTION_VARS,
|
||||
ERR_TOO_MANY_SCRIPT_ARRAYS,
|
||||
ERR_TOO_MANY_FUNCTION_ARRAYS,
|
||||
ERR_INVALID_DECLARATOR,
|
||||
ERR_BAD_LSPEC_ARG_COUNT,
|
||||
ERR_BAD_ARG_COUNT,
|
||||
|
|
358
parse.c
358
parse.c
|
@ -143,8 +143,11 @@ static pcd_t TokenToPCD(tokenType_t token);
|
|||
static pcd_t GetPushVarPCD(symbolType_t symType);
|
||||
static pcd_t GetIncDecPCD(tokenType_t token, symbolType_t symbol);
|
||||
static int EvalConstExpression(void);
|
||||
static void ParseArrayDims(int *size, int *ndim, int dims[MAX_ARRAY_DIMS]);
|
||||
static void SymToArray(int symtype, symbolNode_t *sym, int index, int ndim, int size, int dims[MAX_ARRAY_DIMS]);
|
||||
static void ParseArrayIndices(symbolNode_t *sym, int requiredIndices);
|
||||
static void InitializeArray(symbolNode_t *sym, int dims[MAX_ARRAY_DIMS], int size);
|
||||
static void InitializeScriptArray(symbolNode_t *sym, int dims[MAX_ARRAY_DIMS], int size);
|
||||
static symbolNode_t *DemandSymbol(char *name);
|
||||
static symbolNode_t *SpeculateSymbol(char *name, boolean hasReturn);
|
||||
static symbolNode_t *SpeculateFunction(const char *name, boolean hasReturn);
|
||||
|
@ -171,6 +174,8 @@ boolean pa_ConstExprIsString;
|
|||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
static int ScriptVarCount;
|
||||
static int ScriptArrayCount;
|
||||
static int ScriptArraySize[MAX_SCRIPT_ARRAYS];
|
||||
static statement_t StatementHistory[MAX_STATEMENT_DEPTH];
|
||||
static int StatementIndex;
|
||||
static breakInfo_t BreakInfo[MAX_BREAK];
|
||||
|
@ -533,6 +538,7 @@ static void OuterScript(void)
|
|||
CaseIndex = 0;
|
||||
StatementLevel = 0;
|
||||
ScriptVarCount = 0;
|
||||
ScriptArrayCount = 0;
|
||||
SY_FreeLocals();
|
||||
TK_NextToken();
|
||||
|
||||
|
@ -738,7 +744,7 @@ static void OuterScript(void)
|
|||
{
|
||||
PC_AppendCmd(PCD_TERMINATE);
|
||||
}
|
||||
PC_SetScriptVarCount(scriptNumber, scriptType, ScriptVarCount);
|
||||
PC_SetScriptVarCount(scriptNumber, scriptType, ScriptVarCount, ScriptArrayCount, ScriptArraySize);
|
||||
pa_ScriptCount++;
|
||||
}
|
||||
|
||||
|
@ -761,6 +767,7 @@ static void OuterFunction(void)
|
|||
CaseIndex = 0;
|
||||
StatementLevel = 0;
|
||||
ScriptVarCount = 0;
|
||||
ScriptArrayCount = 0;
|
||||
SY_FreeLocals();
|
||||
TK_NextToken();
|
||||
if(tk_Token != TK_STR && tk_Token != TK_INT &&
|
||||
|
@ -911,7 +918,7 @@ static void OuterFunction(void)
|
|||
sym->info.scriptFunc.predefined = NO;
|
||||
sym->info.scriptFunc.varCount = ScriptVarCount -
|
||||
sym->info.scriptFunc.argCount;
|
||||
PC_AddFunction(sym);
|
||||
PC_AddFunction(sym, ScriptArrayCount, ScriptArraySize);
|
||||
UnspeculateFunction(sym);
|
||||
InsideFunction = NULL;
|
||||
}
|
||||
|
@ -925,7 +932,7 @@ static void OuterFunction(void)
|
|||
static void OuterMapVar(boolean local)
|
||||
{
|
||||
symbolNode_t *sym = NULL;
|
||||
int index, i;
|
||||
int index;
|
||||
|
||||
MS_Message(MSG_DEBUG, "---- %s ----\n", local ? "LeadingStaticVarDeclare" : "OuterMapVar");
|
||||
do
|
||||
|
@ -980,75 +987,17 @@ static void OuterMapVar(boolean local)
|
|||
}
|
||||
else if(tk_Token == TK_LBRACKET)
|
||||
{
|
||||
int size = 0;
|
||||
int ndim = 0;
|
||||
int dims[MAX_ARRAY_DIMS];
|
||||
int size, ndim, dims[MAX_ARRAY_DIMS];
|
||||
|
||||
memset(dims, 0, sizeof(dims));
|
||||
ParseArrayDims(&size, &ndim, dims);
|
||||
|
||||
while(tk_Token == TK_LBRACKET)
|
||||
{
|
||||
if(ndim == MAX_ARRAY_DIMS)
|
||||
{
|
||||
ERR_Error(ERR_TOO_MANY_ARRAY_DIMS, YES);
|
||||
do
|
||||
{
|
||||
TK_NextToken();
|
||||
} while(tk_Token != TK_COMMA && tk_Token != TK_SEMICOLON);
|
||||
break;
|
||||
}
|
||||
TK_NextToken();
|
||||
if (tk_Token == TK_RBRACKET)
|
||||
{
|
||||
ERR_Error(ERR_NEED_ARRAY_SIZE, YES);
|
||||
}
|
||||
else
|
||||
{
|
||||
dims[ndim] = EvalConstExpression();
|
||||
if(dims[ndim] == 0)
|
||||
{
|
||||
ERR_Error(ERR_ZERO_DIMENSION, YES);
|
||||
dims[ndim] = 1;
|
||||
}
|
||||
if(ndim == 0)
|
||||
{
|
||||
size = dims[ndim];
|
||||
}
|
||||
else
|
||||
{
|
||||
size *= dims[ndim];
|
||||
}
|
||||
}
|
||||
ndim++;
|
||||
TK_TokenMustBe(TK_RBRACKET, ERR_MISSING_RBRACKET);
|
||||
TK_NextToken();
|
||||
}
|
||||
if(sym != NULL)
|
||||
{
|
||||
if(ImportMode != IMPORT_Importing)
|
||||
{
|
||||
PC_AddArray(index, size);
|
||||
}
|
||||
MS_Message(MSG_DEBUG, "%s changed to an array of size %d\n", sym->name, size);
|
||||
sym->type = SY_MAPARRAY;
|
||||
sym->info.array.index = index;
|
||||
sym->info.array.ndim = ndim;
|
||||
sym->info.array.size = size;
|
||||
if(ndim > 0)
|
||||
{
|
||||
sym->info.array.dimensions[ndim-1] = 1;
|
||||
for(i = ndim - 2; i >= 0; --i)
|
||||
{
|
||||
sym->info.array.dimensions[i] =
|
||||
sym->info.array.dimensions[i+1] * dims[i+1];
|
||||
}
|
||||
}
|
||||
MS_Message(MSG_DEBUG, " - with multipliers ");
|
||||
for(i = 0; i < ndim; ++i)
|
||||
{
|
||||
MS_Message(MSG_DEBUG, "[%d]", sym->info.array.dimensions[i]);
|
||||
}
|
||||
MS_Message(MSG_DEBUG, "\n");
|
||||
SymToArray(SY_MAPARRAY, sym, index, size, ndim, dims);
|
||||
if(tk_Token == TK_ASSIGN)
|
||||
{
|
||||
InitializeArray(sym, dims, size);
|
||||
|
@ -1519,8 +1468,26 @@ static void LeadingVarDeclare(void)
|
|||
TK_NextToken();
|
||||
if(tk_Token == TK_LBRACKET)
|
||||
{
|
||||
ERR_Error(ERR_ARRAY_MAPVAR_ONLY, YES);
|
||||
do {} while(TK_NextToken() != TK_COMMA && tk_Token != TK_SEMICOLON);
|
||||
int size, ndim, dims[MAX_ARRAY_DIMS];
|
||||
|
||||
ParseArrayDims(&size, &ndim, dims);
|
||||
if(sym != NULL)
|
||||
{
|
||||
ScriptVarCount--;
|
||||
if(ScriptArrayCount == MAX_SCRIPT_ARRAYS)
|
||||
{
|
||||
ERR_Error(InsideFunction ? ERR_TOO_MANY_FUNCTION_ARRAYS : ERR_TOO_MANY_SCRIPT_ARRAYS, YES, NULL);
|
||||
}
|
||||
if(ScriptArrayCount < MAX_SCRIPT_ARRAYS)
|
||||
{
|
||||
ScriptArraySize[ScriptArrayCount] = size;
|
||||
}
|
||||
SymToArray(SY_SCRIPTARRAY, sym, ScriptArrayCount++, size, ndim, dims);
|
||||
if(tk_Token == TK_ASSIGN)
|
||||
{
|
||||
InitializeScriptArray(sym, dims, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(tk_Token == TK_ASSIGN)
|
||||
{
|
||||
|
@ -1785,6 +1752,7 @@ static void LeadingIdentifier(void)
|
|||
case SY_MAPVAR:
|
||||
case SY_WORLDVAR:
|
||||
case SY_GLOBALVAR:
|
||||
case SY_SCRIPTARRAY:
|
||||
case SY_WORLDARRAY:
|
||||
case SY_GLOBALARRAY:
|
||||
LeadingVarAssign(sym);
|
||||
|
@ -2257,7 +2225,7 @@ static void ActionOnCharRange(boolean write)
|
|||
|
||||
sym = SpeculateSymbol(tk_String, NO);
|
||||
if((sym->type != SY_MAPARRAY) && (sym->type != SY_WORLDARRAY)
|
||||
&& (sym->type != SY_GLOBALARRAY))
|
||||
&& (sym->type != SY_GLOBALARRAY) && (sym->type != SY_SCRIPTARRAY))
|
||||
{
|
||||
ERR_Error(ERR_NOT_AN_ARRAY, YES, sym->name);
|
||||
}
|
||||
|
@ -2333,7 +2301,12 @@ static void ActionOnCharRange(boolean write)
|
|||
}
|
||||
}
|
||||
|
||||
if(sym->type == SY_MAPARRAY)
|
||||
if(sym->type == SY_SCRIPTARRAY)
|
||||
{
|
||||
if (write) PC_AppendCmd(PCD_STRCPYTOSCRIPTCHRANGE);
|
||||
else PC_AppendCmd( rangeConstraints ? PCD_PRINTSCRIPTCHRANGE : PCD_PRINTSCRIPTCHARARRAY );
|
||||
}
|
||||
else if(sym->type == SY_MAPARRAY)
|
||||
{
|
||||
if (write) PC_AppendCmd(PCD_STRCPYTOMAPCHRANGE);
|
||||
else PC_AppendCmd( rangeConstraints ? PCD_PRINTMAPCHRANGE : PCD_PRINTMAPCHARARRAY );
|
||||
|
@ -3083,7 +3056,8 @@ static void LeadingIncDec(int token)
|
|||
if(sym->type != SY_SCRIPTVAR && sym->type != SY_MAPVAR
|
||||
&& sym->type != SY_WORLDVAR && sym->type != SY_GLOBALVAR
|
||||
&& sym->type != SY_MAPARRAY && sym->type != SY_GLOBALARRAY
|
||||
&& sym->type != SY_WORLDARRAY && sym->type != SY_SCRIPTALIAS)
|
||||
&& sym->type != SY_WORLDARRAY && sym->type != SY_SCRIPTALIAS
|
||||
&& sym->type != SY_SCRIPTARRAY)
|
||||
{
|
||||
ERR_Error(ERR_INCDEC_OP_ON_NON_VAR, YES);
|
||||
TK_SkipPast(TK_SEMICOLON);
|
||||
|
@ -3091,7 +3065,7 @@ static void LeadingIncDec(int token)
|
|||
}
|
||||
TK_NextToken();
|
||||
if(sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY
|
||||
|| sym->type == SY_GLOBALARRAY)
|
||||
|| sym->type == SY_GLOBALARRAY || sym->type == SY_SCRIPTARRAY)
|
||||
{
|
||||
ParseArrayIndices(sym, sym->info.array.ndim);
|
||||
}
|
||||
|
@ -3133,7 +3107,7 @@ static void LeadingVarAssign(symbolNode_t *sym)
|
|||
{
|
||||
TK_NextToken(); // Fetch assignment operator
|
||||
if(sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY
|
||||
|| sym->type == SY_GLOBALARRAY)
|
||||
|| sym->type == SY_GLOBALARRAY || sym->type == SY_SCRIPTARRAY)
|
||||
{
|
||||
ParseArrayIndices(sym, sym->info.array.ndim);
|
||||
}
|
||||
|
@ -3214,21 +3188,21 @@ static pcd_t GetAssignPCD(tokenType_t token, symbolType_t symbol)
|
|||
static symbolType_t symbolLookup[] =
|
||||
{
|
||||
SY_SCRIPTVAR, SY_MAPVAR, SY_WORLDVAR, SY_GLOBALVAR, SY_MAPARRAY,
|
||||
SY_WORLDARRAY, SY_GLOBALARRAY
|
||||
SY_WORLDARRAY, SY_GLOBALARRAY, SY_SCRIPTARRAY
|
||||
};
|
||||
static pcd_t assignmentLookup[11][7] =
|
||||
static pcd_t assignmentLookup[11][8] =
|
||||
{
|
||||
{ PCD_ASSIGNSCRIPTVAR, PCD_ASSIGNMAPVAR, PCD_ASSIGNWORLDVAR, PCD_ASSIGNGLOBALVAR, PCD_ASSIGNMAPARRAY, PCD_ASSIGNWORLDARRAY, PCD_ASSIGNGLOBALARRAY },
|
||||
{ PCD_ADDSCRIPTVAR, PCD_ADDMAPVAR, PCD_ADDWORLDVAR, PCD_ADDGLOBALVAR, PCD_ADDMAPARRAY, PCD_ADDWORLDARRAY, PCD_ADDGLOBALARRAY },
|
||||
{ PCD_SUBSCRIPTVAR, PCD_SUBMAPVAR, PCD_SUBWORLDVAR, PCD_SUBGLOBALVAR, PCD_SUBMAPARRAY, PCD_SUBWORLDARRAY, PCD_SUBGLOBALARRAY },
|
||||
{ PCD_MULSCRIPTVAR, PCD_MULMAPVAR, PCD_MULWORLDVAR, PCD_MULGLOBALVAR, PCD_MULMAPARRAY, PCD_MULWORLDARRAY, PCD_MULGLOBALARRAY },
|
||||
{ PCD_DIVSCRIPTVAR, PCD_DIVMAPVAR, PCD_DIVWORLDVAR, PCD_DIVGLOBALVAR, PCD_DIVMAPARRAY, PCD_DIVWORLDARRAY, PCD_DIVGLOBALARRAY },
|
||||
{ PCD_MODSCRIPTVAR, PCD_MODMAPVAR, PCD_MODWORLDVAR, PCD_MODGLOBALVAR, PCD_MODMAPARRAY, PCD_MODWORLDARRAY, PCD_MODGLOBALARRAY },
|
||||
{ PCD_ANDSCRIPTVAR, PCD_ANDMAPVAR, PCD_ANDWORLDVAR, PCD_ANDGLOBALVAR, PCD_ANDMAPARRAY, PCD_ANDWORLDARRAY, PCD_ANDGLOBALARRAY },
|
||||
{ PCD_EORSCRIPTVAR, PCD_EORMAPVAR, PCD_EORWORLDVAR, PCD_EORGLOBALVAR, PCD_EORMAPARRAY, PCD_EORWORLDARRAY, PCD_EORGLOBALARRAY },
|
||||
{ PCD_ORSCRIPTVAR, PCD_ORMAPVAR, PCD_ORWORLDVAR, PCD_ORGLOBALVAR, PCD_ORMAPARRAY, PCD_ORWORLDARRAY, PCD_ORGLOBALARRAY },
|
||||
{ PCD_LSSCRIPTVAR, PCD_LSMAPVAR, PCD_LSWORLDVAR, PCD_LSGLOBALVAR, PCD_LSMAPARRAY, PCD_LSWORLDARRAY, PCD_LSGLOBALARRAY },
|
||||
{ PCD_RSSCRIPTVAR, PCD_RSMAPVAR, PCD_RSWORLDVAR, PCD_RSGLOBALVAR, PCD_RSMAPARRAY, PCD_RSWORLDARRAY, PCD_RSGLOBALARRAY }
|
||||
{ PCD_ASSIGNSCRIPTVAR, PCD_ASSIGNMAPVAR, PCD_ASSIGNWORLDVAR, PCD_ASSIGNGLOBALVAR, PCD_ASSIGNMAPARRAY, PCD_ASSIGNWORLDARRAY, PCD_ASSIGNGLOBALARRAY, PCD_ASSIGNSCRIPTARRAY },
|
||||
{ PCD_ADDSCRIPTVAR, PCD_ADDMAPVAR, PCD_ADDWORLDVAR, PCD_ADDGLOBALVAR, PCD_ADDMAPARRAY, PCD_ADDWORLDARRAY, PCD_ADDGLOBALARRAY, PCD_ANDSCRIPTARRAY },
|
||||
{ PCD_SUBSCRIPTVAR, PCD_SUBMAPVAR, PCD_SUBWORLDVAR, PCD_SUBGLOBALVAR, PCD_SUBMAPARRAY, PCD_SUBWORLDARRAY, PCD_SUBGLOBALARRAY, PCD_SUBSCRIPTARRAY },
|
||||
{ PCD_MULSCRIPTVAR, PCD_MULMAPVAR, PCD_MULWORLDVAR, PCD_MULGLOBALVAR, PCD_MULMAPARRAY, PCD_MULWORLDARRAY, PCD_MULGLOBALARRAY, PCD_MULSCRIPTARRAY },
|
||||
{ PCD_DIVSCRIPTVAR, PCD_DIVMAPVAR, PCD_DIVWORLDVAR, PCD_DIVGLOBALVAR, PCD_DIVMAPARRAY, PCD_DIVWORLDARRAY, PCD_DIVGLOBALARRAY, PCD_DIVSCRIPTARRAY },
|
||||
{ PCD_MODSCRIPTVAR, PCD_MODMAPVAR, PCD_MODWORLDVAR, PCD_MODGLOBALVAR, PCD_MODMAPARRAY, PCD_MODWORLDARRAY, PCD_MODGLOBALARRAY, PCD_MODSCRIPTARRAY },
|
||||
{ PCD_ANDSCRIPTVAR, PCD_ANDMAPVAR, PCD_ANDWORLDVAR, PCD_ANDGLOBALVAR, PCD_ANDMAPARRAY, PCD_ANDWORLDARRAY, PCD_ANDGLOBALARRAY, PCD_ANDSCRIPTARRAY },
|
||||
{ PCD_EORSCRIPTVAR, PCD_EORMAPVAR, PCD_EORWORLDVAR, PCD_EORGLOBALVAR, PCD_EORMAPARRAY, PCD_EORWORLDARRAY, PCD_EORGLOBALARRAY, PCD_EORSCRIPTARRAY },
|
||||
{ PCD_ORSCRIPTVAR, PCD_ORMAPVAR, PCD_ORWORLDVAR, PCD_ORGLOBALVAR, PCD_ORMAPARRAY, PCD_ORWORLDARRAY, PCD_ORGLOBALARRAY, PCD_ORSCRIPTARRAY },
|
||||
{ PCD_LSSCRIPTVAR, PCD_LSMAPVAR, PCD_LSWORLDVAR, PCD_LSGLOBALVAR, PCD_LSMAPARRAY, PCD_LSWORLDARRAY, PCD_LSGLOBALARRAY, PCD_LSSCRIPTARRAY },
|
||||
{ PCD_RSSCRIPTVAR, PCD_RSMAPVAR, PCD_RSWORLDVAR, PCD_RSGLOBALVAR, PCD_RSMAPARRAY, PCD_RSWORLDARRAY, PCD_RSGLOBALARRAY, PCD_RSSCRIPTARRAY }
|
||||
};
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(tokenLookup); ++i)
|
||||
|
@ -3593,7 +3567,7 @@ static void ExprFactor(void)
|
|||
if(sym->type != SY_SCRIPTVAR && sym->type != SY_MAPVAR
|
||||
&& sym->type != SY_WORLDVAR && sym->type != SY_GLOBALVAR
|
||||
&& sym->type != SY_MAPARRAY && sym->type != SY_WORLDARRAY
|
||||
&& sym->type != SY_GLOBALARRAY)
|
||||
&& sym->type != SY_GLOBALARRAY && sym->type != SY_SCRIPTARRAY)
|
||||
{
|
||||
ERR_Error(ERR_INCDEC_OP_ON_NON_VAR, YES);
|
||||
}
|
||||
|
@ -3601,7 +3575,7 @@ static void ExprFactor(void)
|
|||
{
|
||||
TK_NextToken();
|
||||
if(sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY
|
||||
|| sym->type == SY_GLOBALARRAY)
|
||||
|| sym->type == SY_GLOBALARRAY || sym->type == SY_SCRIPTARRAY)
|
||||
{
|
||||
ParseArrayIndices(sym, sym->info.array.ndim);
|
||||
PC_AppendCmd(PCD_DUP);
|
||||
|
@ -3627,6 +3601,7 @@ static void ExprFactor(void)
|
|||
case SY_SCRIPTALIAS:
|
||||
// FIXME
|
||||
break;
|
||||
case SY_SCRIPTARRAY:
|
||||
case SY_MAPARRAY:
|
||||
case SY_WORLDARRAY:
|
||||
case SY_GLOBALARRAY:
|
||||
|
@ -3638,7 +3613,7 @@ static void ExprFactor(void)
|
|||
case SY_WORLDVAR:
|
||||
case SY_GLOBALVAR:
|
||||
if(sym->type != SY_MAPARRAY && sym->type != SY_WORLDARRAY
|
||||
&& sym->type != SY_GLOBALARRAY)
|
||||
&& sym->type != SY_GLOBALARRAY && sym->type != SY_SCRIPTARRAY)
|
||||
{
|
||||
TK_NextToken();
|
||||
if(tk_Token == TK_LBRACKET)
|
||||
|
@ -3652,7 +3627,7 @@ static void ExprFactor(void)
|
|||
}
|
||||
if((tk_Token == TK_INC || tk_Token == TK_DEC)
|
||||
&& (sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY
|
||||
|| sym->type == SY_GLOBALARRAY))
|
||||
|| sym->type == SY_GLOBALARRAY || sym->type == SY_SCRIPTARRAY))
|
||||
{
|
||||
PC_AppendCmd(PCD_DUP);
|
||||
}
|
||||
|
@ -3661,7 +3636,7 @@ static void ExprFactor(void)
|
|||
if(tk_Token == TK_INC || tk_Token == TK_DEC)
|
||||
{
|
||||
if(sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY
|
||||
|| sym->type == SY_GLOBALARRAY)
|
||||
|| sym->type == SY_GLOBALARRAY || sym->type == SY_SCRIPTARRAY)
|
||||
{
|
||||
PC_AppendCmd(PCD_SWAP);
|
||||
}
|
||||
|
@ -3967,6 +3942,8 @@ static pcd_t GetPushVarPCD(symbolType_t symType)
|
|||
return PCD_PUSHWORLDVAR;
|
||||
case SY_GLOBALVAR:
|
||||
return PCD_PUSHGLOBALVAR;
|
||||
case SY_SCRIPTARRAY:
|
||||
return PCD_PUSHSCRIPTARRAY;
|
||||
case SY_MAPARRAY:
|
||||
return PCD_PUSHMAPARRAY;
|
||||
case SY_WORLDARRAY:
|
||||
|
@ -3999,6 +3976,7 @@ static pcd_t GetIncDecPCD(tokenType_t token, symbolType_t symbol)
|
|||
{ TK_INC, SY_MAPVAR, PCD_INCMAPVAR },
|
||||
{ TK_INC, SY_WORLDVAR, PCD_INCWORLDVAR },
|
||||
{ TK_INC, SY_GLOBALVAR, PCD_INCGLOBALVAR },
|
||||
{ TK_INC, SY_SCRIPTARRAY, PCD_INCSCRIPTARRAY },
|
||||
{ TK_INC, SY_MAPARRAY, PCD_INCMAPARRAY },
|
||||
{ TK_INC, SY_WORLDARRAY, PCD_INCWORLDARRAY },
|
||||
{ TK_INC, SY_GLOBALARRAY, PCD_INCGLOBALARRAY },
|
||||
|
@ -4007,6 +3985,7 @@ static pcd_t GetIncDecPCD(tokenType_t token, symbolType_t symbol)
|
|||
{ TK_DEC, SY_MAPVAR, PCD_DECMAPVAR },
|
||||
{ TK_DEC, SY_WORLDVAR, PCD_DECWORLDVAR },
|
||||
{ TK_DEC, SY_GLOBALVAR, PCD_DECGLOBALVAR },
|
||||
{ TK_DEC, SY_SCRIPTARRAY, PCD_DECSCRIPTARRAY },
|
||||
{ TK_DEC, SY_MAPARRAY, PCD_DECMAPARRAY },
|
||||
{ TK_DEC, SY_WORLDARRAY, PCD_DECWORLDARRAY },
|
||||
{ TK_DEC, SY_GLOBALARRAY, PCD_DECGLOBALARRAY },
|
||||
|
@ -4025,6 +4004,91 @@ static pcd_t GetIncDecPCD(tokenType_t token, symbolType_t symbol)
|
|||
return PCD_NOP;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ParseArrayDims
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void ParseArrayDims(int *size_p, int *ndim_p, int dims[MAX_ARRAY_DIMS])
|
||||
{
|
||||
int size = 0;
|
||||
int ndim = 0;
|
||||
memset(dims, 0, sizeof(dims));
|
||||
|
||||
while(tk_Token == TK_LBRACKET)
|
||||
{
|
||||
if(ndim == MAX_ARRAY_DIMS)
|
||||
{
|
||||
ERR_Error(ERR_TOO_MANY_ARRAY_DIMS, YES);
|
||||
do
|
||||
{
|
||||
TK_NextToken();
|
||||
} while(tk_Token != TK_COMMA && tk_Token != TK_SEMICOLON);
|
||||
break;
|
||||
}
|
||||
TK_NextToken();
|
||||
if (tk_Token == TK_RBRACKET)
|
||||
{
|
||||
ERR_Error(ERR_NEED_ARRAY_SIZE, YES);
|
||||
}
|
||||
else
|
||||
{
|
||||
dims[ndim] = EvalConstExpression();
|
||||
if(dims[ndim] == 0)
|
||||
{
|
||||
ERR_Error(ERR_ZERO_DIMENSION, YES);
|
||||
dims[ndim] = 1;
|
||||
}
|
||||
if(ndim == 0)
|
||||
{
|
||||
size = dims[ndim];
|
||||
}
|
||||
else
|
||||
{
|
||||
size *= dims[ndim];
|
||||
}
|
||||
}
|
||||
ndim++;
|
||||
TK_TokenMustBe(TK_RBRACKET, ERR_MISSING_RBRACKET);
|
||||
TK_NextToken();
|
||||
}
|
||||
if(pc_EnforceHexen)
|
||||
{
|
||||
TK_Undo(); // backup so error pointer is on last bracket instead of following token
|
||||
ERR_Error(ERR_HEXEN_COMPAT, YES);
|
||||
TK_NextToken();
|
||||
}
|
||||
*size_p = size;
|
||||
*ndim_p = ndim;
|
||||
}
|
||||
|
||||
static void SymToArray(int symtype, symbolNode_t *sym, int index, int size, int ndim, int dims[MAX_ARRAY_DIMS])
|
||||
{
|
||||
int i;
|
||||
|
||||
MS_Message(MSG_DEBUG, "%s changed to an array of size %d\n", sym->name, size);
|
||||
sym->type = symtype;
|
||||
sym->info.array.index = index;
|
||||
sym->info.array.ndim = ndim;
|
||||
sym->info.array.size = size;
|
||||
if(ndim > 0)
|
||||
{
|
||||
sym->info.array.dimensions[ndim-1] = 1;
|
||||
for(i = ndim - 2; i >= 0; --i)
|
||||
{
|
||||
sym->info.array.dimensions[i] =
|
||||
sym->info.array.dimensions[i+1] * dims[i+1];
|
||||
}
|
||||
}
|
||||
MS_Message(MSG_DEBUG, " - with multipliers ");
|
||||
for(i = 0; i < ndim; ++i)
|
||||
{
|
||||
MS_Message(MSG_DEBUG, "[%d]", sym->info.array.dimensions[i]);
|
||||
}
|
||||
MS_Message(MSG_DEBUG, "\n");
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ParseArrayIndices
|
||||
|
@ -4044,8 +4108,8 @@ static void ParseArrayIndices(symbolNode_t *sym, int requiredIndices)
|
|||
while(tk_Token == TK_LBRACKET)
|
||||
{
|
||||
TK_NextToken();
|
||||
if((sym->type == SY_MAPARRAY && i == requiredIndices) ||
|
||||
(sym->type != SY_MAPARRAY && i > 0))
|
||||
if(((sym->type == SY_MAPARRAY || sym->type == SY_SCRIPTARRAY) && i == requiredIndices) ||
|
||||
(sym->type != SY_MAPARRAY && sym->type != SY_SCRIPTARRAY && i > 0))
|
||||
{
|
||||
if (!warned)
|
||||
{
|
||||
|
@ -4097,6 +4161,12 @@ static void ParseArrayIndices(symbolNode_t *sym, int requiredIndices)
|
|||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ProcessArrayLevel
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void ProcessArrayLevel(int level, int *entry, int ndim,
|
||||
int dims[MAX_ARRAY_DIMS], int muls[MAX_ARRAY_DIMS], char *name)
|
||||
{
|
||||
|
@ -4216,6 +4286,112 @@ static void InitializeArray(symbolNode_t *sym, int dims[MAX_ARRAY_DIMS], int siz
|
|||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ProcessScriptArrayLevel
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void ProcessScriptArrayLevel(int level, symbolNode_t *sym, int ndim,
|
||||
int dims[MAX_ARRAY_DIMS], int muls[MAX_ARRAY_DIMS], char *name)
|
||||
{
|
||||
int warned_too_many = NO;
|
||||
int i;
|
||||
int loc = 0;
|
||||
|
||||
for(i = 0; ; ++i)
|
||||
{
|
||||
if(tk_Token == TK_COMMA)
|
||||
{
|
||||
loc += muls[level-1];
|
||||
TK_NextToken();
|
||||
}
|
||||
else if(tk_Token == TK_RBRACE)
|
||||
{
|
||||
TK_NextToken();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(level == ndim)
|
||||
{
|
||||
if(tk_Token == TK_LBRACE)
|
||||
{
|
||||
ERR_Error(ERR_TOO_MANY_DIM_USED, YES, name, ndim);
|
||||
SkipBraceBlock(0);
|
||||
TK_NextToken();
|
||||
loc++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(i >= dims[level - 1] && !warned_too_many)
|
||||
{
|
||||
warned_too_many = YES;
|
||||
ERR_Error(ERR_TOO_MANY_ARRAY_INIT, YES);
|
||||
}
|
||||
PC_AppendPushVal(loc + i);
|
||||
EvalExpression();
|
||||
PC_AppendCmd(PCD_ASSIGNSCRIPTARRAY);
|
||||
PC_AppendShrink(sym->info.array.index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Bugfix for r3226 by Zom-B
|
||||
if (i >= dims[level - 1])
|
||||
{
|
||||
if (!warned_too_many)
|
||||
{
|
||||
warned_too_many = YES;
|
||||
ERR_Error(ERR_TOO_MANY_ARRAY_INIT, YES);
|
||||
}
|
||||
}
|
||||
TK_TokenMustBe(TK_LBRACE, ERR_MISSING_LBRACE_ARR);
|
||||
TK_NextToken();
|
||||
ProcessScriptArrayLevel(level+1, sym, ndim, dims, muls, name);
|
||||
assert(level > 0);
|
||||
loc += muls[level-1];
|
||||
}
|
||||
if(i < dims[level-1]-1)
|
||||
{
|
||||
if(tk_Token != TK_RBRACE)
|
||||
{
|
||||
TK_TokenMustBe(TK_COMMA, ERR_MISSING_COMMA);
|
||||
TK_NextToken();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(tk_Token != TK_COMMA)
|
||||
{
|
||||
TK_TokenMustBe(TK_RBRACE, ERR_MISSING_RBRACE_ARR);
|
||||
}
|
||||
else
|
||||
{
|
||||
TK_NextToken();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TK_TokenMustBe(TK_RBRACE, ERR_MISSING_RBRACE_ARR);
|
||||
TK_NextToken();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// InitializeScriptArray
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void InitializeScriptArray(symbolNode_t *sym, int dims[MAX_ARRAY_DIMS], int size)
|
||||
{
|
||||
TK_NextTokenMustBe(TK_LBRACE, ERR_MISSING_LBRACE_ARR);
|
||||
TK_NextToken();
|
||||
ArrayHasStrings = NO;
|
||||
ProcessScriptArrayLevel(1, sym, sym->info.array.ndim, dims,
|
||||
sym->info.array.dimensions, sym->name);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DemandSymbol
|
||||
|
|
78
pcode.c
78
pcode.c
|
@ -28,11 +28,13 @@ typedef struct scriptInfo_s
|
|||
S_WORD number;
|
||||
U_BYTE type;
|
||||
U_BYTE argCount;
|
||||
U_BYTE arrayCount;
|
||||
U_WORD varCount;
|
||||
U_WORD flags;
|
||||
int address;
|
||||
int srcLine;
|
||||
boolean imported;
|
||||
int arraySizes[MAX_SCRIPT_ARRAYS];
|
||||
} scriptInfo_t;
|
||||
|
||||
typedef struct functionInfo_s
|
||||
|
@ -40,8 +42,10 @@ typedef struct functionInfo_s
|
|||
U_BYTE hasReturnValue;
|
||||
U_BYTE argCount;
|
||||
U_BYTE localCount;
|
||||
U_BYTE arrayCount;
|
||||
int address;
|
||||
int name;
|
||||
int arraySizes[MAX_SCRIPT_ARRAYS];
|
||||
} functionInfo_t;
|
||||
|
||||
typedef struct mapVarInfo_s
|
||||
|
@ -103,6 +107,7 @@ static int PushByteAddr;
|
|||
static char Imports[MAX_IMPORTS][9];
|
||||
static int NumImports;
|
||||
static boolean HaveExtendedScripts;
|
||||
static boolean HaveScriptArrays;
|
||||
|
||||
static char *PCDNames[PCODE_COMMAND_COUNT] =
|
||||
{
|
||||
|
@ -476,6 +481,23 @@ static char *PCDNames[PCODE_COMMAND_COUNT] =
|
|||
"PCD_CALLSTACK", // from Eternity
|
||||
"PCD_SCRIPTWAITNAMED",
|
||||
"PCD_TRANSLATIONRANGE3",
|
||||
"PCD_ASSIGNSCRIPTARRAY",
|
||||
"PCD_PUSHSCRIPTARRAY",
|
||||
"PCD_ADDSCRIPTARRAY",
|
||||
"PCD_SUBSCRIPTARRAY",
|
||||
"PCD_MULSCRIPTARRAY",
|
||||
"PCD_DIVSCRIPTARRAY",
|
||||
"PCD_MODSCRIPTARRAY",
|
||||
"PCD_INCSCRIPTARRAY",
|
||||
"PCD_DECSCRIPTARRAY",
|
||||
"PCD_ANDSCRIPTARRAY",
|
||||
"PCD_EORSCRIPTARRAY",
|
||||
"PCD_ORSCRIPTARRAY",
|
||||
"PCD_LSSCRIPTARRAY",
|
||||
"PCD_RSSCRIPTARRAY",
|
||||
"PCD_PRINTSCRIPTCHARARRAY",
|
||||
"PCD_PRINTSCRIPTCHRANGE",
|
||||
"PCD_STRCPYTOSCRIPTCHRANGE",
|
||||
};
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
@ -523,7 +545,8 @@ void PC_CloseObject(void)
|
|||
}
|
||||
if(!pc_NoShrink || (NumLanguages > 1) || (NumStringLists > 0) ||
|
||||
(pc_FunctionCount > 0) || MapVariablesInit || NumArrays != 0 ||
|
||||
pc_EncryptStrings || NumImports != 0 || HaveExtendedScripts)
|
||||
pc_EncryptStrings || NumImports != 0 || HaveExtendedScripts ||
|
||||
HaveScriptArrays)
|
||||
{
|
||||
if(pc_EnforceHexen)
|
||||
{
|
||||
|
@ -672,9 +695,25 @@ static void CloseNew(void)
|
|||
}
|
||||
}
|
||||
|
||||
// Add chunks for scripts with arrays
|
||||
for(i = 0; i < pc_ScriptCount; ++i)
|
||||
{
|
||||
if(ScriptInfo[i].arrayCount)
|
||||
{
|
||||
PC_Append("SARY", 4);
|
||||
PC_AppendInt(2 + ScriptInfo[i].arrayCount * 4);
|
||||
PC_AppendWord(ScriptInfo[i].number);
|
||||
for(j = 0; j < ScriptInfo[i].arrayCount; ++j)
|
||||
{
|
||||
PC_AppendInt(ScriptInfo[i].arraySizes[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write the string table for named scripts.
|
||||
STR_WriteListChunk(STRLIST_NAMEDSCRIPTS, MAKE4CC('S','N','A','M'), NO);
|
||||
|
||||
// Write the functions provided by this file.
|
||||
if(pc_FunctionCount > 0)
|
||||
{
|
||||
PC_Append("FUNC", 4);
|
||||
|
@ -694,6 +733,22 @@ static void CloseNew(void)
|
|||
STR_WriteListChunk(STRLIST_FUNCTIONS, MAKE4CC('F','N','A','M'), NO);
|
||||
}
|
||||
|
||||
// Add chunks for functions with arrays
|
||||
for(i = 0; i < pc_FunctionCount; ++i)
|
||||
{
|
||||
if(FunctionInfo[i].arrayCount)
|
||||
{
|
||||
PC_Append("FARY", 4);
|
||||
PC_AppendInt(2 + FunctionInfo[i].arrayCount * 4);
|
||||
PC_AppendWord(i);
|
||||
for(j = 0; j < FunctionInfo[i].arrayCount; ++j)
|
||||
{
|
||||
PC_AppendInt(FunctionInfo[i].arraySizes[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(NumLanguages > 1)
|
||||
{
|
||||
for(i = 0; i < NumLanguages; i++)
|
||||
|
@ -1426,6 +1481,7 @@ void PC_AddScript(int number, int type, int flags, int argCount)
|
|||
script->flags = flags;
|
||||
script->srcLine = tk_Line;
|
||||
script->imported = (ImportMode == IMPORT_Importing) ? YES : NO;
|
||||
script->arrayCount = 0;
|
||||
pc_ScriptCount++;
|
||||
}
|
||||
}
|
||||
|
@ -1439,7 +1495,7 @@ void PC_AddScript(int number, int type, int flags, int argCount)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void PC_SetScriptVarCount(int number, int type, int varCount)
|
||||
void PC_SetScriptVarCount(int number, int type, int varCount, int arrayCount, int *arraySizes)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -1448,6 +1504,12 @@ void PC_SetScriptVarCount(int number, int type, int varCount)
|
|||
if(ScriptInfo[i].number == number && ScriptInfo[i].type == type)
|
||||
{
|
||||
ScriptInfo[i].varCount = varCount;
|
||||
if (arrayCount > 0 && arrayCount < MAX_SCRIPT_ARRAYS)
|
||||
{
|
||||
ScriptInfo[i].arrayCount = (U_BYTE)arrayCount;
|
||||
memcpy(ScriptInfo[i].arraySizes, arraySizes, arrayCount * sizeof(int));
|
||||
HaveScriptArrays = YES;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1459,7 +1521,7 @@ void PC_SetScriptVarCount(int number, int type, int varCount)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void PC_AddFunction(symbolNode_t *sym)
|
||||
void PC_AddFunction(symbolNode_t *sym, int arrayCount, int *arraySizes)
|
||||
{
|
||||
functionInfo_t *function;
|
||||
|
||||
|
@ -1479,6 +1541,16 @@ void PC_AddFunction(symbolNode_t *sym)
|
|||
function->name = STR_AppendToList (STRLIST_FUNCTIONS, sym->name);
|
||||
function->address = sym->info.scriptFunc.address;
|
||||
sym->info.scriptFunc.funcNumber = pc_FunctionCount;
|
||||
if (arrayCount > 0 && arrayCount < MAX_SCRIPT_ARRAYS)
|
||||
{
|
||||
function->arrayCount = (U_BYTE)arrayCount;
|
||||
memcpy(function->arraySizes, arraySizes, arrayCount * sizeof(int));
|
||||
HaveScriptArrays = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
function->arrayCount = 0;
|
||||
}
|
||||
pc_FunctionCount++;
|
||||
}
|
||||
|
||||
|
|
22
pcode.h
22
pcode.h
|
@ -426,6 +426,24 @@ typedef enum
|
|||
PCD_SCRIPTWAITNAMED,
|
||||
PCD_TRANSLATIONRANGE3,
|
||||
|
||||
PCD_ASSIGNSCRIPTARRAY,
|
||||
PCD_PUSHSCRIPTARRAY,
|
||||
PCD_ADDSCRIPTARRAY,
|
||||
PCD_SUBSCRIPTARRAY,
|
||||
PCD_MULSCRIPTARRAY,
|
||||
PCD_DIVSCRIPTARRAY,
|
||||
PCD_MODSCRIPTARRAY,
|
||||
PCD_INCSCRIPTARRAY,
|
||||
PCD_DECSCRIPTARRAY,
|
||||
PCD_ANDSCRIPTARRAY,
|
||||
PCD_EORSCRIPTARRAY,
|
||||
PCD_ORSCRIPTARRAY,
|
||||
PCD_LSSCRIPTARRAY,
|
||||
PCD_RSSCRIPTARRAY,
|
||||
PCD_PRINTSCRIPTCHARARRAY,
|
||||
PCD_PRINTSCRIPTCHRANGE,
|
||||
PCD_STRCPYTOSCRIPTCHRANGE,
|
||||
|
||||
PCODE_COMMAND_COUNT
|
||||
} pcd_t;
|
||||
|
||||
|
@ -452,8 +470,8 @@ void PC_Skip(size_t size);
|
|||
//void PC_SkipWord(void);
|
||||
void PC_SkipInt(void);
|
||||
void PC_AddScript(int number, int type, int flags, int argCount);
|
||||
void PC_SetScriptVarCount(int number, int type, int varCount);
|
||||
void PC_AddFunction(struct symbolNode_s *sym);
|
||||
void PC_SetScriptVarCount(int number, int type, int varCount, int arrayCount, int *arraySizes);
|
||||
void PC_AddFunction(struct symbolNode_s *sym, int arrayCount, int *sizes);
|
||||
void PC_PutMapVariable(int index, int value);
|
||||
void PC_NameMapVariable(int index, struct symbolNode_s *sym);
|
||||
void PC_AddArray(int index, int size);
|
||||
|
|
3
symbol.c
3
symbol.c
|
@ -210,6 +210,7 @@ static char *SymbolTypeNames[] =
|
|||
"SY_MAPVAR",
|
||||
"SY_WORLDVAR",
|
||||
"SY_GLOBALVAR",
|
||||
"SY_SCRIPTARRAY",
|
||||
"SY_MAPARRAY",
|
||||
"SY_WORLDARRAY",
|
||||
"SY_GLOBALARRAY",
|
||||
|
@ -279,7 +280,7 @@ symbolNode_t *SY_FindGlobal(char *name)
|
|||
sym->unused = NO;
|
||||
if(sym->type == SY_SCRIPTFUNC)
|
||||
{
|
||||
PC_AddFunction(sym);
|
||||
PC_AddFunction(sym, 0, NULL);
|
||||
}
|
||||
else if(sym->type == SY_MAPVAR)
|
||||
{
|
||||
|
|
1
symbol.h
1
symbol.h
|
@ -28,6 +28,7 @@ typedef enum
|
|||
SY_MAPVAR,
|
||||
SY_WORLDVAR,
|
||||
SY_GLOBALVAR,
|
||||
SY_SCRIPTARRAY,
|
||||
SY_MAPARRAY,
|
||||
SY_WORLDARRAY,
|
||||
SY_GLOBALARRAY,
|
||||
|
|
Loading…
Reference in a new issue