mirror of
https://github.com/ZDoom/acc.git
synced 2024-11-12 23:54:12 +00:00
4961 lines
109 KiB
C
4961 lines
109 KiB
C
|
|
//**************************************************************************
|
|
//**
|
|
//** parse.c
|
|
//**
|
|
//**************************************************************************
|
|
|
|
// HEADER FILES ------------------------------------------------------------
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
|
|
#include "common.h"
|
|
#include "parse.h"
|
|
#include "symbol.h"
|
|
#include "pcode.h"
|
|
#include "token.h"
|
|
#include "error.h"
|
|
#include "misc.h"
|
|
#include "strlist.h"
|
|
|
|
// MACROS ------------------------------------------------------------------
|
|
|
|
#define MAX_STATEMENT_DEPTH 128
|
|
#define MAX_BREAK 128
|
|
#define MAX_CONTINUE 128
|
|
#define MAX_CASE 128
|
|
#define EXPR_STACK_DEPTH 64
|
|
|
|
// TYPES -------------------------------------------------------------------
|
|
|
|
typedef enum
|
|
{
|
|
STMT_SCRIPT,
|
|
STMT_IF,
|
|
STMT_ELSE,
|
|
STMT_DO,
|
|
STMT_WHILEUNTIL,
|
|
STMT_SWITCH,
|
|
STMT_FOR
|
|
} statement_t;
|
|
|
|
typedef struct
|
|
{
|
|
int level;
|
|
int addressPtr;
|
|
} breakInfo_t;
|
|
|
|
typedef struct
|
|
{
|
|
int level;
|
|
int addressPtr;
|
|
} continueInfo_t;
|
|
|
|
typedef struct
|
|
{
|
|
int level;
|
|
int value;
|
|
boolean isDefault;
|
|
int address;
|
|
} caseInfo_t;
|
|
|
|
typedef struct prefunc_s
|
|
{
|
|
struct prefunc_s *next;
|
|
symbolNode_t *sym;
|
|
int address;
|
|
int argcount;
|
|
int line;
|
|
char *source;
|
|
} prefunc_t;
|
|
|
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
|
|
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
|
|
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
|
|
|
static void CountScript(int type);
|
|
static void Outside(void);
|
|
static void OuterScript(void);
|
|
static void OuterFunction(void);
|
|
static void OuterMapVar(boolean local);
|
|
static void OuterWorldVar(boolean isGlobal);
|
|
static void OuterSpecialDef(void);
|
|
static void OuterDefine(boolean force);
|
|
static void OuterInclude(void);
|
|
static void OuterImport(void);
|
|
static boolean ProcessStatement(statement_t owner);
|
|
static void LeadingCompoundStatement(statement_t owner);
|
|
static void LeadingVarDeclare(void);
|
|
static void LeadingLineSpecial(boolean executewait);
|
|
static void LeadingFunction(boolean executewait);
|
|
static void LeadingIdentifier(void);
|
|
static void BuildPrintString(void);
|
|
static void ActionOnCharRange(boolean write);
|
|
static void LeadingStrcpy(void);
|
|
static void LeadingPrint(void);
|
|
static void LeadingHudMessage(void);
|
|
static void LeadingMorphActor(void);
|
|
static void LeadingLumpReadArray(void);
|
|
static void LeadingVarAssign(symbolNode_t *sym);
|
|
static pcd_t GetAssignPCD(tokenType_t token, symbolType_t symbol);
|
|
static void LeadingInternFunc(symbolNode_t *sym);
|
|
static void LeadingScriptFunc(symbolNode_t *sym);
|
|
static void LeadingSuspend(void);
|
|
static void LeadingTerminate(void);
|
|
static void LeadingRestart(void);
|
|
static void LeadingReturn(void);
|
|
static void LeadingIf(void);
|
|
static void LeadingFor(void);
|
|
static void LeadingWhileUntil(void);
|
|
static void LeadingDo(void);
|
|
static void LeadingSwitch(void);
|
|
static void LeadingCase(void);
|
|
static void LeadingDefault(void);
|
|
static void LeadingBreak(void);
|
|
static void LeadingContinue(void);
|
|
static void LeadingCreateTranslation(void);
|
|
static void LeadingIncDec(int token);
|
|
static void PushCase(int value, boolean isDefault);
|
|
static caseInfo_t *GetCaseInfo(void);
|
|
static int CaseInfoCmp(const void *a, const void *b);
|
|
static boolean DefaultInCurrent(void);
|
|
static void PushBreak(void);
|
|
static void WriteBreaks(void);
|
|
static boolean BreakAncestor(void);
|
|
static void PushContinue(void);
|
|
static void WriteContinues(int address);
|
|
static boolean ContinueAncestor(void);
|
|
static void ProcessInternFunc(symbolNode_t *sym);
|
|
static void ProcessScriptFunc(symbolNode_t *sym, boolean discardReturn);
|
|
static void EvalExpression(void);
|
|
static void ExprLevX(int level);
|
|
static void ExprLevA(void);
|
|
static void ExprFactor(void);
|
|
static void ExprTernary(void);
|
|
static void ConstExprFactor(void);
|
|
static void SendExprCommand(pcd_t pcd);
|
|
static void PushExStk(int value);
|
|
static int PopExStk(void);
|
|
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]);
|
|
static symbolNode_t *DemandSymbol(char *name);
|
|
static symbolNode_t *SpeculateSymbol(char *name, boolean hasReturn);
|
|
static symbolNode_t *SpeculateFunction(const char *name, boolean hasReturn);
|
|
static void UnspeculateFunction(symbolNode_t *sym);
|
|
static void AddScriptFuncRef(symbolNode_t *sym, int address, int argcount);
|
|
static void CheckForUndefinedFunctions(void);
|
|
static void SkipBraceBlock(int depth);
|
|
|
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
int pa_ScriptCount;
|
|
struct ScriptTypes *pa_TypedScriptCounts;
|
|
int pa_MapVarCount;
|
|
int pa_WorldVarCount;
|
|
int pa_GlobalVarCount;
|
|
int pa_WorldArrayCount;
|
|
int pa_GlobalArrayCount;
|
|
enum ImportModes ImportMode = IMPORT_None;
|
|
boolean ExporterFlagged;
|
|
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];
|
|
static int BreakIndex;
|
|
static continueInfo_t ContinueInfo[MAX_CONTINUE];
|
|
static int ContinueIndex;
|
|
static caseInfo_t CaseInfo[MAX_CASE];
|
|
static int CaseIndex;
|
|
static int StatementLevel;
|
|
static int ExprStack[EXPR_STACK_DEPTH];
|
|
static int ExprStackIndex;
|
|
static boolean ConstantExpression;
|
|
static symbolNode_t *InsideFunction;
|
|
static prefunc_t *FillinFunctions;
|
|
static prefunc_t **FillinFunctionsLatest = &FillinFunctions;
|
|
static boolean ArrayHasStrings;
|
|
|
|
static int AdjustStmtLevel[] =
|
|
{
|
|
0, // STMT_SCRIPT
|
|
0, // STMT_IF
|
|
0, // STMT_ELSE
|
|
1, // STMT_DO
|
|
1, // STMT_WHILEUNTIL
|
|
1, // STMT_SWITCH
|
|
1 // STMT_FOR
|
|
};
|
|
|
|
static boolean IsBreakRoot[] =
|
|
{
|
|
NO, // STMT_SCRIPT
|
|
NO, // STMT_IF
|
|
NO, // STMT_ELSE
|
|
YES, // STMT_DO
|
|
YES, // STMT_WHILEUNTIL
|
|
YES, // STMT_SWITCH
|
|
YES // STMT_FOR
|
|
};
|
|
|
|
static boolean IsContinueRoot[] =
|
|
{
|
|
NO, // STMT_SCRIPT
|
|
NO, // STMT_IF
|
|
NO, // STMT_ELSE
|
|
YES, // STMT_DO
|
|
YES, // STMT_WHILEUNTIL
|
|
NO, // STMT_SWITCH
|
|
YES // STMT_FOR
|
|
};
|
|
|
|
static tokenType_t LevAOps[] =
|
|
{
|
|
TK_TERNARY,
|
|
TK_NONE
|
|
};
|
|
|
|
static tokenType_t LevBOps[] =
|
|
{
|
|
TK_ORLOGICAL,
|
|
TK_NONE
|
|
};
|
|
|
|
static tokenType_t LevCOps[] =
|
|
{
|
|
TK_ANDLOGICAL,
|
|
TK_NONE
|
|
};
|
|
|
|
static tokenType_t LevDOps[] =
|
|
{
|
|
TK_ORBITWISE,
|
|
TK_NONE
|
|
};
|
|
|
|
static tokenType_t LevEOps[] =
|
|
{
|
|
TK_EORBITWISE,
|
|
TK_NONE
|
|
};
|
|
|
|
static tokenType_t LevFOps[] =
|
|
{
|
|
TK_ANDBITWISE,
|
|
TK_NONE
|
|
};
|
|
|
|
static tokenType_t LevGOps[] =
|
|
{
|
|
TK_EQ,
|
|
TK_NE,
|
|
TK_NONE
|
|
};
|
|
|
|
static tokenType_t LevHOps[] =
|
|
{
|
|
TK_LT,
|
|
TK_LE,
|
|
TK_GT,
|
|
TK_GE,
|
|
TK_NONE
|
|
};
|
|
|
|
static tokenType_t LevIOps[] =
|
|
{
|
|
TK_LSHIFT,
|
|
TK_RSHIFT,
|
|
TK_NONE
|
|
};
|
|
|
|
static tokenType_t LevJOps[] =
|
|
{
|
|
TK_PLUS,
|
|
TK_MINUS,
|
|
TK_NONE
|
|
};
|
|
|
|
static tokenType_t LevKOps[] =
|
|
{
|
|
TK_ASTERISK,
|
|
TK_SLASH,
|
|
TK_PERCENT,
|
|
TK_NONE
|
|
};
|
|
|
|
static tokenType_t *OpsList[] =
|
|
{
|
|
LevAOps,
|
|
LevBOps,
|
|
LevCOps,
|
|
LevDOps,
|
|
LevEOps,
|
|
LevFOps,
|
|
LevGOps,
|
|
LevHOps,
|
|
LevIOps,
|
|
LevJOps,
|
|
LevKOps,
|
|
NULL
|
|
};
|
|
|
|
static tokenType_t AssignOps[] =
|
|
{
|
|
TK_ASSIGN,
|
|
TK_ADDASSIGN,
|
|
TK_SUBASSIGN,
|
|
TK_MULASSIGN,
|
|
TK_DIVASSIGN,
|
|
TK_MODASSIGN,
|
|
TK_ANDASSIGN,
|
|
TK_EORASSIGN,
|
|
TK_ORASSIGN,
|
|
TK_LSASSIGN,
|
|
TK_RSASSIGN,
|
|
TK_NONE
|
|
};
|
|
|
|
static struct ScriptTypes ScriptCounts[] =
|
|
{
|
|
{ "closed", 0, 0 },
|
|
{ "open", OPEN_SCRIPTS_BASE, 0 },
|
|
{ "respawn", RESPAWN_SCRIPTS_BASE, 0 },
|
|
{ "death", DEATH_SCRIPTS_BASE, 0 },
|
|
{ "enter", ENTER_SCRIPTS_BASE, 0 },
|
|
{ "pickup", PICKUP_SCRIPTS_BASE, 0 },
|
|
{ "bluereturn", BLUE_RETURN_SCRIPTS_BASE, 0 },
|
|
{ "redreturn", RED_RETURN_SCRIPTS_BASE, 0 },
|
|
{ "whitereturn", WHITE_RETURN_SCRIPTS_BASE, 0 },
|
|
{ "lightning", LIGHTNING_SCRIPTS_BASE, 0 },
|
|
{ "disconnect", DISCONNECT_SCRIPTS_BASE, 0 },
|
|
{ "unloading", UNLOADING_SCRIPTS_BASE, 0 },
|
|
{ "return", RETURN_SCRIPTS_BASE, 0 },
|
|
{ "event", EVENT_SCRIPTS_BASE, 0 },
|
|
{ "kill", KILL_SCRIPTS_BASE, 0 },
|
|
{ "reopen", REOPEN_SCRIPTS_BASE, 0 },
|
|
{ NULL, -1, 0 }
|
|
};
|
|
|
|
// CODE --------------------------------------------------------------------
|
|
|
|
//==========================================================================
|
|
//
|
|
// PA_Parse
|
|
//
|
|
//==========================================================================
|
|
|
|
void PA_Parse(void)
|
|
{
|
|
int i;
|
|
|
|
pa_ScriptCount = 0;
|
|
pa_TypedScriptCounts = ScriptCounts;
|
|
for (i = 0; ScriptCounts[i].TypeName != NULL; i++)
|
|
{
|
|
ScriptCounts[i].TypeCount = 0;
|
|
}
|
|
pa_MapVarCount = 0;
|
|
pa_WorldVarCount = 0;
|
|
pa_GlobalVarCount = 0;
|
|
pa_WorldArrayCount = 0;
|
|
pa_GlobalArrayCount = 0;
|
|
TK_NextToken();
|
|
Outside();
|
|
CheckForUndefinedFunctions();
|
|
ERR_Finish();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CountScript
|
|
//
|
|
//==========================================================================
|
|
|
|
static void CountScript(int type)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; ScriptCounts[i].TypeName != NULL; i++)
|
|
{
|
|
if (ScriptCounts[i].TypeBase == type)
|
|
{
|
|
if (type != 0)
|
|
{
|
|
MS_Message(MSG_DEBUG, "Script type: %s\n",
|
|
ScriptCounts[i].TypeName);
|
|
}
|
|
ScriptCounts[i].TypeCount++;
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Outside
|
|
//
|
|
//==========================================================================
|
|
|
|
static void Outside(void)
|
|
{
|
|
boolean done;
|
|
int outertokencount;
|
|
|
|
done = NO;
|
|
outertokencount = 0;
|
|
while(done == NO)
|
|
{
|
|
outertokencount++;
|
|
switch(tk_Token)
|
|
{
|
|
case TK_EOF:
|
|
done = YES;
|
|
break;
|
|
case TK_SCRIPT:
|
|
OuterScript();
|
|
break;
|
|
case TK_FUNCTION:
|
|
OuterFunction();
|
|
break;
|
|
case TK_INT:
|
|
case TK_STR:
|
|
case TK_BOOL:
|
|
OuterMapVar(NO);
|
|
break;
|
|
case TK_WORLD:
|
|
OuterWorldVar(NO);
|
|
break;
|
|
case TK_GLOBAL:
|
|
OuterWorldVar(YES);
|
|
break;
|
|
case TK_SPECIAL:
|
|
OuterSpecialDef();
|
|
break;
|
|
case TK_NUMBERSIGN:
|
|
TK_NextToken();
|
|
switch(tk_Token)
|
|
{
|
|
case TK_DEFINE:
|
|
OuterDefine(NO);
|
|
break;
|
|
case TK_LIBDEFINE:
|
|
OuterDefine(YES);
|
|
break;
|
|
case TK_INCLUDE:
|
|
OuterInclude();
|
|
break;
|
|
case TK_NOCOMPACT:
|
|
if(ImportMode != IMPORT_Importing)
|
|
{
|
|
if(pc_Address != 8)
|
|
{
|
|
ERR_Error(ERR_NOCOMPACT_NOT_HERE, YES);
|
|
}
|
|
MS_Message(MSG_DEBUG, "Forcing NoShrink\n");
|
|
pc_NoShrink = TRUE;
|
|
}
|
|
TK_NextToken();
|
|
break;
|
|
case TK_WADAUTHOR:
|
|
if(ImportMode != IMPORT_Importing)
|
|
{
|
|
MS_Message(MSG_DEBUG, "Will write WadAuthor-compatible object\n");
|
|
MS_Message(MSG_NORMAL, "You don't need to use #wadauthor anymore.\n");
|
|
pc_WadAuthor = TRUE;
|
|
}
|
|
TK_NextToken();
|
|
break;
|
|
case TK_NOWADAUTHOR:
|
|
if(ImportMode != IMPORT_Importing)
|
|
{
|
|
MS_Message(MSG_DEBUG, "Will write WadAuthor-incompatible object\n");
|
|
pc_WadAuthor = FALSE;
|
|
}
|
|
TK_NextToken();
|
|
break;
|
|
case TK_ENCRYPTSTRINGS:
|
|
if(ImportMode != IMPORT_Importing)
|
|
{
|
|
MS_Message(MSG_DEBUG, "Strings will be encrypted\n");
|
|
pc_EncryptStrings = TRUE;
|
|
if(pc_EnforceHexen)
|
|
{
|
|
ERR_Error(ERR_HEXEN_COMPAT, YES);
|
|
}
|
|
}
|
|
TK_NextToken();
|
|
break;
|
|
case TK_IMPORT:
|
|
OuterImport();
|
|
outertokencount = 0;
|
|
break;
|
|
case TK_LIBRARY:
|
|
if (outertokencount != 1)
|
|
{
|
|
ERR_Error(ERR_LIBRARY_NOT_FIRST, YES);
|
|
}
|
|
TK_NextTokenMustBe(TK_STRING, ERR_STRING_LIT_NOT_FOUND);
|
|
if(ImportMode == IMPORT_None)
|
|
{
|
|
MS_Message(MSG_DEBUG, "Allocations modified for exporting\n");
|
|
ImportMode = IMPORT_Exporting;
|
|
if(pc_EnforceHexen)
|
|
{
|
|
ERR_Error(ERR_HEXEN_COMPAT, YES);
|
|
}
|
|
}
|
|
else if(ImportMode == IMPORT_Importing)
|
|
{
|
|
PC_AddImport(tk_String);
|
|
ExporterFlagged = YES;
|
|
}
|
|
TK_NextToken();
|
|
break;
|
|
case TK_REGION: // [mxd]
|
|
case TK_ENDREGION:
|
|
outertokencount--; // #region markers should not count as "real" tokens
|
|
TK_SkipLine();
|
|
break;
|
|
default:
|
|
ERR_Error(ERR_INVALID_DIRECTIVE, YES);
|
|
TK_SkipLine();
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
ERR_Exit(ERR_INVALID_DECLARATOR, YES, NULL);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// OuterScript
|
|
//
|
|
//==========================================================================
|
|
|
|
static void OuterScript(void)
|
|
{
|
|
int scriptNumber;
|
|
symbolNode_t *sym;
|
|
int scriptType, scriptFlags;
|
|
|
|
MS_Message(MSG_DEBUG, "---- OuterScript ----\n");
|
|
BreakIndex = 0;
|
|
CaseIndex = 0;
|
|
StatementLevel = 0;
|
|
ScriptVarCount = 0;
|
|
ScriptArrayCount = 0;
|
|
SY_FreeLocals();
|
|
TK_NextToken();
|
|
|
|
if(ImportMode == IMPORT_Importing)
|
|
{
|
|
// When importing, the script number is not recorded, because
|
|
// it might be a #define that is not included by the main .acs
|
|
// file, so processing it would generate a syntax error.
|
|
SkipBraceBlock(0);
|
|
TK_NextToken();
|
|
return;
|
|
}
|
|
|
|
// [RH] If you want to use script 0, it must be written as <<0>>.
|
|
// This is to avoid using it accidentally, since ZDoom uses script
|
|
// 0 to implement many of the Strife-specific line specials.
|
|
|
|
if(tk_Token == TK_LSHIFT)
|
|
{
|
|
TK_NextTokenMustBe(TK_NUMBER, ERR_SCRIPT_OUT_OF_RANGE);
|
|
if(tk_Number != 0)
|
|
{
|
|
ERR_Exit(ERR_SCRIPT_OUT_OF_RANGE, YES, NULL);
|
|
}
|
|
TK_NextTokenMustBe(TK_RSHIFT, ERR_SCRIPT_OUT_OF_RANGE);
|
|
TK_NextToken();
|
|
scriptNumber = 0;
|
|
}
|
|
else if(tk_Token == TK_STRING)
|
|
{ // Named scripts start counting at -1 and go down from there.
|
|
if(strcasecmp("None", tk_String) == 0)
|
|
{
|
|
ERR_Error(ERR_SCRIPT_NAMED_NONE, YES, NULL);
|
|
}
|
|
scriptNumber = -1 - STR_FindInListInsensitive(STRLIST_NAMEDSCRIPTS, tk_String);
|
|
TK_NextToken();
|
|
}
|
|
else
|
|
{
|
|
scriptNumber = EvalConstExpression();
|
|
if(scriptNumber < 1 || scriptNumber > 32767)
|
|
{
|
|
TK_Undo();
|
|
ERR_Error(ERR_SCRIPT_OUT_OF_RANGE, YES, NULL);
|
|
SkipBraceBlock(0);
|
|
TK_NextToken();
|
|
return;
|
|
}
|
|
}
|
|
if (scriptNumber >= 0)
|
|
{
|
|
MS_Message(MSG_DEBUG, "Script number: %d\n", scriptNumber);
|
|
}
|
|
else
|
|
{
|
|
MS_Message(MSG_DEBUG, "Script name: %s (%d)\n",
|
|
STR_GetString(STRLIST_NAMEDSCRIPTS, -scriptNumber - 1),
|
|
scriptNumber);
|
|
}
|
|
scriptType = 0;
|
|
scriptFlags = 0;
|
|
if(tk_Token == TK_LPAREN)
|
|
{
|
|
if(TK_NextToken() == TK_VOID)
|
|
{
|
|
TK_NextTokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
}
|
|
else
|
|
{
|
|
TK_Undo();
|
|
do
|
|
{
|
|
TK_NextTokenMustBe(TK_INT, ERR_BAD_VAR_TYPE);
|
|
TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER);
|
|
if(ScriptVarCount == 4)
|
|
{
|
|
ERR_Error(ERR_TOO_MANY_SCRIPT_ARGS, YES);
|
|
}
|
|
if(SY_FindLocal(tk_String) != NULL)
|
|
{ // Redefined
|
|
ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String);
|
|
}
|
|
else if(ScriptVarCount < 4)
|
|
{
|
|
sym = SY_InsertLocal(tk_String, SY_SCRIPTVAR);
|
|
sym->info.var.index = ScriptVarCount;
|
|
ScriptVarCount++;
|
|
}
|
|
TK_NextToken();
|
|
} while(tk_Token == TK_COMMA);
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
}
|
|
TK_NextToken();
|
|
switch(tk_Token)
|
|
{
|
|
case TK_DISCONNECT:
|
|
scriptType = DISCONNECT_SCRIPTS_BASE;
|
|
if(ScriptVarCount != 1)
|
|
{
|
|
ERR_Error(ERR_DISCONNECT_NEEDS_1_ARG, YES);
|
|
}
|
|
break;
|
|
|
|
case TK_OPEN:
|
|
case TK_RESPAWN:
|
|
case TK_DEATH:
|
|
case TK_ENTER:
|
|
case TK_PICKUP:
|
|
case TK_BLUERETURN:
|
|
case TK_REDRETURN:
|
|
case TK_WHITERETURN:
|
|
case TK_LIGHTNING:
|
|
case TK_UNLOADING:
|
|
case TK_RETURN:
|
|
case TK_KILL:
|
|
case TK_REOPEN:
|
|
ERR_Error(ERR_UNCLOSED_WITH_ARGS, YES);
|
|
break;
|
|
|
|
case TK_EVENT:
|
|
scriptType = EVENT_SCRIPTS_BASE;
|
|
if(ScriptVarCount != 3)
|
|
{
|
|
ERR_Error(ERR_EVENT_NEEDS_3_ARG, YES);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
TK_Undo();
|
|
}
|
|
MS_Message(MSG_DEBUG, "Script type: %s (%d %s)\n",
|
|
scriptType == 0 ? "closed" : "disconnect",
|
|
ScriptVarCount, ScriptVarCount == 1 ? "arg" : "args");
|
|
}
|
|
else switch (tk_Token)
|
|
{
|
|
case TK_OPEN:
|
|
scriptType = OPEN_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_RESPAWN: // [BC]
|
|
scriptType = RESPAWN_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_DEATH: // [BC]
|
|
scriptType = DEATH_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_ENTER: // [BC]
|
|
scriptType = ENTER_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_RETURN:
|
|
scriptType = RETURN_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_PICKUP: // [BC]
|
|
scriptType = PICKUP_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_BLUERETURN: // [BC]
|
|
scriptType = BLUE_RETURN_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_REDRETURN: // [BC]
|
|
scriptType = RED_RETURN_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_WHITERETURN: // [BC]
|
|
scriptType = WHITE_RETURN_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_LIGHTNING:
|
|
scriptType = LIGHTNING_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_UNLOADING:
|
|
scriptType = UNLOADING_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_DISCONNECT:
|
|
scriptType = DISCONNECT_SCRIPTS_BASE;
|
|
ERR_Error (ERR_DISCONNECT_NEEDS_1_ARG, YES);
|
|
break;
|
|
|
|
case TK_EVENT: // [BB]
|
|
scriptType = EVENT_SCRIPTS_BASE;
|
|
ERR_Error (ERR_EVENT_NEEDS_3_ARG, YES);
|
|
break;
|
|
|
|
case TK_KILL: // [JM]
|
|
scriptType = KILL_SCRIPTS_BASE;
|
|
break;
|
|
|
|
case TK_REOPEN: // [Nash]
|
|
scriptType = REOPEN_SCRIPTS_BASE;
|
|
break;
|
|
|
|
default:
|
|
ERR_Error(ERR_BAD_SCRIPT_DECL, YES);
|
|
SkipBraceBlock(0);
|
|
TK_NextToken();
|
|
return;
|
|
}
|
|
TK_NextToken();
|
|
if(tk_Token == TK_NET)
|
|
{
|
|
scriptFlags |= NET_SCRIPT_FLAG;
|
|
TK_NextToken();
|
|
}
|
|
// [BB] If NET and CLIENTSIDE are specified, this construction can only parse
|
|
// "NET CLIENTSIDE" but not "CLIENTSIDE NET".
|
|
if(tk_Token == TK_CLIENTSIDE)
|
|
{
|
|
scriptFlags |= CLIENTSIDE_SCRIPT_FLAG;
|
|
TK_NextToken();
|
|
}
|
|
CountScript(scriptType);
|
|
PC_AddScript(scriptNumber, scriptType, scriptFlags, ScriptVarCount);
|
|
pc_LastAppendedCommand = PCD_NOP;
|
|
if(ProcessStatement(STMT_SCRIPT) == NO)
|
|
{
|
|
ERR_Error(ERR_INVALID_STATEMENT, YES);
|
|
}
|
|
if(pc_LastAppendedCommand != PCD_TERMINATE)
|
|
{
|
|
PC_AppendCmd(PCD_TERMINATE);
|
|
}
|
|
PC_SetScriptVarCount(scriptNumber, scriptType, ScriptVarCount, ScriptArrayCount, ScriptArraySize);
|
|
pa_ScriptCount++;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// OuterFunction
|
|
//
|
|
//==========================================================================
|
|
|
|
static void OuterFunction(void)
|
|
{
|
|
enum ImportModes importing;
|
|
boolean hasReturn;
|
|
symbolNode_t *sym;
|
|
int defLine;
|
|
|
|
MS_Message(MSG_DEBUG, "---- OuterFunction ----\n");
|
|
importing = ImportMode;
|
|
BreakIndex = 0;
|
|
CaseIndex = 0;
|
|
StatementLevel = 0;
|
|
ScriptVarCount = 0;
|
|
ScriptArrayCount = 0;
|
|
SY_FreeLocals();
|
|
TK_NextToken();
|
|
if(tk_Token != TK_STR && tk_Token != TK_INT &&
|
|
tk_Token != TK_VOID && tk_Token != TK_BOOL)
|
|
{
|
|
ERR_Error(ERR_BAD_RETURN_TYPE, YES);
|
|
tk_Token = TK_VOID;
|
|
}
|
|
hasReturn = tk_Token != TK_VOID;
|
|
TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER);
|
|
sym = SY_FindGlobal(tk_String);
|
|
if(sym != NULL)
|
|
{
|
|
if(sym->type != SY_SCRIPTFUNC)
|
|
{ // Redefined
|
|
ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String);
|
|
SkipBraceBlock(0);
|
|
TK_NextToken();
|
|
return;
|
|
}
|
|
if(!sym->info.scriptFunc.predefined)
|
|
{
|
|
ERR_Error(ERR_FUNCTION_ALREADY_DEFINED, YES);
|
|
ERR_ErrorAt(sym->info.scriptFunc.sourceName, sym->info.scriptFunc.sourceLine);
|
|
ERR_Error(ERR_NONE, YES, "Previous definition was here.");
|
|
SkipBraceBlock(0);
|
|
TK_NextToken();
|
|
return;
|
|
}
|
|
if(sym->info.scriptFunc.hasReturnValue && !hasReturn)
|
|
{
|
|
ERR_Error(ERR_PREVIOUS_NOT_VOID, YES);
|
|
ERR_ErrorAt(sym->info.scriptFunc.sourceName, sym->info.scriptFunc.sourceLine);
|
|
ERR_Error(ERR_NONE, YES, "Previous use was here.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sym = SY_InsertGlobal(tk_String, SY_SCRIPTFUNC);
|
|
if (importing == IMPORT_Importing)
|
|
{
|
|
sym->info.scriptFunc.address = 0;
|
|
sym->info.scriptFunc.predefined = NO;
|
|
}
|
|
else
|
|
{
|
|
sym->info.scriptFunc.address = pc_Address;
|
|
sym->info.scriptFunc.predefined = YES;
|
|
// only for consistency with other speculated functions and pretty logs
|
|
sym->info.scriptFunc.funcNumber = 0;
|
|
}
|
|
}
|
|
defLine = tk_Line;
|
|
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
if(TK_NextToken() == TK_VOID)
|
|
{
|
|
TK_NextTokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
}
|
|
else
|
|
{
|
|
TK_Undo();
|
|
do
|
|
{
|
|
symbolType_t type;
|
|
symbolNode_t *local;
|
|
|
|
TK_NextToken();
|
|
/* if(tk_Token != TK_INT && tk_Token != TK_STR && tk_Token != TK_BOOL)
|
|
{
|
|
ERR_Error(ERR_BAD_VAR_TYPE, YES);
|
|
tk_Token = TK_INT;
|
|
}
|
|
*/ if(tk_Token == TK_INT || tk_Token == TK_BOOL)
|
|
{
|
|
if(TK_NextToken() == TK_LBRACKET)
|
|
{
|
|
TK_NextTokenMustBe(TK_RBRACKET, ERR_BAD_VAR_TYPE);
|
|
type = SY_SCRIPTALIAS;
|
|
}
|
|
else
|
|
{
|
|
TK_Undo();
|
|
type = SY_SCRIPTVAR;
|
|
}
|
|
}
|
|
else if(tk_Token == TK_STR)
|
|
{
|
|
type = SY_SCRIPTVAR;
|
|
}
|
|
else
|
|
{
|
|
type = SY_SCRIPTVAR;
|
|
ERR_Error(ERR_BAD_VAR_TYPE, YES);
|
|
tk_Token = TK_INT;
|
|
}
|
|
TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER);
|
|
if(SY_FindLocal(tk_String) != NULL)
|
|
{ // Redefined
|
|
ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String);
|
|
}
|
|
else
|
|
{
|
|
local = SY_InsertLocal(tk_String, type);
|
|
local->info.var.index = ScriptVarCount;
|
|
ScriptVarCount++;
|
|
}
|
|
TK_NextToken();
|
|
} while(tk_Token == TK_COMMA);
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
}
|
|
if(pc_EnforceHexen)
|
|
{
|
|
ERR_Error(ERR_HEXEN_COMPAT, YES);
|
|
}
|
|
|
|
sym->info.scriptFunc.sourceLine = defLine;
|
|
sym->info.scriptFunc.sourceName = tk_SourceName;
|
|
sym->info.scriptFunc.argCount = ScriptVarCount;
|
|
sym->info.scriptFunc.address = (importing == IMPORT_Importing) ? 0 : pc_Address;
|
|
sym->info.scriptFunc.hasReturnValue = hasReturn;
|
|
|
|
if(importing == IMPORT_Importing)
|
|
{
|
|
SkipBraceBlock(0);
|
|
TK_NextToken();
|
|
sym->info.scriptFunc.predefined = NO;
|
|
sym->info.scriptFunc.varCount = ScriptVarCount;
|
|
return;
|
|
}
|
|
|
|
TK_NextToken();
|
|
InsideFunction = sym;
|
|
pc_LastAppendedCommand = PCD_NOP;
|
|
|
|
// If we just call ProcessStatement(STMT_SCRIPT), and this function
|
|
// needs to return a value but the last pcode output was not a return,
|
|
// then the line number given in the error can be confusing because it
|
|
// is beyond the end of the function. To avoid this, we process the
|
|
// compound statement ourself and check if it returned something
|
|
// before checking for the '}'. If a return is required, then the error
|
|
// line will be shown as the one that contains the '}' (if present).
|
|
|
|
TK_TokenMustBe(TK_LBRACE, ERR_MISSING_LBRACE);
|
|
TK_NextToken();
|
|
do {} while(ProcessStatement(STMT_SCRIPT) == YES);
|
|
|
|
if(pc_LastAppendedCommand != PCD_RETURNVOID &&
|
|
pc_LastAppendedCommand != PCD_RETURNVAL)
|
|
{
|
|
if(hasReturn)
|
|
{
|
|
TK_Undo();
|
|
ERR_Error(ERR_MUST_RETURN_A_VALUE, YES, NULL);
|
|
}
|
|
PC_AppendCmd(PCD_RETURNVOID);
|
|
}
|
|
|
|
TK_TokenMustBe(TK_RBRACE, ERR_INVALID_STATEMENT);
|
|
TK_NextToken();
|
|
|
|
sym->info.scriptFunc.predefined = NO;
|
|
sym->info.scriptFunc.varCount = ScriptVarCount -
|
|
sym->info.scriptFunc.argCount;
|
|
PC_AddFunction(sym, ScriptArrayCount, ScriptArraySize);
|
|
UnspeculateFunction(sym);
|
|
InsideFunction = NULL;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// OuterMapVar
|
|
//
|
|
//==========================================================================
|
|
|
|
static void OuterMapVar(boolean local)
|
|
{
|
|
symbolNode_t *sym = NULL;
|
|
int index;
|
|
|
|
MS_Message(MSG_DEBUG, "---- %s ----\n", local ? "LeadingStaticVarDeclare" : "OuterMapVar");
|
|
do
|
|
{
|
|
if(pa_MapVarCount >= MAX_MAP_VARIABLES)
|
|
{
|
|
ERR_Error(ERR_TOO_MANY_MAP_VARS, YES);
|
|
index = MAX_MAP_VARIABLES;
|
|
}
|
|
TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER);
|
|
sym = local ? SY_FindLocal(tk_String)
|
|
: SY_FindGlobal(tk_String);
|
|
if(sym != NULL)
|
|
{ // Redefined
|
|
ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String);
|
|
index = MAX_MAP_VARIABLES;
|
|
}
|
|
else
|
|
{
|
|
sym = local ? SY_InsertLocal(tk_String, SY_MAPVAR)
|
|
: SY_InsertGlobal(tk_String, SY_MAPVAR);
|
|
if(ImportMode == IMPORT_Importing)
|
|
{
|
|
sym->info.var.index = index = 0;
|
|
}
|
|
else
|
|
{
|
|
sym->info.var.index = index = pa_MapVarCount;
|
|
if (!local)
|
|
{ // Local variables are not exported
|
|
PC_NameMapVariable(index, sym);
|
|
}
|
|
pa_MapVarCount++;
|
|
}
|
|
}
|
|
TK_NextToken();
|
|
if(tk_Token == TK_ASSIGN)
|
|
{
|
|
if(ImportMode != IMPORT_Importing)
|
|
{
|
|
TK_NextToken();
|
|
PC_PutMapVariable (index, EvalConstExpression());
|
|
}
|
|
else
|
|
{
|
|
// When importing, skip the initializer, because we don't care.
|
|
do
|
|
{
|
|
TK_NextToken();
|
|
} while(tk_Token != TK_COMMA && tk_Token != TK_SEMICOLON);
|
|
}
|
|
}
|
|
else if(tk_Token == TK_LBRACKET)
|
|
{
|
|
int size, ndim, dims[MAX_ARRAY_DIMS];
|
|
|
|
ParseArrayDims(&size, &ndim, dims);
|
|
|
|
if(sym != NULL)
|
|
{
|
|
if(ImportMode != IMPORT_Importing)
|
|
{
|
|
PC_AddArray(index, size);
|
|
}
|
|
SymToArray(SY_MAPARRAY, sym, index, size, ndim, dims);
|
|
if(tk_Token == TK_ASSIGN)
|
|
{
|
|
InitializeArray(sym, dims, size);
|
|
}
|
|
}
|
|
}
|
|
} while(tk_Token == TK_COMMA);
|
|
TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// OuterWorldVar
|
|
//
|
|
//==========================================================================
|
|
|
|
static void OuterWorldVar(boolean isGlobal)
|
|
{
|
|
int index;
|
|
symbolNode_t *sym;
|
|
|
|
MS_Message(MSG_DEBUG, "---- Outer%sVar ----\n", isGlobal ? "Global" : "World");
|
|
if(TK_NextToken() != TK_INT)
|
|
{
|
|
if(tk_Token != TK_BOOL)
|
|
{
|
|
TK_TokenMustBe(TK_STR, ERR_BAD_VAR_TYPE);
|
|
}
|
|
}
|
|
do
|
|
{
|
|
TK_NextTokenMustBe(TK_NUMBER, ERR_MISSING_WVAR_INDEX);
|
|
if(tk_Number >= (isGlobal ? MAX_GLOBAL_VARIABLES : MAX_WORLD_VARIABLES))
|
|
{
|
|
ERR_Error(ERR_BAD_WVAR_INDEX+isGlobal, YES);
|
|
index = 0;
|
|
}
|
|
else
|
|
{
|
|
index = tk_Number;
|
|
}
|
|
TK_NextTokenMustBe(TK_COLON, ERR_MISSING_WVAR_COLON+isGlobal);
|
|
TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER);
|
|
if(SY_FindGlobal(tk_String) != NULL)
|
|
{ // Redefined
|
|
ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String);
|
|
}
|
|
else
|
|
{
|
|
TK_NextToken();
|
|
if(tk_Token == TK_LBRACKET)
|
|
{
|
|
TK_NextToken ();
|
|
if(tk_Token != TK_RBRACKET)
|
|
{
|
|
ERR_Error(ERR_NO_NEED_ARRAY_SIZE, YES);
|
|
TK_SkipPast(TK_RBRACKET);
|
|
}
|
|
else
|
|
{
|
|
TK_NextToken();
|
|
}
|
|
if(tk_Token == TK_LBRACKET)
|
|
{
|
|
ERR_Error(ERR_NO_MULTIDIMENSIONS, YES);
|
|
do
|
|
{
|
|
TK_SkipPast(TK_RBRACKET);
|
|
}
|
|
while(tk_Token == TK_LBRACKET);
|
|
}
|
|
sym = SY_InsertGlobal(tk_String, isGlobal ? SY_GLOBALARRAY : SY_WORLDARRAY);
|
|
sym->info.array.index = index;
|
|
sym->info.array.ndim = 1;
|
|
sym->info.array.size = 0x7fffffff; // not used
|
|
memset(sym->info.array.dimensions, 0, sizeof(sym->info.array.dimensions));
|
|
|
|
if (isGlobal)
|
|
pa_GlobalArrayCount++;
|
|
else
|
|
pa_WorldArrayCount++;
|
|
}
|
|
else
|
|
{
|
|
sym = SY_InsertGlobal(tk_String, isGlobal ? SY_GLOBALVAR : SY_WORLDVAR);
|
|
sym->info.var.index = index;
|
|
if (isGlobal)
|
|
pa_GlobalVarCount++;
|
|
else
|
|
pa_WorldVarCount++;
|
|
}
|
|
}
|
|
} while(tk_Token == TK_COMMA);
|
|
TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// OuterSpecialDef
|
|
//
|
|
//==========================================================================
|
|
|
|
static void OuterSpecialDef(void)
|
|
{
|
|
int special;
|
|
symbolNode_t *sym;
|
|
|
|
MS_Message(MSG_DEBUG, "---- OuterSpecialDef ----\n");
|
|
if(ImportMode == IMPORT_Importing)
|
|
{
|
|
// No need to process special definitions when importing.
|
|
TK_SkipPast(TK_SEMICOLON);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (TK_NextToken() == TK_MINUS)
|
|
{
|
|
TK_NextTokenMustBe(TK_NUMBER, ERR_MISSING_SPEC_VAL);
|
|
special = -tk_Number;
|
|
}
|
|
else
|
|
{
|
|
TK_TokenMustBe(TK_NUMBER, ERR_MISSING_SPEC_VAL);
|
|
special = tk_Number;
|
|
}
|
|
TK_NextTokenMustBe(TK_COLON, ERR_MISSING_SPEC_COLON);
|
|
TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER);
|
|
sym = SY_InsertGlobalUnique(tk_String, SY_SPECIAL);
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
TK_NextTokenMustBe(TK_NUMBER, ERR_MISSING_SPEC_ARGC);
|
|
sym->info.special.value = special;
|
|
sym->info.special.argCount = tk_Number | (tk_Number << 16);
|
|
TK_NextToken();
|
|
if(tk_Token == TK_COMMA)
|
|
{ // Get maximum arg count
|
|
TK_NextTokenMustBe(TK_NUMBER, ERR_MISSING_SPEC_ARGC);
|
|
sym->info.special.argCount =
|
|
(sym->info.special.argCount & 0xffff) | (tk_Number << 16);
|
|
}
|
|
else
|
|
{
|
|
TK_Undo ();
|
|
}
|
|
TK_NextTokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
TK_NextToken();
|
|
} while(tk_Token == TK_COMMA);
|
|
TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// OuterDefine
|
|
//
|
|
//==========================================================================
|
|
|
|
static void OuterDefine(boolean force)
|
|
{
|
|
int value;
|
|
symbolNode_t *sym;
|
|
|
|
MS_Message(MSG_DEBUG, "---- OuterDefine %s----\n",
|
|
force ? "(forced) " : "");
|
|
TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER);
|
|
sym = SY_InsertGlobalUnique(tk_String, SY_CONSTANT);
|
|
// Defines inside an import are deleted when the import is popped.
|
|
if(ImportMode != IMPORT_Importing || force)
|
|
{
|
|
sym->info.constant.fileDepth = 0;
|
|
}
|
|
else
|
|
{
|
|
sym->info.constant.fileDepth = TK_GetDepth();
|
|
}
|
|
|
|
TK_NextToken();
|
|
value = EvalConstExpression();
|
|
MS_Message(MSG_DEBUG, "Constant value: %d\n", value);
|
|
sym->info.constant.value = value;
|
|
sym->info.constant.strValue = pa_ConstExprIsString ? strdup(STR_Get(value)) : NULL;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// OuterInclude
|
|
//
|
|
//==========================================================================
|
|
|
|
static void OuterInclude(void)
|
|
{
|
|
// Don't include inside an import
|
|
if(ImportMode != IMPORT_Importing)
|
|
{
|
|
MS_Message(MSG_DEBUG, "---- OuterInclude ----\n");
|
|
TK_NextTokenMustBe(TK_STRING, ERR_STRING_LIT_NOT_FOUND);
|
|
TK_Include(tk_String);
|
|
}
|
|
else
|
|
{
|
|
TK_NextToken();
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// OuterImport
|
|
//
|
|
//==========================================================================
|
|
|
|
static void OuterImport(void)
|
|
{
|
|
|
|
MS_Message(MSG_DEBUG, "---- OuterImport ----\n");
|
|
if(ImportMode == IMPORT_Importing)
|
|
{
|
|
// Don't import inside an import
|
|
TK_NextToken();
|
|
}
|
|
else
|
|
{
|
|
MS_Message(MSG_DEBUG, "Importing a file\n");
|
|
TK_NextTokenMustBe(TK_STRING, ERR_STRING_LIT_NOT_FOUND);
|
|
TK_Import(tk_String, ImportMode);
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ProcessStatement
|
|
//
|
|
//==========================================================================
|
|
|
|
static boolean ProcessStatement(statement_t owner)
|
|
{
|
|
if(StatementIndex == MAX_STATEMENT_DEPTH)
|
|
{
|
|
ERR_Exit(ERR_STATEMENT_OVERFLOW, YES);
|
|
}
|
|
StatementHistory[StatementIndex++] = owner;
|
|
StatementLevel += AdjustStmtLevel[owner];
|
|
switch(tk_Token)
|
|
{
|
|
case TK_INT:
|
|
case TK_STR:
|
|
case TK_BOOL:
|
|
case TK_STATIC:
|
|
LeadingVarDeclare();
|
|
break;
|
|
case TK_LINESPECIAL:
|
|
if (tk_SpecialValue >= 0)
|
|
{
|
|
LeadingLineSpecial(NO);
|
|
}
|
|
else
|
|
{
|
|
LeadingFunction(NO);
|
|
}
|
|
break;
|
|
case TK_ACSEXECUTEWAIT:
|
|
if(InsideFunction)
|
|
{
|
|
ERR_Error(ERR_LATENT_IN_FUNC, YES);
|
|
}
|
|
tk_SpecialArgCount = 1 | (5<<16);
|
|
tk_SpecialValue = 80;
|
|
LeadingLineSpecial(YES);
|
|
break;
|
|
case TK_ACSNAMEDEXECUTEWAIT:
|
|
if(InsideFunction)
|
|
{
|
|
ERR_Error(ERR_LATENT_IN_FUNC, YES);
|
|
}
|
|
tk_SpecialArgCount = 1 | (5<<16);
|
|
tk_SpecialValue = -39;
|
|
LeadingFunction(YES);
|
|
break;
|
|
case TK_RESTART:
|
|
LeadingRestart();
|
|
break;
|
|
case TK_SUSPEND:
|
|
LeadingSuspend();
|
|
break;
|
|
case TK_TERMINATE:
|
|
LeadingTerminate();
|
|
break;
|
|
case TK_RETURN:
|
|
LeadingReturn();
|
|
break;
|
|
case TK_IDENTIFIER:
|
|
LeadingIdentifier();
|
|
break;
|
|
case TK_PRINT:
|
|
case TK_PRINTBOLD:
|
|
case TK_LOG:
|
|
LeadingPrint();
|
|
break;
|
|
|
|
case TK_STRPARAM_EVAL:
|
|
LeadingPrint();
|
|
PC_AppendCmd(PCD_DROP);
|
|
/* Duplicate code: LeadingPrint() post-processing: */
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
break;
|
|
|
|
case TK_STRCPY:
|
|
LeadingStrcpy();
|
|
PC_AppendCmd(PCD_DROP);
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
break;
|
|
|
|
case TK_HUDMESSAGE:
|
|
case TK_HUDMESSAGEBOLD:
|
|
LeadingHudMessage();
|
|
break;
|
|
case TK_MORPHACTOR:
|
|
LeadingMorphActor();
|
|
PC_AppendCmd(PCD_DROP);
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
break;
|
|
|
|
case TK_LUMPREADARRAY:
|
|
LeadingLumpReadArray();
|
|
PC_AppendCmd(PCD_DROP);
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
break;
|
|
|
|
case TK_IF:
|
|
LeadingIf();
|
|
break;
|
|
case TK_FOR:
|
|
LeadingFor();
|
|
break;
|
|
case TK_WHILE:
|
|
case TK_UNTIL:
|
|
LeadingWhileUntil();
|
|
break;
|
|
case TK_DO:
|
|
LeadingDo();
|
|
break;
|
|
case TK_SWITCH:
|
|
LeadingSwitch();
|
|
break;
|
|
case TK_CASE:
|
|
if(owner != STMT_SWITCH)
|
|
{
|
|
ERR_Error(ERR_CASE_NOT_IN_SWITCH, YES);
|
|
TK_SkipPast(TK_COLON);
|
|
}
|
|
else
|
|
{
|
|
LeadingCase();
|
|
}
|
|
break;
|
|
case TK_DEFAULT:
|
|
if(owner != STMT_SWITCH)
|
|
{
|
|
ERR_Error(ERR_DEFAULT_NOT_IN_SWITCH, YES);
|
|
TK_SkipPast(TK_COLON);
|
|
}
|
|
else if(DefaultInCurrent() == YES)
|
|
{
|
|
ERR_Error(ERR_MULTIPLE_DEFAULT, YES);
|
|
TK_SkipPast(TK_COLON);
|
|
}
|
|
else
|
|
{
|
|
LeadingDefault();
|
|
}
|
|
break;
|
|
case TK_BREAK:
|
|
if(BreakAncestor() == NO)
|
|
{
|
|
ERR_Error(ERR_MISPLACED_BREAK, YES);
|
|
TK_SkipPast(TK_SEMICOLON);
|
|
}
|
|
else
|
|
{
|
|
LeadingBreak();
|
|
}
|
|
break;
|
|
case TK_CONTINUE:
|
|
if(ContinueAncestor() == NO)
|
|
{
|
|
ERR_Error(ERR_MISPLACED_CONTINUE, YES);
|
|
TK_SkipPast(TK_SEMICOLON);
|
|
}
|
|
else
|
|
{
|
|
LeadingContinue();
|
|
}
|
|
break;
|
|
case TK_CREATETRANSLATION:
|
|
LeadingCreateTranslation();
|
|
break;
|
|
case TK_LBRACE:
|
|
LeadingCompoundStatement(owner);
|
|
break;
|
|
case TK_SEMICOLON:
|
|
TK_NextToken();
|
|
break;
|
|
case TK_INC:
|
|
case TK_DEC:
|
|
LeadingIncDec(tk_Token);
|
|
break;
|
|
|
|
default:
|
|
StatementIndex--;
|
|
StatementLevel -= AdjustStmtLevel[owner];
|
|
return NO;
|
|
break;
|
|
}
|
|
StatementIndex--;
|
|
StatementLevel -= AdjustStmtLevel[owner];
|
|
return YES;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingCompoundStatement
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingCompoundStatement(statement_t owner)
|
|
{
|
|
//StatementLevel += AdjustStmtLevel[owner];
|
|
TK_NextToken(); // Eat the TK_LBRACE
|
|
do {} while(ProcessStatement(owner) == YES);
|
|
TK_TokenMustBe(TK_RBRACE, ERR_INVALID_STATEMENT);
|
|
TK_NextToken();
|
|
//StatementLevel -= AdjustStmtLevel[owner];
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingVarDeclare
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingVarDeclare(void)
|
|
{
|
|
symbolNode_t *sym = NULL;
|
|
|
|
if(tk_Token == TK_STATIC)
|
|
{
|
|
TK_NextToken();
|
|
if(tk_Token != TK_INT && tk_Token != TK_STR && tk_Token != TK_BOOL)
|
|
{
|
|
ERR_Error(ERR_BAD_VAR_TYPE, YES);
|
|
TK_Undo();
|
|
}
|
|
OuterMapVar(YES);
|
|
return;
|
|
}
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingVarDeclare ----\n");
|
|
do
|
|
{
|
|
TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER);
|
|
#if 0
|
|
if(ScriptVarCount == MAX_SCRIPT_VARIABLES)
|
|
{
|
|
ERR_Error(InsideFunction
|
|
? ERR_TOO_MANY_FUNCTION_VARS
|
|
: ERR_TOO_MANY_SCRIPT_VARS,
|
|
YES, NULL);
|
|
ScriptVarCount++;
|
|
}
|
|
else
|
|
#endif
|
|
if(SY_FindLocal(tk_String) != NULL)
|
|
{ // Redefined
|
|
ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String);
|
|
}
|
|
else
|
|
{
|
|
sym = SY_InsertLocal(tk_String, SY_SCRIPTVAR);
|
|
sym->info.var.index = ScriptVarCount;
|
|
ScriptVarCount++;
|
|
}
|
|
TK_NextToken();
|
|
if(tk_Token == TK_LBRACKET)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
else if(tk_Token == TK_ASSIGN)
|
|
{
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
if(sym != NULL)
|
|
{
|
|
PC_AppendCmd(PCD_ASSIGNSCRIPTVAR);
|
|
PC_AppendShrink(sym->info.var.index);
|
|
}
|
|
}
|
|
} while(tk_Token == TK_COMMA);
|
|
TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingLineSpecial
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingLineSpecial(boolean executewait)
|
|
{
|
|
int i;
|
|
int argCount;
|
|
int argCountMin;
|
|
int argCountMax;
|
|
int argSave[8];
|
|
U_INT specialValue;
|
|
boolean direct;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingLineSpecial ----\n");
|
|
argCountMin = tk_SpecialArgCount & 0xffff;
|
|
argCountMax = tk_SpecialArgCount >> 16;
|
|
specialValue = tk_SpecialValue;
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
i = 0;
|
|
if(argCountMax > 0)
|
|
{
|
|
if(TK_NextToken() == TK_CONST)
|
|
{
|
|
TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
direct = YES;
|
|
}
|
|
else
|
|
{
|
|
TK_Undo();
|
|
direct = NO;
|
|
}
|
|
do
|
|
{
|
|
if(i == argCountMax)
|
|
{
|
|
ERR_Error(ERR_BAD_LSPEC_ARG_COUNT, YES);
|
|
i = argCountMax+1;
|
|
}
|
|
TK_NextToken();
|
|
if(direct == YES)
|
|
{
|
|
argSave[i] = EvalConstExpression();
|
|
}
|
|
else
|
|
{
|
|
EvalExpression();
|
|
if (i == 0 && executewait)
|
|
{
|
|
PC_AppendCmd(PCD_DUP);
|
|
}
|
|
}
|
|
if(i < argCountMax)
|
|
{
|
|
i++;
|
|
}
|
|
} while(tk_Token == TK_COMMA);
|
|
if(i < argCountMin)
|
|
{
|
|
ERR_Error(ERR_BAD_LSPEC_ARG_COUNT, YES);
|
|
TK_SkipPast(TK_SEMICOLON);
|
|
return;
|
|
}
|
|
argCount = i;
|
|
}
|
|
else
|
|
{
|
|
// [RH] I added some zero-argument specials without realizing that
|
|
// ACS won't allow for less than one, so fake them as one-argument
|
|
// specials with a parameter of 0.
|
|
argCount = 1;
|
|
direct = YES;
|
|
argSave[0] = 0;
|
|
TK_NextToken ();
|
|
}
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
if (specialValue > 255)
|
|
{
|
|
for(; argCount < 5; ++argCount)
|
|
{
|
|
PC_AppendPushVal(0);
|
|
}
|
|
PC_AppendCmd(PCD_LSPEC5EX);
|
|
PC_AppendInt(specialValue);
|
|
if(executewait)
|
|
{
|
|
PC_AppendCmd(PCD_SCRIPTWAITDIRECT);
|
|
PC_AppendInt(argSave[0]);
|
|
}
|
|
}
|
|
else if(direct == NO)
|
|
{
|
|
PC_AppendCmd(PCD_LSPEC1+(argCount-1));
|
|
if(pc_NoShrink)
|
|
{
|
|
PC_AppendInt(specialValue);
|
|
}
|
|
else
|
|
{
|
|
if (specialValue > 255)
|
|
{
|
|
ERR_Error(ERR_SPECIAL_RANGE, YES);
|
|
}
|
|
PC_AppendByte((U_BYTE)specialValue);
|
|
}
|
|
if(executewait)
|
|
{
|
|
PC_AppendCmd(PCD_SCRIPTWAIT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
boolean useintform;
|
|
|
|
if(pc_NoShrink)
|
|
{
|
|
PC_AppendCmd(PCD_LSPEC1DIRECT+(argCount-1));
|
|
PC_AppendInt(specialValue);
|
|
useintform = YES;
|
|
}
|
|
else
|
|
{
|
|
useintform = NO;
|
|
for (i = 0; i < argCount; i++)
|
|
{
|
|
if ((U_INT)argSave[i] > 255)
|
|
{
|
|
useintform = YES;
|
|
break;
|
|
}
|
|
}
|
|
PC_AppendCmd((argCount-1)+(useintform?PCD_LSPEC1DIRECT:PCD_LSPEC1DIRECTB));
|
|
PC_AppendByte((U_BYTE)specialValue);
|
|
}
|
|
if (useintform)
|
|
{
|
|
for (i = 0; i < argCount; i++)
|
|
{
|
|
PC_AppendInt(argSave[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < argCount; i++)
|
|
{
|
|
PC_AppendByte((U_BYTE)argSave[i]);
|
|
}
|
|
}
|
|
if(executewait)
|
|
{
|
|
PC_AppendCmd(PCD_SCRIPTWAITDIRECT);
|
|
PC_AppendInt(argSave[0]);
|
|
}
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingFunction
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingFunction(boolean executewait)
|
|
{
|
|
int i;
|
|
int argCount;
|
|
int argCountMin;
|
|
int argCountMax;
|
|
int specialValue;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingFunction ----\n");
|
|
argCountMin = tk_SpecialArgCount & 0xffff;
|
|
argCountMax = tk_SpecialArgCount >> 16;
|
|
specialValue = -tk_SpecialValue;
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
i = 0;
|
|
if(argCountMax > 0)
|
|
{
|
|
if(TK_NextToken() == TK_CONST)
|
|
{
|
|
// Just skip const declarators
|
|
TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
}
|
|
else
|
|
{
|
|
TK_Undo();
|
|
}
|
|
do
|
|
{
|
|
if(i == argCountMax)
|
|
{
|
|
ERR_Error(ERR_BAD_ARG_COUNT, YES);
|
|
i = argCountMax+1;
|
|
}
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
if (i == 0 && executewait)
|
|
{
|
|
PC_AppendCmd(PCD_DUP);
|
|
}
|
|
if(i < argCountMax)
|
|
{
|
|
i++;
|
|
}
|
|
} while(tk_Token == TK_COMMA);
|
|
if(i < argCountMin)
|
|
{
|
|
ERR_Error(ERR_BAD_ARG_COUNT, YES);
|
|
TK_SkipPast(TK_SEMICOLON);
|
|
return;
|
|
}
|
|
argCount = i;
|
|
}
|
|
else
|
|
{
|
|
argCount = 0;
|
|
TK_NextToken ();
|
|
}
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
PC_AppendCmd(PCD_CALLFUNC);
|
|
if(pc_NoShrink)
|
|
{
|
|
PC_AppendInt(argCount);
|
|
PC_AppendInt(specialValue);
|
|
}
|
|
else
|
|
{
|
|
PC_AppendByte((U_BYTE)argCount);
|
|
PC_AppendWord((U_WORD)specialValue);
|
|
}
|
|
PC_AppendCmd(PCD_DROP);
|
|
if(executewait)
|
|
{
|
|
PC_AppendCmd(PCD_SCRIPTWAITNAMED);
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingIdentifier
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingIdentifier(void)
|
|
{
|
|
symbolNode_t *sym;
|
|
|
|
sym = SpeculateSymbol(tk_String, NO);
|
|
switch(sym->type)
|
|
{
|
|
case SY_MAPARRAY:
|
|
case SY_SCRIPTVAR:
|
|
case SY_SCRIPTALIAS:
|
|
case SY_MAPVAR:
|
|
case SY_WORLDVAR:
|
|
case SY_GLOBALVAR:
|
|
case SY_SCRIPTARRAY:
|
|
case SY_WORLDARRAY:
|
|
case SY_GLOBALARRAY:
|
|
LeadingVarAssign(sym);
|
|
break;
|
|
case SY_INTERNFUNC:
|
|
LeadingInternFunc(sym);
|
|
break;
|
|
case SY_SCRIPTFUNC:
|
|
LeadingScriptFunc(sym);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingInternFunc
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingInternFunc(symbolNode_t *sym)
|
|
{
|
|
if(InsideFunction && sym->info.internFunc.latent)
|
|
{
|
|
ERR_Error(ERR_LATENT_IN_FUNC, YES);
|
|
}
|
|
ProcessInternFunc(sym);
|
|
if(sym->info.internFunc.hasReturnValue == YES)
|
|
{
|
|
PC_AppendCmd(PCD_DROP);
|
|
}
|
|
TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ProcessInternFunc
|
|
//
|
|
//==========================================================================
|
|
|
|
static void ProcessInternFunc(symbolNode_t *sym)
|
|
{
|
|
int i;
|
|
int argCount;
|
|
int optMask;
|
|
int outMask;
|
|
boolean direct;
|
|
boolean specialDirect;
|
|
int argSave[8];
|
|
|
|
MS_Message(MSG_DEBUG, "---- ProcessInternFunc ----\n");
|
|
argCount = sym->info.internFunc.argCount;
|
|
optMask = sym->info.internFunc.optMask;
|
|
outMask = sym->info.internFunc.outMask;
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
if(TK_NextToken() == TK_CONST)
|
|
{
|
|
TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
if(sym->info.internFunc.directCommand == PCD_NOP)
|
|
{
|
|
ERR_Error(ERR_NO_DIRECT_VER, YES, NULL);
|
|
direct = NO;
|
|
specialDirect = NO;
|
|
}
|
|
else
|
|
{
|
|
direct = YES;
|
|
if (pc_NoShrink || argCount > 2 ||
|
|
(sym->info.internFunc.directCommand != PCD_DELAYDIRECT &&
|
|
sym->info.internFunc.directCommand != PCD_RANDOMDIRECT))
|
|
{
|
|
specialDirect = NO;
|
|
PC_AppendCmd(sym->info.internFunc.directCommand);
|
|
}
|
|
else
|
|
{
|
|
specialDirect = YES;
|
|
}
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
else
|
|
{
|
|
direct = NO;
|
|
specialDirect = NO; // keep GCC quiet
|
|
}
|
|
i = 0;
|
|
if(argCount > 0)
|
|
{
|
|
if(tk_Token == TK_RPAREN)
|
|
{
|
|
ERR_Error(ERR_MISSING_PARAM, YES);
|
|
}
|
|
else
|
|
{
|
|
TK_Undo(); // Adjust for first expression
|
|
do
|
|
{
|
|
if(i == argCount)
|
|
{
|
|
ERR_Error(ERR_BAD_ARG_COUNT, YES);
|
|
TK_SkipTo(TK_SEMICOLON);
|
|
TK_Undo();
|
|
return;
|
|
}
|
|
TK_NextToken();
|
|
if(direct == YES)
|
|
{
|
|
if (tk_Token != TK_COMMA)
|
|
{
|
|
if (specialDirect)
|
|
{
|
|
argSave[i] = EvalConstExpression();
|
|
}
|
|
else
|
|
{
|
|
PC_AppendInt(EvalConstExpression());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (optMask & 1)
|
|
{
|
|
if (specialDirect)
|
|
{
|
|
argSave[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
PC_AppendInt(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERR_Error(ERR_MISSING_PARAM, YES);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (tk_Token != TK_COMMA)
|
|
{
|
|
if (!(outMask & 1))
|
|
{
|
|
EvalExpression();
|
|
}
|
|
else if (tk_Token != TK_IDENTIFIER)
|
|
{
|
|
ERR_Error (ERR_PARM_MUST_BE_VAR, YES);
|
|
do
|
|
{
|
|
TK_NextToken();
|
|
} while (tk_Token != TK_COMMA && tk_Token != TK_RPAREN);
|
|
}
|
|
else
|
|
{
|
|
symbolNode_t *sym = DemandSymbol (tk_String);
|
|
PC_AppendCmd (PCD_PUSHNUMBER);
|
|
switch (sym->type)
|
|
{
|
|
case SY_SCRIPTVAR:
|
|
PC_AppendInt(sym->info.var.index | OUTVAR_SCRIPT_SPEC);
|
|
break;
|
|
case SY_MAPVAR:
|
|
PC_AppendInt(sym->info.var.index | OUTVAR_MAP_SPEC);
|
|
break;
|
|
case SY_WORLDVAR:
|
|
PC_AppendInt(sym->info.var.index | OUTVAR_WORLD_SPEC);
|
|
break;
|
|
case SY_GLOBALVAR:
|
|
PC_AppendInt(sym->info.var.index | OUTVAR_GLOBAL_SPEC);
|
|
break;
|
|
default:
|
|
ERR_Error (ERR_PARM_MUST_BE_VAR, YES);
|
|
do
|
|
{
|
|
TK_NextToken();
|
|
} while (tk_Token != TK_COMMA && tk_Token != TK_RPAREN);
|
|
break;
|
|
}
|
|
TK_NextToken ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (optMask & 1)
|
|
{
|
|
PC_AppendPushVal(0);
|
|
}
|
|
else
|
|
{
|
|
ERR_Error(ERR_MISSING_PARAM, YES);
|
|
}
|
|
}
|
|
}
|
|
i++;
|
|
optMask >>= 1;
|
|
outMask >>= 1;
|
|
} while(tk_Token == TK_COMMA);
|
|
}
|
|
}
|
|
while (i < argCount && (optMask & 1))
|
|
{
|
|
if (direct == YES)
|
|
{
|
|
if (specialDirect)
|
|
{
|
|
argSave[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
PC_AppendInt(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PC_AppendPushVal(0);
|
|
}
|
|
i++;
|
|
optMask >>= 1;
|
|
}
|
|
if(i != argCount && i > 0)
|
|
{
|
|
ERR_Error(ERR_BAD_ARG_COUNT, YES);
|
|
}
|
|
TK_TokenMustBe(TK_RPAREN, argCount > 0 ? ERR_MISSING_RPAREN : ERR_BAD_ARG_COUNT);
|
|
if(direct == NO)
|
|
{
|
|
PC_AppendCmd(sym->info.internFunc.stackCommand);
|
|
}
|
|
else if (specialDirect)
|
|
{
|
|
boolean useintform = NO;
|
|
pcd_t shortpcd;
|
|
|
|
switch (sym->info.internFunc.directCommand)
|
|
{
|
|
case PCD_DELAYDIRECT:
|
|
shortpcd = PCD_DELAYDIRECTB;
|
|
break;
|
|
case PCD_RANDOMDIRECT:
|
|
shortpcd = PCD_RANDOMDIRECTB;
|
|
break;
|
|
default:
|
|
useintform = YES;
|
|
shortpcd = PCD_NOP;
|
|
break;
|
|
}
|
|
|
|
if (!useintform)
|
|
{
|
|
for (i = 0; i < argCount; i++)
|
|
{
|
|
if ((U_INT)argSave[i] > 255)
|
|
{
|
|
useintform = YES;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (useintform)
|
|
{
|
|
PC_AppendCmd(sym->info.internFunc.directCommand);
|
|
for (i = 0; i < argCount; i++)
|
|
{
|
|
PC_AppendInt (argSave[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PC_AppendCmd (shortpcd);
|
|
for (i = 0; i < argCount; i++)
|
|
{
|
|
PC_AppendByte ((U_BYTE)argSave[i]);
|
|
}
|
|
}
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingScriptFunc
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingScriptFunc(symbolNode_t *sym)
|
|
{
|
|
ProcessScriptFunc(sym, YES);
|
|
TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// ProcessScriptFunc
|
|
//
|
|
//==========================================================================
|
|
|
|
static void ProcessScriptFunc(symbolNode_t *sym, boolean discardReturn)
|
|
{
|
|
int i;
|
|
int argCount;
|
|
|
|
MS_Message(MSG_DEBUG, "---- ProcessScriptFunc ----\n");
|
|
if(sym->info.scriptFunc.predefined == YES && discardReturn == NO)
|
|
{
|
|
sym->info.scriptFunc.hasReturnValue = YES;
|
|
}
|
|
argCount = sym->info.scriptFunc.argCount;
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
i = 0;
|
|
if(argCount == 0)
|
|
{
|
|
TK_NextTokenMustBe(TK_RPAREN, ERR_BAD_ARG_COUNT);
|
|
}
|
|
else if(argCount > 0)
|
|
{
|
|
TK_NextToken();
|
|
if(tk_Token == TK_RPAREN)
|
|
{
|
|
ERR_Error(ERR_BAD_ARG_COUNT, YES);
|
|
TK_SkipTo(TK_SEMICOLON);
|
|
return;
|
|
}
|
|
TK_Undo();
|
|
do
|
|
{
|
|
if(i == argCount)
|
|
{
|
|
ERR_Error(ERR_BAD_ARG_COUNT, YES);
|
|
TK_SkipTo(TK_SEMICOLON);
|
|
return;
|
|
}
|
|
TK_NextToken();
|
|
if (tk_Token != TK_COMMA)
|
|
{
|
|
EvalExpression();
|
|
}
|
|
else
|
|
{
|
|
ERR_Error(ERR_MISSING_PARAM, YES);
|
|
TK_SkipTo(TK_SEMICOLON);
|
|
return;
|
|
}
|
|
i++;
|
|
} while(tk_Token == TK_COMMA);
|
|
}
|
|
if(argCount < 0)
|
|
{ // Function has not been defined yet, so assume arg count is correct
|
|
TK_NextToken();
|
|
while (tk_Token != TK_RPAREN)
|
|
{
|
|
EvalExpression();
|
|
i++;
|
|
if (tk_Token == TK_COMMA)
|
|
{
|
|
TK_NextToken();
|
|
}
|
|
else if (tk_Token != TK_RPAREN)
|
|
{
|
|
ERR_Error(ERR_MISSING_PARAM, YES);
|
|
TK_SkipTo(TK_SEMICOLON);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if(i != argCount)
|
|
{
|
|
ERR_Error(ERR_BAD_ARG_COUNT, YES);
|
|
TK_SkipTo(TK_SEMICOLON);
|
|
return;
|
|
}
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
PC_AppendCmd(discardReturn ? PCD_CALLDISCARD : PCD_CALL);
|
|
if(sym->info.scriptFunc.predefined && ImportMode != IMPORT_Importing)
|
|
{
|
|
AddScriptFuncRef(sym, pc_Address, i);
|
|
}
|
|
if (pc_NoShrink)
|
|
{
|
|
PC_AppendInt(sym->info.scriptFunc.funcNumber);
|
|
}
|
|
else
|
|
{
|
|
PC_AppendByte((U_BYTE)sym->info.scriptFunc.funcNumber);
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// BuildPrintString
|
|
//
|
|
//==========================================================================
|
|
|
|
static void BuildPrintString(void)
|
|
{
|
|
pcd_t printCmd;
|
|
|
|
do
|
|
{
|
|
switch(TK_NextCharacter())
|
|
{
|
|
case 'a': // character array support [JB]
|
|
ActionOnCharRange(NO);
|
|
continue;
|
|
case 's': // string
|
|
printCmd = PCD_PRINTSTRING;
|
|
break;
|
|
case 'l': // [RH] localized string
|
|
printCmd = PCD_PRINTLOCALIZED;
|
|
break;
|
|
case 'i': // integer
|
|
case 'd': // decimal
|
|
printCmd = PCD_PRINTNUMBER;
|
|
break;
|
|
case 'c': // character
|
|
printCmd = PCD_PRINTCHARACTER;
|
|
break;
|
|
case 'n': // [BC] name
|
|
printCmd = PCD_PRINTNAME;
|
|
break;
|
|
case 'f': // [RH] fixed point
|
|
printCmd = PCD_PRINTFIXED;
|
|
break;
|
|
case 'k': // [GRB] key binding
|
|
printCmd = PCD_PRINTBIND;
|
|
break;
|
|
case 'b': // [RH] binary integer
|
|
printCmd = PCD_PRINTBINARY;
|
|
break;
|
|
case 'x': // [RH] hexadecimal integer
|
|
printCmd = PCD_PRINTHEX;
|
|
break;
|
|
default:
|
|
printCmd = PCD_PRINTSTRING;
|
|
ERR_Error(ERR_UNKNOWN_PRTYPE, YES);
|
|
break;
|
|
}
|
|
TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
PC_AppendCmd(printCmd);
|
|
} while(tk_Token == TK_COMMA);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ActionOnCharRange // FDARI (mutation of PrintCharArray // JB)
|
|
//
|
|
//==========================================================================
|
|
|
|
static void ActionOnCharRange(boolean write)
|
|
{
|
|
boolean rangeConstraints;
|
|
symbolNode_t *sym;
|
|
TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
TK_NextToken();
|
|
|
|
if (tk_Token == TK_LPAREN)
|
|
{
|
|
rangeConstraints = YES;
|
|
TK_NextToken();
|
|
}
|
|
else
|
|
{
|
|
rangeConstraints = NO;
|
|
}
|
|
|
|
sym = SpeculateSymbol(tk_String, NO);
|
|
if((sym->type != SY_MAPARRAY) && (sym->type != SY_WORLDARRAY)
|
|
&& (sym->type != SY_GLOBALARRAY) && (sym->type != SY_SCRIPTARRAY))
|
|
{
|
|
ERR_Error(ERR_NOT_AN_ARRAY, YES, sym->name);
|
|
}
|
|
TK_NextToken();
|
|
if(sym->info.array.ndim > 1)
|
|
{
|
|
ParseArrayIndices(sym, sym->info.array.ndim-1);
|
|
}
|
|
else
|
|
{
|
|
PC_AppendPushVal(0);
|
|
}
|
|
|
|
PC_AppendPushVal(sym->info.array.index);
|
|
|
|
|
|
if (rangeConstraints)
|
|
{
|
|
switch (tk_Token)
|
|
{
|
|
case TK_RPAREN:
|
|
rangeConstraints = NO;
|
|
TK_NextToken();
|
|
break;
|
|
case TK_COMMA:
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
|
|
switch (tk_Token)
|
|
{
|
|
case TK_RPAREN:
|
|
TK_NextToken();
|
|
PC_AppendPushVal(0x7FFFFFFF);
|
|
break;
|
|
case TK_COMMA:
|
|
TK_NextToken();
|
|
EvalExpression(); // limit on capacity
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
TK_NextToken();
|
|
break;
|
|
default:
|
|
ERR_Error(ERR_MISSING_RPAREN, YES);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
ERR_Error(ERR_MISSING_RPAREN, YES);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (write)
|
|
{
|
|
if (!rangeConstraints)
|
|
{
|
|
PC_AppendPushVal(0);
|
|
PC_AppendPushVal(0x7FFFFFFF);
|
|
}
|
|
|
|
TK_TokenMustBe(TK_COMMA, ERR_MISSING_COMMA);
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
|
|
if (tk_Token == TK_COMMA)
|
|
{
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
}
|
|
else
|
|
{
|
|
PC_AppendPushVal(0);
|
|
}
|
|
}
|
|
|
|
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 );
|
|
}
|
|
else if(sym->type == SY_WORLDARRAY)
|
|
{
|
|
if (write) PC_AppendCmd(PCD_STRCPYTOWORLDCHRANGE);
|
|
else PC_AppendCmd( rangeConstraints ? PCD_PRINTWORLDCHRANGE : PCD_PRINTWORLDCHARARRAY );
|
|
}
|
|
else // if(sym->type == SY_GLOBALARRAY)
|
|
{
|
|
if (write) PC_AppendCmd(PCD_STRCPYTOGLOBALCHRANGE);
|
|
else PC_AppendCmd( rangeConstraints ? PCD_PRINTGLOBALCHRANGE : PCD_PRINTGLOBALCHARARRAY );
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingStrcpy
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingStrcpy(void)
|
|
{
|
|
MS_Message(MSG_DEBUG, "---- LeadingStrcpy ----\n");
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
|
|
switch(TK_NextCharacter()) // structure borrowed from printbuilder
|
|
{
|
|
case 'a':
|
|
ActionOnCharRange(YES);
|
|
break;
|
|
|
|
default:
|
|
ERR_Error(ERR_UNKNOWN_PRTYPE, YES);
|
|
break;
|
|
}
|
|
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingPrint
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingPrint(void)
|
|
{
|
|
tokenType_t stmtToken;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingPrint ----\n");
|
|
stmtToken = tk_Token; // Will be TK_PRINT or TK_PRINTBOLD, TK_LOG or TK_STRPARAM_EVAL [FDARI]
|
|
PC_AppendCmd(PCD_BEGINPRINT);
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
BuildPrintString();
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
|
|
switch (stmtToken)
|
|
{
|
|
case TK_PRINT:
|
|
PC_AppendCmd(PCD_ENDPRINT);
|
|
break;
|
|
|
|
case TK_PRINTBOLD:
|
|
PC_AppendCmd(PCD_ENDPRINTBOLD);
|
|
break;
|
|
|
|
case TK_STRPARAM_EVAL:
|
|
PC_AppendCmd(PCD_SAVESTRING);
|
|
return; // THE CALLER MUST DO THE POST-PROCESSING
|
|
|
|
case TK_LOG:
|
|
default:
|
|
PC_AppendCmd(PCD_ENDLOG);
|
|
break;
|
|
}
|
|
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingHudMessage
|
|
//
|
|
// hudmessage(str text; int type, int id, int color, fixed x, fixed y, fixed holdtime, ...)
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingHudMessage(void)
|
|
{
|
|
tokenType_t stmtToken;
|
|
int i;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingHudMessage ----\n");
|
|
stmtToken = tk_Token; // Will be TK_HUDMESSAGE or TK_HUDMESSAGEBOLD
|
|
PC_AppendCmd(PCD_BEGINPRINT);
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
BuildPrintString();
|
|
TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_PARAM);
|
|
PC_AppendCmd(PCD_MOREHUDMESSAGE);
|
|
for (i = 6; i > 0; i--)
|
|
{
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
if (i > 1)
|
|
TK_TokenMustBe(TK_COMMA, ERR_MISSING_PARAM);
|
|
}
|
|
if (tk_Token == TK_COMMA)
|
|
{ // HUD message has optional parameters
|
|
PC_AppendCmd(PCD_OPTHUDMESSAGE);
|
|
do
|
|
{
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
} while (tk_Token == TK_COMMA);
|
|
}
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
PC_AppendCmd(stmtToken == TK_HUDMESSAGE ?
|
|
PCD_ENDHUDMESSAGE : PCD_ENDHUDMESSAGEBOLD);
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingMorphActor
|
|
//
|
|
// MorphActor(int tid, [str playerclass, [str monsterclass, [int duration, [int style, [str morphflash, [str unmorphflash]]]]]])
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingMorphActor(void)
|
|
{
|
|
int i;
|
|
byte strMask;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingMorphActor ----\n");
|
|
strMask = 0b01100110;
|
|
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
if(TK_NextToken() == TK_CONST)
|
|
{
|
|
TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
ERR_Error(ERR_NO_DIRECT_VER, YES, NULL);
|
|
TK_NextToken();
|
|
}
|
|
i = 0;
|
|
if(tk_Token == TK_RPAREN)
|
|
{
|
|
ERR_Error(ERR_MISSING_PARAM, YES);
|
|
}
|
|
else
|
|
{
|
|
TK_Undo(); // Adjust for first expression
|
|
do
|
|
{
|
|
if(i == 7)
|
|
{
|
|
ERR_Error(ERR_BAD_ARG_COUNT, YES);
|
|
TK_SkipTo(TK_SEMICOLON);
|
|
TK_Undo();
|
|
return;
|
|
}
|
|
TK_NextToken();
|
|
|
|
if(tk_Token != TK_COMMA)
|
|
{
|
|
EvalExpression();
|
|
}
|
|
else
|
|
{
|
|
if(i > 0)
|
|
{
|
|
if(strMask & 1)
|
|
{
|
|
PC_AppendPushVal(STR_Find(""));
|
|
}
|
|
else
|
|
{
|
|
PC_AppendPushVal(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERR_Error(ERR_MISSING_PARAM, YES);
|
|
}
|
|
}
|
|
i++;
|
|
strMask >>= 1;
|
|
} while(tk_Token == TK_COMMA);
|
|
}
|
|
while(i < 7)
|
|
{
|
|
if(strMask & 1)
|
|
{
|
|
PC_AppendPushVal(STR_Find(""));
|
|
}
|
|
else
|
|
{
|
|
PC_AppendPushVal(0);
|
|
}
|
|
i++;
|
|
strMask >>= 1;
|
|
}
|
|
if(i != 7)
|
|
{
|
|
ERR_Error(ERR_BAD_ARG_COUNT, YES);
|
|
}
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
PC_AppendCmd(PCD_MORPHACTOR);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LumpReadOnCharRange
|
|
//
|
|
//==========================================================================
|
|
|
|
static U_BYTE LumpReadOnCharRange(void)
|
|
{
|
|
symbolNode_t *sym;
|
|
TK_NextToken();
|
|
|
|
sym = SpeculateSymbol(tk_String, NO);
|
|
if((sym->type != SY_MAPARRAY) && (sym->type != SY_WORLDARRAY)
|
|
&& (sym->type != SY_GLOBALARRAY) && (sym->type != SY_SCRIPTARRAY))
|
|
{
|
|
ERR_Error(ERR_NOT_AN_ARRAY, YES, sym->name);
|
|
}
|
|
|
|
PC_AppendPushVal(sym->info.array.index);
|
|
|
|
if(sym->type == SY_SCRIPTARRAY)
|
|
{
|
|
return 162;
|
|
}
|
|
else if(sym->type == SY_MAPARRAY)
|
|
{
|
|
return 163;
|
|
}
|
|
else if(sym->type == SY_WORLDARRAY)
|
|
{
|
|
return 164;
|
|
}
|
|
else
|
|
{
|
|
return 165;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingLumpReadArray
|
|
//
|
|
// LumpReadArray(int lump, int pos, Array dest, int index)
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingLumpReadArray(void)
|
|
{
|
|
U_BYTE funcIndex = 0;
|
|
int i = 0;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingLumpReadArray ----\n");
|
|
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
if(TK_NextToken() == TK_CONST)
|
|
{
|
|
TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
ERR_Error(ERR_NO_DIRECT_VER, YES, NULL);
|
|
TK_NextToken();
|
|
}
|
|
|
|
if(tk_Token == TK_RPAREN)
|
|
{
|
|
ERR_Error(ERR_MISSING_PARAM, YES);
|
|
}
|
|
else
|
|
{
|
|
TK_Undo(); // Adjust for first expression
|
|
do
|
|
{
|
|
if(i == 5)
|
|
{
|
|
ERR_Error(ERR_BAD_ARG_COUNT, YES);
|
|
TK_SkipTo(TK_SEMICOLON);
|
|
TK_Undo();
|
|
return;
|
|
}
|
|
TK_NextToken();
|
|
|
|
if(tk_Token != TK_COMMA)
|
|
{
|
|
if(i == 2)
|
|
{
|
|
funcIndex = LumpReadOnCharRange();
|
|
}
|
|
else
|
|
{
|
|
EvalExpression();
|
|
}
|
|
}
|
|
i++;
|
|
} while(tk_Token == TK_COMMA);
|
|
}
|
|
if(i < 3)
|
|
{
|
|
ERR_Error(ERR_BAD_ARG_COUNT, YES);
|
|
}
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
PC_AppendCmd(PCD_CALLFUNC);
|
|
PC_AppendByte(i);
|
|
PC_AppendWord(funcIndex);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingCreateTranslation
|
|
//
|
|
// Simple grammar:
|
|
//
|
|
// tranlationstmt: CreateTranslation ( exp opt_args ) ;
|
|
// opt_args: /* empty: just reset the translation */ | , arglist
|
|
// arglist: arg | arglist arg
|
|
// arg: range = replacement
|
|
// range: exp : exp
|
|
// replacement: palrep | colorrep | desatrep | colouriserep | tintrep
|
|
// palrep: exp : exp
|
|
// colorrep: [exp,exp,exp]:[exp,exp,exp]
|
|
// desatrep: %colorrep
|
|
// colouriserep: #[exp,exp,exp]
|
|
// tintrep: @exp[exp,exp,exp]
|
|
//==========================================================================
|
|
|
|
static void LeadingCreateTranslation(void)
|
|
{
|
|
MS_Message(MSG_DEBUG, "---- LeadingCreateTranslation ----\n");
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
PC_AppendCmd(PCD_STARTTRANSLATION);
|
|
while (tk_Token == TK_COMMA)
|
|
{
|
|
pcd_t translationcode;
|
|
|
|
TK_NextToken();
|
|
EvalExpression(); // Get first palette entry in range
|
|
TK_TokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
TK_NextToken();
|
|
EvalExpression(); // Get second palette entry in range
|
|
TK_TokenMustBe(TK_ASSIGN, ERR_MISSING_ASSIGN);
|
|
|
|
TK_NextToken();
|
|
if(tk_Token == TK_PERCENT)
|
|
{
|
|
translationcode = PCD_TRANSLATIONRANGE3;
|
|
TK_NextTokenMustBe(TK_LBRACKET, ERR_MISSING_LBRACKET);
|
|
}
|
|
else if (tk_Token == TK_NUMBERSIGN)
|
|
{
|
|
translationcode = PCD_TRANSLATIONRANGE4;
|
|
TK_NextTokenMustBe(TK_LBRACKET, ERR_MISSING_LBRACKET);
|
|
}
|
|
else if (tk_Token == TK_ATSIGN)
|
|
{
|
|
translationcode = PCD_TRANSLATIONRANGE5;
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
TK_TokenMustBe(TK_LBRACKET, ERR_MISSING_LBRACKET);
|
|
}
|
|
else
|
|
{
|
|
translationcode = PCD_TRANSLATIONRANGE2;
|
|
}
|
|
if(tk_Token == TK_LBRACKET)
|
|
{ // Replacement is color range
|
|
int i, j;
|
|
|
|
TK_NextToken();
|
|
|
|
for(j = 2; j != 0; --j)
|
|
{
|
|
for(i = 3; i != 0; --i)
|
|
{
|
|
EvalExpression();
|
|
if(i != 1)
|
|
{
|
|
TK_TokenMustBe(TK_COMMA, ERR_MISSING_COMMA);
|
|
}
|
|
else
|
|
{
|
|
TK_TokenMustBe(TK_RBRACKET, ERR_MISSING_RBRACKET);
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
if (translationcode == PCD_TRANSLATIONRANGE4 || translationcode == PCD_TRANSLATIONRANGE5)
|
|
break;
|
|
if(j == 2)
|
|
{
|
|
TK_TokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
TK_NextTokenMustBe(TK_LBRACKET, ERR_MISSING_LBRACKET);
|
|
TK_NextToken();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // Replacement is palette range
|
|
EvalExpression();
|
|
TK_TokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
translationcode = PCD_TRANSLATIONRANGE1;
|
|
}
|
|
PC_AppendCmd(translationcode);
|
|
}
|
|
PC_AppendCmd(PCD_ENDTRANSLATION);
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingIf
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingIf(void)
|
|
{
|
|
int jumpAddrPtr1;
|
|
int jumpAddrPtr2;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingIf ----\n");
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
PC_AppendCmd(PCD_IFNOTGOTO);
|
|
jumpAddrPtr1 = pc_Address;
|
|
PC_SkipInt();
|
|
TK_NextToken();
|
|
if(ProcessStatement(STMT_IF) == NO)
|
|
{
|
|
ERR_Error(ERR_INVALID_STATEMENT, YES);
|
|
}
|
|
if(tk_Token == TK_ELSE)
|
|
{
|
|
PC_AppendCmd(PCD_GOTO);
|
|
jumpAddrPtr2 = pc_Address;
|
|
PC_SkipInt();
|
|
PC_WriteInt(pc_Address, jumpAddrPtr1);
|
|
TK_NextToken();
|
|
if(ProcessStatement(STMT_ELSE) == NO)
|
|
{
|
|
ERR_Error(ERR_INVALID_STATEMENT, YES);
|
|
}
|
|
PC_WriteInt(pc_Address, jumpAddrPtr2);
|
|
}
|
|
else
|
|
{
|
|
PC_WriteInt(pc_Address, jumpAddrPtr1);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingFor
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingFor(void)
|
|
{
|
|
int exprAddr;
|
|
int incAddr;
|
|
int ifgotoAddr;
|
|
int gotoAddr;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingFor ----\n");
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
TK_NextToken();
|
|
if(ProcessStatement(STMT_IF) == NO)
|
|
{
|
|
ERR_Error(ERR_INVALID_STATEMENT, YES);
|
|
}
|
|
exprAddr = pc_Address;
|
|
EvalExpression();
|
|
TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
PC_AppendCmd(PCD_IFGOTO);
|
|
ifgotoAddr = pc_Address;
|
|
PC_SkipInt();
|
|
PC_AppendCmd(PCD_GOTO);
|
|
gotoAddr = pc_Address;
|
|
PC_SkipInt();
|
|
incAddr = pc_Address;
|
|
forSemicolonHack = TRUE;
|
|
if(ProcessStatement(STMT_IF) == NO)
|
|
{
|
|
ERR_Error(ERR_INVALID_STATEMENT, YES);
|
|
}
|
|
forSemicolonHack = FALSE;
|
|
PC_AppendCmd(PCD_GOTO);
|
|
PC_AppendInt(exprAddr);
|
|
PC_WriteInt(pc_Address,ifgotoAddr);
|
|
if(ProcessStatement(STMT_FOR) == NO)
|
|
{
|
|
ERR_Error(ERR_INVALID_STATEMENT, YES);
|
|
}
|
|
PC_AppendCmd(PCD_GOTO);
|
|
PC_AppendInt(incAddr);
|
|
WriteContinues(incAddr);
|
|
WriteBreaks();
|
|
PC_WriteInt(pc_Address,gotoAddr);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingWhileUntil
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingWhileUntil(void)
|
|
{
|
|
tokenType_t stmtToken;
|
|
int topAddr;
|
|
int outAddrPtr;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingWhileUntil ----\n");
|
|
stmtToken = tk_Token;
|
|
topAddr = pc_Address;
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
PC_AppendCmd(stmtToken == TK_WHILE ? PCD_IFNOTGOTO : PCD_IFGOTO);
|
|
outAddrPtr = pc_Address;
|
|
PC_SkipInt();
|
|
TK_NextToken();
|
|
if(ProcessStatement(STMT_WHILEUNTIL) == NO)
|
|
{
|
|
ERR_Error(ERR_INVALID_STATEMENT, YES);
|
|
}
|
|
PC_AppendCmd(PCD_GOTO);
|
|
PC_AppendInt(topAddr);
|
|
|
|
PC_WriteInt(pc_Address, outAddrPtr);
|
|
|
|
WriteContinues(topAddr);
|
|
WriteBreaks();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingDo
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingDo(void)
|
|
{
|
|
int topAddr;
|
|
int exprAddr;
|
|
tokenType_t stmtToken;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingDo ----\n");
|
|
topAddr = pc_Address;
|
|
TK_NextToken();
|
|
if(ProcessStatement(STMT_DO) == NO)
|
|
{
|
|
ERR_Error(ERR_INVALID_STATEMENT, YES, NULL);
|
|
}
|
|
if(tk_Token != TK_WHILE && tk_Token != TK_UNTIL)
|
|
{
|
|
ERR_Error(ERR_BAD_DO_STATEMENT, YES, NULL);
|
|
TK_SkipPast(TK_SEMICOLON);
|
|
return;
|
|
}
|
|
stmtToken = tk_Token;
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
exprAddr = pc_Address;
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
PC_AppendCmd(stmtToken == TK_WHILE ? PCD_IFGOTO : PCD_IFNOTGOTO);
|
|
PC_AppendInt(topAddr);
|
|
WriteContinues(exprAddr);
|
|
WriteBreaks();
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingSwitch
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingSwitch(void)
|
|
{
|
|
int switcherAddrPtr;
|
|
int outAddrPtr;
|
|
caseInfo_t *cInfo;
|
|
int defaultAddress;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingSwitch ----\n");
|
|
|
|
TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN);
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
|
|
PC_AppendCmd(PCD_GOTO);
|
|
switcherAddrPtr = pc_Address;
|
|
PC_SkipInt();
|
|
|
|
TK_NextToken();
|
|
if(ProcessStatement(STMT_SWITCH) == NO)
|
|
{
|
|
ERR_Error(ERR_INVALID_STATEMENT, YES, NULL);
|
|
}
|
|
|
|
PC_AppendCmd(PCD_GOTO);
|
|
outAddrPtr = pc_Address;
|
|
PC_SkipInt();
|
|
|
|
PC_WriteInt(pc_Address, switcherAddrPtr);
|
|
defaultAddress = 0;
|
|
|
|
if(pc_HexenCase)
|
|
{
|
|
while((cInfo = GetCaseInfo()) != NULL)
|
|
{
|
|
if(cInfo->isDefault == YES)
|
|
{
|
|
defaultAddress = cInfo->address;
|
|
continue;
|
|
}
|
|
PC_AppendCmd(PCD_CASEGOTO);
|
|
PC_AppendInt(cInfo->value);
|
|
PC_AppendInt(cInfo->address);
|
|
}
|
|
}
|
|
else if(CaseIndex != 0)
|
|
{
|
|
caseInfo_t *maxCase = &CaseInfo[CaseIndex];
|
|
caseInfo_t *minCase = maxCase;
|
|
|
|
// [RH] Sort cases so that the VM can handle them with
|
|
// a quick binary search.
|
|
while((cInfo = GetCaseInfo()) != NULL)
|
|
{
|
|
minCase = cInfo;
|
|
}
|
|
qsort(minCase, maxCase - minCase, sizeof(caseInfo_t), CaseInfoCmp);
|
|
if(minCase->isDefault == YES)
|
|
{
|
|
defaultAddress = minCase->address;
|
|
minCase++;
|
|
}
|
|
if (minCase < maxCase)
|
|
{
|
|
PC_AppendCmd(PCD_CASEGOTOSORTED);
|
|
if(pc_Address%4 != 0)
|
|
{ // Align to a 4-byte boundary
|
|
U_INT pad = 0;
|
|
PC_Append((void *)&pad, 4-(pc_Address%4));
|
|
}
|
|
PC_AppendInt(maxCase - minCase);
|
|
for(; minCase < maxCase; ++minCase)
|
|
{
|
|
PC_AppendInt(minCase->value);
|
|
PC_AppendInt(minCase->address);
|
|
}
|
|
}
|
|
}
|
|
PC_AppendCmd(PCD_DROP);
|
|
|
|
if(defaultAddress != 0)
|
|
{
|
|
PC_AppendCmd(PCD_GOTO);
|
|
PC_AppendInt(defaultAddress);
|
|
}
|
|
|
|
PC_WriteInt(pc_Address, outAddrPtr);
|
|
|
|
WriteBreaks();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingCase
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingCase(void)
|
|
{
|
|
MS_Message(MSG_DEBUG, "---- LeadingCase ----\n");
|
|
TK_NextToken();
|
|
PushCase(EvalConstExpression(), NO);
|
|
TK_TokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingDefault
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingDefault(void)
|
|
{
|
|
MS_Message(MSG_DEBUG, "---- LeadingDefault ----\n");
|
|
TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON);
|
|
PushCase(0, YES);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// PushCase
|
|
//
|
|
//==========================================================================
|
|
|
|
static void PushCase(int value, boolean isDefault)
|
|
{
|
|
if(CaseIndex == MAX_CASE)
|
|
{
|
|
ERR_Exit(ERR_CASE_OVERFLOW, YES);
|
|
}
|
|
CaseInfo[CaseIndex].level = StatementLevel;
|
|
CaseInfo[CaseIndex].value = value;
|
|
CaseInfo[CaseIndex].isDefault = isDefault;
|
|
CaseInfo[CaseIndex].address = pc_Address;
|
|
CaseIndex++;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// GetCaseInfo
|
|
//
|
|
//==========================================================================
|
|
|
|
static caseInfo_t *GetCaseInfo(void)
|
|
{
|
|
if(CaseIndex == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
if(CaseInfo[CaseIndex-1].level > StatementLevel)
|
|
{
|
|
return &CaseInfo[--CaseIndex];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CaseInfoCmp
|
|
//
|
|
//==========================================================================
|
|
|
|
static int CaseInfoCmp (const void *a, const void *b)
|
|
{
|
|
const caseInfo_t *ca = (const caseInfo_t *)a;
|
|
const caseInfo_t *cb = (const caseInfo_t *)b;
|
|
|
|
// The default case always gets moved to the front.
|
|
if(ca->isDefault)
|
|
{
|
|
return -1;
|
|
}
|
|
if(cb->isDefault)
|
|
{
|
|
return 1;
|
|
}
|
|
return ca->value - cb->value;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// DefaultInCurrent
|
|
//
|
|
//==========================================================================
|
|
|
|
static boolean DefaultInCurrent(void)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < CaseIndex; i++)
|
|
{
|
|
if(CaseInfo[i].isDefault == YES
|
|
&& CaseInfo[i].level == StatementLevel)
|
|
{
|
|
return YES;
|
|
}
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingBreak
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingBreak(void)
|
|
{
|
|
MS_Message(MSG_DEBUG, "---- LeadingBreak ----\n");
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
PC_AppendCmd(PCD_GOTO);
|
|
PushBreak();
|
|
PC_SkipInt();
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// PushBreak
|
|
//
|
|
//==========================================================================
|
|
|
|
static void PushBreak(void)
|
|
{
|
|
if(BreakIndex == MAX_CASE)
|
|
{
|
|
ERR_Exit(ERR_BREAK_OVERFLOW, YES);
|
|
}
|
|
BreakInfo[BreakIndex].level = StatementLevel;
|
|
BreakInfo[BreakIndex].addressPtr = pc_Address;
|
|
BreakIndex++;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// WriteBreaks
|
|
//
|
|
//==========================================================================
|
|
|
|
static void WriteBreaks(void)
|
|
{
|
|
while(BreakIndex && BreakInfo[BreakIndex-1].level > StatementLevel)
|
|
{
|
|
PC_WriteInt(pc_Address, BreakInfo[--BreakIndex].addressPtr);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// BreakAncestor
|
|
//
|
|
// Returns YES if the current statement history contains a break root
|
|
// statement.
|
|
//
|
|
//==========================================================================
|
|
|
|
static boolean BreakAncestor(void)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < StatementIndex; i++)
|
|
{
|
|
if(IsBreakRoot[StatementHistory[i]])
|
|
{
|
|
return YES;
|
|
}
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingContinue
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingContinue(void)
|
|
{
|
|
MS_Message(MSG_DEBUG, "---- LeadingContinue ----\n");
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
PC_AppendCmd(PCD_GOTO);
|
|
PushContinue();
|
|
PC_SkipInt();
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// PushContinue
|
|
//
|
|
//==========================================================================
|
|
|
|
static void PushContinue(void)
|
|
{
|
|
if(ContinueIndex == MAX_CONTINUE)
|
|
{
|
|
ERR_Exit(ERR_CONTINUE_OVERFLOW, YES);
|
|
}
|
|
ContinueInfo[ContinueIndex].level = StatementLevel;
|
|
ContinueInfo[ContinueIndex].addressPtr = pc_Address;
|
|
ContinueIndex++;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// WriteContinues
|
|
//
|
|
//==========================================================================
|
|
|
|
static void WriteContinues(int address)
|
|
{
|
|
if(ContinueIndex == 0)
|
|
{
|
|
return;
|
|
}
|
|
while(ContinueInfo[ContinueIndex-1].level > StatementLevel)
|
|
{
|
|
PC_WriteInt(address, ContinueInfo[--ContinueIndex].addressPtr);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ContinueAncestor
|
|
//
|
|
//==========================================================================
|
|
|
|
static boolean ContinueAncestor(void)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < StatementIndex; i++)
|
|
{
|
|
if(IsContinueRoot[StatementHistory[i]])
|
|
{
|
|
return YES;
|
|
}
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingIncDec
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingIncDec(int token)
|
|
{
|
|
symbolNode_t *sym;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingIncDec ----\n");
|
|
TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INCDEC_OP_ON_NON_VAR);
|
|
sym = DemandSymbol(tk_String);
|
|
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_SCRIPTARRAY)
|
|
{
|
|
ERR_Error(ERR_INCDEC_OP_ON_NON_VAR, YES);
|
|
TK_SkipPast(TK_SEMICOLON);
|
|
return;
|
|
}
|
|
TK_NextToken();
|
|
if(sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY
|
|
|| sym->type == SY_GLOBALARRAY || sym->type == SY_SCRIPTARRAY)
|
|
{
|
|
ParseArrayIndices(sym, sym->info.array.ndim);
|
|
}
|
|
else if(tk_Token == TK_LBRACKET)
|
|
{
|
|
ERR_Error(ERR_NOT_AN_ARRAY, YES, sym->name);
|
|
while(tk_Token == TK_LBRACKET)
|
|
{
|
|
TK_SkipPast(TK_RBRACKET);
|
|
}
|
|
}
|
|
PC_AppendCmd(GetIncDecPCD(token, sym->type));
|
|
PC_AppendShrink(sym->info.var.index);
|
|
if(forSemicolonHack)
|
|
{
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
}
|
|
else
|
|
{
|
|
TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingVarAssign
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingVarAssign(symbolNode_t *sym)
|
|
{
|
|
boolean done;
|
|
tokenType_t assignToken;
|
|
|
|
MS_Message(MSG_DEBUG, "---- LeadingVarAssign ----\n");
|
|
done = NO;
|
|
do
|
|
{
|
|
TK_NextToken(); // Fetch assignment operator
|
|
if(sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY
|
|
|| sym->type == SY_GLOBALARRAY || sym->type == SY_SCRIPTARRAY)
|
|
{
|
|
ParseArrayIndices(sym, sym->info.array.ndim);
|
|
}
|
|
else if(tk_Token == TK_LBRACKET)
|
|
{
|
|
ERR_Error(ERR_NOT_AN_ARRAY, YES, sym->name);
|
|
while(tk_Token == TK_LBRACKET)
|
|
{
|
|
TK_SkipPast(TK_RBRACKET);
|
|
}
|
|
}
|
|
if(tk_Token == TK_INC || tk_Token == TK_DEC)
|
|
{ // Postfix increment or decrement
|
|
PC_AppendCmd(GetIncDecPCD(tk_Token, sym->type));
|
|
if (pc_NoShrink)
|
|
{
|
|
PC_AppendInt(sym->info.var.index);
|
|
}
|
|
else
|
|
{
|
|
PC_AppendByte(sym->info.var.index);
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
else
|
|
{ // Normal operator
|
|
if(TK_Member(AssignOps) == NO)
|
|
{
|
|
ERR_Error(ERR_MISSING_ASSIGN_OP, YES);
|
|
TK_SkipPast(TK_SEMICOLON);
|
|
return;
|
|
}
|
|
assignToken = tk_Token;
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
PC_AppendCmd(GetAssignPCD(assignToken, sym->type));
|
|
PC_AppendShrink(sym->info.var.index);
|
|
}
|
|
if(tk_Token == TK_COMMA)
|
|
{
|
|
TK_NextTokenMustBe(TK_IDENTIFIER, ERR_BAD_ASSIGNMENT);
|
|
sym = DemandSymbol(tk_String);
|
|
if(sym->type != SY_SCRIPTVAR && sym->type != SY_MAPVAR
|
|
&& sym->type != SY_WORLDVAR && sym->type != SY_GLOBALVAR
|
|
&& sym->type != SY_WORLDARRAY && sym->type != SY_GLOBALARRAY
|
|
&& sym->type != SY_SCRIPTALIAS)
|
|
{
|
|
ERR_Error(ERR_BAD_ASSIGNMENT, YES);
|
|
TK_SkipPast(TK_SEMICOLON);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
TK_NextToken();
|
|
done = YES;
|
|
}
|
|
} while(done == NO);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// GetAssignPCD
|
|
//
|
|
//==========================================================================
|
|
|
|
static pcd_t GetAssignPCD(tokenType_t token, symbolType_t symbol)
|
|
{
|
|
size_t i, j;
|
|
static tokenType_t tokenLookup[] =
|
|
{
|
|
TK_ASSIGN, TK_ADDASSIGN, TK_SUBASSIGN,
|
|
TK_MULASSIGN, TK_DIVASSIGN, TK_MODASSIGN,
|
|
TK_ANDASSIGN, TK_EORASSIGN, TK_ORASSIGN,
|
|
TK_LSASSIGN, TK_RSASSIGN
|
|
};
|
|
static symbolType_t symbolLookup[] =
|
|
{
|
|
SY_SCRIPTVAR, SY_MAPVAR, SY_WORLDVAR, SY_GLOBALVAR, SY_MAPARRAY,
|
|
SY_WORLDARRAY, SY_GLOBALARRAY, SY_SCRIPTARRAY
|
|
};
|
|
static pcd_t assignmentLookup[11][8] =
|
|
{
|
|
{ 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_ADDSCRIPTARRAY },
|
|
{ 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)
|
|
{
|
|
if(tokenLookup[i] == token)
|
|
{
|
|
for(j = 0; j < ARRAY_SIZE(symbolLookup); ++j)
|
|
{
|
|
if (symbolLookup[j] == symbol)
|
|
{
|
|
return assignmentLookup[i][j];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return PCD_NOP;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingSuspend
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingSuspend(void)
|
|
{
|
|
MS_Message(MSG_DEBUG, "---- LeadingSuspend ----\n");
|
|
if(InsideFunction)
|
|
{
|
|
ERR_Error(ERR_SUSPEND_IN_FUNCTION, YES);
|
|
}
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
PC_AppendCmd(PCD_SUSPEND);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingTerminate
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingTerminate(void)
|
|
{
|
|
MS_Message(MSG_DEBUG, "---- LeadingTerminate ----\n");
|
|
if(InsideFunction)
|
|
{
|
|
ERR_Error(ERR_TERMINATE_IN_FUNCTION, YES);
|
|
}
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
PC_AppendCmd(PCD_TERMINATE);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingRestart
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingRestart(void)
|
|
{
|
|
MS_Message(MSG_DEBUG, "---- LeadingRestart ----\n");
|
|
if(InsideFunction)
|
|
{
|
|
ERR_Error(ERR_RESTART_IN_FUNCTION, YES);
|
|
}
|
|
TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
PC_AppendCmd(PCD_RESTART);
|
|
TK_NextToken();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// LeadingReturn
|
|
//
|
|
//==========================================================================
|
|
|
|
static void LeadingReturn(void)
|
|
{
|
|
MS_Message(MSG_DEBUG, "---- LeadingReturn ----\n");
|
|
if(!InsideFunction)
|
|
{
|
|
ERR_Error(ERR_RETURN_OUTSIDE_FUNCTION, YES);
|
|
while (TK_NextToken () != TK_SEMICOLON)
|
|
{
|
|
if (tk_Token == TK_EOF)
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TK_NextToken();
|
|
if(tk_Token == TK_SEMICOLON)
|
|
{
|
|
if(InsideFunction->info.scriptFunc.hasReturnValue)
|
|
{
|
|
ERR_Error(ERR_MUST_RETURN_A_VALUE, YES);
|
|
}
|
|
PC_AppendCmd(PCD_RETURNVOID);
|
|
}
|
|
else
|
|
{
|
|
if(!InsideFunction->info.scriptFunc.hasReturnValue)
|
|
{
|
|
ERR_Error(ERR_MUST_NOT_RETURN_A_VALUE, YES);
|
|
}
|
|
EvalExpression();
|
|
TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON);
|
|
PC_AppendCmd(PCD_RETURNVAL);
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// EvalConstExpression
|
|
//
|
|
//==========================================================================
|
|
|
|
static int EvalConstExpression(void)
|
|
{
|
|
pa_ConstExprIsString = NO; // Used by PC_PutMapVariable
|
|
ExprStackIndex = 0;
|
|
ConstantExpression = YES;
|
|
ExprLevA();
|
|
if(ExprStackIndex != 1)
|
|
{
|
|
ERR_Error(ERR_BAD_CONST_EXPR, YES, NULL);
|
|
ExprStack[0] = 0;
|
|
ExprStackIndex = 1;
|
|
}
|
|
return PopExStk();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// EvalExpression
|
|
//
|
|
// [RH] Rewrote all the ExprLevA - ExprLevJ functions in favor of a single
|
|
// table-driven parser function.
|
|
//
|
|
//==========================================================================
|
|
|
|
static void EvalExpression(void)
|
|
{
|
|
ConstantExpression = NO;
|
|
ExprLevA();
|
|
}
|
|
|
|
static void ExprLevA(void)
|
|
{
|
|
ExprLevX(0);
|
|
}
|
|
|
|
// Operator precedence levels:
|
|
// Operator: ? :
|
|
// Operator: ||
|
|
// Operator: &&
|
|
// Operator: |
|
|
// Operator: ^
|
|
// Operator: &
|
|
// Operators: == !=
|
|
// Operators: < <= > >=
|
|
// Operators: << >>
|
|
// Operators: + -
|
|
// Operators: * / %
|
|
|
|
static void ExprLevX(int level)
|
|
{
|
|
if(OpsList[level] == NULL)
|
|
{
|
|
boolean unaryMinus;
|
|
|
|
unaryMinus = FALSE;
|
|
if(tk_Token == TK_MINUS)
|
|
{
|
|
unaryMinus = TRUE;
|
|
TK_NextToken();
|
|
}
|
|
if(tk_Token == TK_PLUS)
|
|
{
|
|
// Completely ignore unary plus
|
|
TK_NextToken();
|
|
}
|
|
if(ConstantExpression == YES)
|
|
{
|
|
ConstExprFactor();
|
|
}
|
|
else
|
|
{
|
|
ExprFactor();
|
|
}
|
|
if(unaryMinus == TRUE)
|
|
{
|
|
SendExprCommand(PCD_UNARYMINUS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ExprLevX(level + 1);
|
|
while(TK_Member(OpsList[level]))
|
|
{
|
|
if (tk_Token == TK_TERNARY)
|
|
{
|
|
ExprTernary();
|
|
}
|
|
else
|
|
{
|
|
tokenType_t token = tk_Token;
|
|
TK_NextToken();
|
|
ExprLevX(level + 1);
|
|
SendExprCommand(TokenToPCD(token));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ExprLineSpecial(void)
|
|
{
|
|
int argCountMin = tk_SpecialArgCount & 0xffff;
|
|
int argCountMax = tk_SpecialArgCount >> 16;
|
|
int specialValue = tk_SpecialValue;
|
|
|
|
// There are two ways to use a special in an expression:
|
|
// 1. The special name by itself returns the special's number.
|
|
// 2. The special followed by parameters actually executes the special.
|
|
TK_NextToken();
|
|
if(tk_Token != TK_LPAREN)
|
|
{
|
|
PC_AppendPushVal(specialValue);
|
|
}
|
|
else
|
|
{
|
|
int argCount = 0;
|
|
|
|
TK_NextToken();
|
|
if(tk_Token != TK_RPAREN)
|
|
{
|
|
TK_Undo();
|
|
do
|
|
{
|
|
TK_NextToken();
|
|
EvalExpression();
|
|
argCount++;
|
|
} while(tk_Token == TK_COMMA);
|
|
}
|
|
if(argCount < argCountMin || argCount > argCountMax)
|
|
{
|
|
ERR_Error(specialValue >=0? ERR_BAD_LSPEC_ARG_COUNT : ERR_BAD_ARG_COUNT, YES);
|
|
return;
|
|
}
|
|
if (specialValue >= 0)
|
|
{
|
|
for(; argCount < 5; ++argCount)
|
|
{
|
|
PC_AppendPushVal(0);
|
|
}
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
TK_NextToken();
|
|
PC_AppendCmd(specialValue <= 255? PCD_LSPEC5RESULT : PCD_LSPEC5EXRESULT);
|
|
if(pc_NoShrink || specialValue > 255)
|
|
{
|
|
PC_AppendInt(specialValue);
|
|
}
|
|
else
|
|
{
|
|
PC_AppendByte((U_BYTE)specialValue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN);
|
|
TK_NextToken();
|
|
PC_AppendCmd(PCD_CALLFUNC);
|
|
if(pc_NoShrink)
|
|
{
|
|
PC_AppendInt(argCount);
|
|
PC_AppendInt(-specialValue);
|
|
}
|
|
else
|
|
{
|
|
PC_AppendByte((U_BYTE)argCount);
|
|
PC_AppendWord((U_WORD)-specialValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ExprTernary(void)
|
|
{
|
|
int jumpAddrPtr;
|
|
|
|
PC_AppendCmd(PCD_IFNOTGOTO);
|
|
jumpAddrPtr = pc_Address;
|
|
PC_SkipInt();
|
|
|
|
TK_NextToken();
|
|
ExprLevA();
|
|
|
|
if(tk_Token != TK_COLON)
|
|
{
|
|
ERR_Error(ERR_BAD_EXPR, YES, NULL);
|
|
}
|
|
else
|
|
{
|
|
PC_AppendCmd(PCD_GOTO);
|
|
PC_WriteInt(pc_Address + 4, jumpAddrPtr); // int = 4 bytes
|
|
jumpAddrPtr = pc_Address;
|
|
PC_SkipInt();
|
|
|
|
TK_NextToken();
|
|
ExprLevA();
|
|
|
|
PC_WriteInt(pc_Address, jumpAddrPtr);
|
|
}
|
|
|
|
}
|
|
|
|
static void ExprFactor(void)
|
|
{
|
|
symbolNode_t *sym;
|
|
tokenType_t opToken;
|
|
|
|
switch(tk_Token)
|
|
{
|
|
case TK_STRING:
|
|
if (ImportMode != IMPORT_Importing)
|
|
{
|
|
tk_Number = STR_Find(tk_String);
|
|
PC_AppendPushVal(tk_Number);
|
|
if (ImportMode == IMPORT_Exporting)
|
|
{
|
|
// The VM identifies strings by storing a library ID in the
|
|
// high word of the string index. The library ID is not
|
|
// known until the script actually gets loaded into the game,
|
|
// so we need to use this p-code to tack the ID of the
|
|
// currently running library to the index of the string that
|
|
// just got pushed onto the stack.
|
|
//
|
|
// Simply assuming that a string is from the current library
|
|
// is not good enough, because they can be passed around
|
|
// between libraries and the map's behavior. Thus, we need
|
|
// to know which object file a particular string belongs to.
|
|
//
|
|
// A specific example:
|
|
// A map's behavior calls a function in a library and passes
|
|
// a string:
|
|
//
|
|
// LibFunc ("The library will do something with this string.");
|
|
//
|
|
// The library function needs to know that the string originated
|
|
// outside the library. Similarly, if a library function returns
|
|
// a string, the caller needs to know that the string did not
|
|
// originate from the same object file.
|
|
//
|
|
// And that's why strings have library IDs tacked onto them.
|
|
// The map's main behavior (i.e. an object that is not a library)
|
|
// always uses library ID 0 to identify its strings, so its
|
|
// strings don't need to be tagged.
|
|
PC_AppendCmd(PCD_TAGSTRING);
|
|
}
|
|
}
|
|
TK_NextToken();
|
|
break;
|
|
case TK_NUMBER:
|
|
PC_AppendPushVal(tk_Number);
|
|
TK_NextToken();
|
|
break;
|
|
case TK_LPAREN:
|
|
TK_NextToken();
|
|
ExprLevA();
|
|
if(tk_Token != TK_RPAREN)
|
|
{
|
|
ERR_Error(ERR_BAD_EXPR, YES, NULL);
|
|
TK_SkipPast(TK_RPAREN);
|
|
}
|
|
else
|
|
{
|
|
TK_NextToken();
|
|
}
|
|
break;
|
|
case TK_NOT:
|
|
TK_NextToken();
|
|
ExprFactor();
|
|
PC_AppendCmd(PCD_NEGATELOGICAL);
|
|
break;
|
|
case TK_TILDE:
|
|
TK_NextToken();
|
|
ExprFactor();
|
|
PC_AppendCmd(PCD_NEGATEBINARY);
|
|
break;
|
|
case TK_INC:
|
|
case TK_DEC:
|
|
opToken = tk_Token;
|
|
TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INCDEC_OP_ON_NON_VAR);
|
|
sym = DemandSymbol(tk_String);
|
|
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_SCRIPTARRAY)
|
|
{
|
|
ERR_Error(ERR_INCDEC_OP_ON_NON_VAR, YES);
|
|
}
|
|
else
|
|
{
|
|
TK_NextToken();
|
|
if(sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY
|
|
|| sym->type == SY_GLOBALARRAY || sym->type == SY_SCRIPTARRAY)
|
|
{
|
|
ParseArrayIndices(sym, sym->info.array.ndim);
|
|
PC_AppendCmd(PCD_DUP);
|
|
}
|
|
else if(tk_Token == TK_LBRACKET)
|
|
{
|
|
ERR_Error(ERR_NOT_AN_ARRAY, YES, sym->name);
|
|
while(tk_Token == TK_LBRACKET)
|
|
{
|
|
TK_SkipPast(TK_RBRACKET);
|
|
}
|
|
}
|
|
PC_AppendCmd(GetIncDecPCD(opToken, sym->type));
|
|
PC_AppendShrink(sym->info.var.index);
|
|
PC_AppendCmd(GetPushVarPCD(sym->type));
|
|
PC_AppendShrink(sym->info.var.index);
|
|
}
|
|
break;
|
|
case TK_IDENTIFIER:
|
|
sym = SpeculateSymbol(tk_String, YES);
|
|
switch(sym->type)
|
|
{
|
|
case SY_SCRIPTALIAS:
|
|
// FIXME
|
|
break;
|
|
case SY_SCRIPTARRAY:
|
|
case SY_MAPARRAY:
|
|
case SY_WORLDARRAY:
|
|
case SY_GLOBALARRAY:
|
|
TK_NextToken();
|
|
ParseArrayIndices(sym, sym->info.array.ndim);
|
|
// fallthrough
|
|
case SY_SCRIPTVAR:
|
|
case SY_MAPVAR:
|
|
case SY_WORLDVAR:
|
|
case SY_GLOBALVAR:
|
|
if(sym->type != SY_MAPARRAY && sym->type != SY_WORLDARRAY
|
|
&& sym->type != SY_GLOBALARRAY && sym->type != SY_SCRIPTARRAY)
|
|
{
|
|
TK_NextToken();
|
|
if(tk_Token == TK_LBRACKET)
|
|
{
|
|
ERR_Error(ERR_NOT_AN_ARRAY, YES, sym->name);
|
|
while(tk_Token == TK_LBRACKET)
|
|
{
|
|
TK_SkipPast(TK_RBRACKET);
|
|
}
|
|
}
|
|
}
|
|
if((tk_Token == TK_INC || tk_Token == TK_DEC)
|
|
&& (sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY
|
|
|| sym->type == SY_GLOBALARRAY || sym->type == SY_SCRIPTARRAY))
|
|
{
|
|
PC_AppendCmd(PCD_DUP);
|
|
}
|
|
PC_AppendCmd(GetPushVarPCD(sym->type));
|
|
PC_AppendShrink(sym->info.var.index);
|
|
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_SCRIPTARRAY)
|
|
{
|
|
PC_AppendCmd(PCD_SWAP);
|
|
}
|
|
PC_AppendCmd(GetIncDecPCD(tk_Token, sym->type));
|
|
PC_AppendShrink(sym->info.var.index);
|
|
TK_NextToken();
|
|
}
|
|
break;
|
|
case SY_INTERNFUNC:
|
|
if(sym->info.internFunc.hasReturnValue == NO)
|
|
{
|
|
ERR_Error(ERR_EXPR_FUNC_NO_RET_VAL, YES);
|
|
}
|
|
ProcessInternFunc(sym);
|
|
break;
|
|
case SY_SCRIPTFUNC:
|
|
if(sym->info.scriptFunc.predefined == NO
|
|
&& sym->info.scriptFunc.hasReturnValue == NO)
|
|
{
|
|
ERR_Error(ERR_EXPR_FUNC_NO_RET_VAL, YES);
|
|
}
|
|
ProcessScriptFunc(sym, NO);
|
|
break;
|
|
default:
|
|
ERR_Error(ERR_ILLEGAL_EXPR_IDENT, YES, tk_String);
|
|
TK_NextToken();
|
|
break;
|
|
}
|
|
break;
|
|
case TK_LINESPECIAL:
|
|
ExprLineSpecial();
|
|
break;
|
|
case TK_STRPARAM_EVAL:
|
|
LeadingPrint();
|
|
TK_NextToken();
|
|
break;
|
|
case TK_STRCPY:
|
|
LeadingStrcpy();
|
|
TK_NextToken();
|
|
break;
|
|
case TK_MORPHACTOR:
|
|
LeadingMorphActor();
|
|
TK_NextToken();
|
|
break;
|
|
case TK_LUMPREADARRAY:
|
|
LeadingLumpReadArray();
|
|
TK_NextToken();
|
|
break;
|
|
default:
|
|
ERR_Error(ERR_BAD_EXPR, YES);
|
|
TK_NextToken();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void ConstExprFactor(void)
|
|
{
|
|
switch(tk_Token)
|
|
{
|
|
case TK_STRING:
|
|
if (ImportMode != IMPORT_Importing)
|
|
{
|
|
int strnum = STR_Find(tk_String);
|
|
if (ImportMode == IMPORT_Exporting)
|
|
{
|
|
pa_ConstExprIsString = YES;
|
|
}
|
|
PushExStk(strnum);
|
|
}
|
|
else
|
|
{
|
|
// Importing, so it doesn't matter
|
|
PushExStk(0);
|
|
}
|
|
TK_NextToken();
|
|
break;
|
|
case TK_NUMBER:
|
|
PushExStk(tk_Number);
|
|
TK_NextToken();
|
|
break;
|
|
case TK_LPAREN:
|
|
TK_NextToken();
|
|
ExprLevA();
|
|
if(tk_Token != TK_RPAREN)
|
|
{
|
|
ERR_Error(ERR_BAD_CONST_EXPR, YES);
|
|
TK_SkipPast(TK_RPAREN);
|
|
}
|
|
else
|
|
{
|
|
TK_NextToken();
|
|
}
|
|
break;
|
|
case TK_NOT:
|
|
TK_NextToken();
|
|
ConstExprFactor();
|
|
SendExprCommand(PCD_NEGATELOGICAL);
|
|
break;
|
|
case TK_TILDE:
|
|
TK_NextToken();
|
|
ConstExprFactor();
|
|
SendExprCommand(PCD_NEGATEBINARY);
|
|
break;
|
|
default:
|
|
ERR_Error(ERR_BAD_CONST_EXPR, YES);
|
|
PushExStk(0);
|
|
while(tk_Token != TK_COMMA &&
|
|
tk_Token != TK_SEMICOLON &&
|
|
tk_Token != TK_RPAREN)
|
|
{
|
|
if(tk_Token == TK_EOF)
|
|
{
|
|
ERR_Exit(ERR_EOF, YES);
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// SendExprCommand
|
|
//
|
|
//==========================================================================
|
|
|
|
static void SendExprCommand(pcd_t pcd)
|
|
{
|
|
int operand1, operand2;
|
|
|
|
if(ConstantExpression == NO)
|
|
{
|
|
PC_AppendCmd(pcd);
|
|
return;
|
|
}
|
|
switch(pcd)
|
|
{
|
|
case PCD_ADD:
|
|
PushExStk(PopExStk()+PopExStk());
|
|
break;
|
|
case PCD_SUBTRACT:
|
|
operand2 = PopExStk();
|
|
PushExStk(PopExStk()-operand2);
|
|
break;
|
|
case PCD_MULTIPLY:
|
|
PushExStk(PopExStk()*PopExStk());
|
|
break;
|
|
case PCD_DIVIDE:
|
|
case PCD_MODULUS:
|
|
operand2 = PopExStk();
|
|
operand1 = PopExStk();
|
|
if (operand2 != 0)
|
|
{
|
|
PushExStk(pcd == PCD_DIVIDE ? operand1/operand2 : operand1%operand2);
|
|
}
|
|
else
|
|
{
|
|
ERR_Error(ERR_DIV_BY_ZERO_IN_CONST_EXPR, YES);
|
|
PushExStk(operand1);
|
|
}
|
|
break;
|
|
case PCD_EQ:
|
|
PushExStk(PopExStk() == PopExStk());
|
|
break;
|
|
case PCD_NE:
|
|
PushExStk(PopExStk() != PopExStk());
|
|
break;
|
|
case PCD_LT:
|
|
operand2 = PopExStk();
|
|
PushExStk(PopExStk() < operand2);
|
|
break;
|
|
case PCD_GT:
|
|
operand2 = PopExStk();
|
|
PushExStk(PopExStk() > operand2);
|
|
break;
|
|
case PCD_LE:
|
|
operand2 = PopExStk();
|
|
PushExStk(PopExStk() <= operand2);
|
|
break;
|
|
case PCD_GE:
|
|
operand2 = PopExStk();
|
|
PushExStk(PopExStk() >= operand2);
|
|
break;
|
|
case PCD_ANDLOGICAL:
|
|
operand2 = PopExStk();
|
|
operand1 = PopExStk();
|
|
PushExStk(operand1 && operand2);
|
|
break;
|
|
case PCD_ORLOGICAL:
|
|
operand2 = PopExStk();
|
|
operand1 = PopExStk();
|
|
PushExStk(operand1 || operand2);
|
|
break;
|
|
case PCD_ANDBITWISE:
|
|
PushExStk(PopExStk()&PopExStk());
|
|
break;
|
|
case PCD_ORBITWISE:
|
|
PushExStk(PopExStk()|PopExStk());
|
|
break;
|
|
case PCD_EORBITWISE:
|
|
PushExStk(PopExStk()^PopExStk());
|
|
break;
|
|
case PCD_NEGATELOGICAL:
|
|
PushExStk(!PopExStk());
|
|
break;
|
|
case PCD_NEGATEBINARY:
|
|
PushExStk(~PopExStk());
|
|
break;
|
|
case PCD_LSHIFT:
|
|
operand2 = PopExStk();
|
|
PushExStk(PopExStk()<<operand2);
|
|
break;
|
|
case PCD_RSHIFT:
|
|
operand2 = PopExStk();
|
|
PushExStk(PopExStk()>>operand2);
|
|
break;
|
|
case PCD_UNARYMINUS:
|
|
PushExStk(-PopExStk());
|
|
break;
|
|
default:
|
|
ERR_Exit(ERR_UNKNOWN_CONST_EXPR_PCD, YES);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// PushExStk
|
|
//
|
|
//==========================================================================
|
|
|
|
static void PushExStk(int value)
|
|
{
|
|
if(ExprStackIndex == EXPR_STACK_DEPTH)
|
|
{
|
|
ERR_Exit(ERR_EXPR_STACK_OVERFLOW, YES);
|
|
}
|
|
ExprStack[ExprStackIndex++] = value;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// PopExStk
|
|
//
|
|
//==========================================================================
|
|
|
|
static int PopExStk(void)
|
|
{
|
|
if(ExprStackIndex < 1)
|
|
{
|
|
ERR_Error(ERR_EXPR_STACK_EMPTY, YES);
|
|
return 0;
|
|
}
|
|
return ExprStack[--ExprStackIndex];
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// TokenToPCD
|
|
//
|
|
//==========================================================================
|
|
|
|
static pcd_t TokenToPCD(tokenType_t token)
|
|
{
|
|
int i;
|
|
static struct
|
|
{
|
|
tokenType_t token;
|
|
pcd_t pcd;
|
|
} operatorLookup[] =
|
|
{
|
|
{ TK_ORLOGICAL, PCD_ORLOGICAL },
|
|
{ TK_ANDLOGICAL, PCD_ANDLOGICAL },
|
|
{ TK_ORBITWISE, PCD_ORBITWISE },
|
|
{ TK_EORBITWISE, PCD_EORBITWISE },
|
|
{ TK_ANDBITWISE, PCD_ANDBITWISE },
|
|
{ TK_EQ, PCD_EQ },
|
|
{ TK_NE, PCD_NE },
|
|
{ TK_LT, PCD_LT },
|
|
{ TK_LE, PCD_LE },
|
|
{ TK_GT, PCD_GT },
|
|
{ TK_GE, PCD_GE },
|
|
{ TK_LSHIFT, PCD_LSHIFT },
|
|
{ TK_RSHIFT, PCD_RSHIFT },
|
|
{ TK_PLUS, PCD_ADD },
|
|
{ TK_MINUS, PCD_SUBTRACT },
|
|
{ TK_ASTERISK, PCD_MULTIPLY },
|
|
{ TK_SLASH, PCD_DIVIDE },
|
|
{ TK_PERCENT, PCD_MODULUS },
|
|
{ TK_NONE, PCD_NOP }
|
|
};
|
|
|
|
for(i = 0; operatorLookup[i].token != TK_NONE; i++)
|
|
{
|
|
if(operatorLookup[i].token == token)
|
|
{
|
|
return operatorLookup[i].pcd;
|
|
}
|
|
}
|
|
return PCD_NOP;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// GetPushVarPCD
|
|
//
|
|
//==========================================================================
|
|
|
|
static pcd_t GetPushVarPCD(symbolType_t symType)
|
|
{
|
|
switch(symType)
|
|
{
|
|
case SY_SCRIPTVAR:
|
|
return PCD_PUSHSCRIPTVAR;
|
|
case SY_MAPVAR:
|
|
return PCD_PUSHMAPVAR;
|
|
case SY_WORLDVAR:
|
|
return PCD_PUSHWORLDVAR;
|
|
case SY_GLOBALVAR:
|
|
return PCD_PUSHGLOBALVAR;
|
|
case SY_SCRIPTARRAY:
|
|
return PCD_PUSHSCRIPTARRAY;
|
|
case SY_MAPARRAY:
|
|
return PCD_PUSHMAPARRAY;
|
|
case SY_WORLDARRAY:
|
|
return PCD_PUSHWORLDARRAY;
|
|
case SY_GLOBALARRAY:
|
|
return PCD_PUSHGLOBALARRAY;
|
|
default:
|
|
break;
|
|
}
|
|
return PCD_NOP;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// GetIncDecPCD
|
|
//
|
|
//==========================================================================
|
|
|
|
static pcd_t GetIncDecPCD(tokenType_t token, symbolType_t symbol)
|
|
{
|
|
int i;
|
|
static struct
|
|
{
|
|
tokenType_t token;
|
|
symbolType_t symbol;
|
|
pcd_t pcd;
|
|
} incDecLookup[] =
|
|
{
|
|
{ TK_INC, SY_SCRIPTVAR, PCD_INCSCRIPTVAR },
|
|
{ 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 },
|
|
|
|
{ TK_DEC, SY_SCRIPTVAR, PCD_DECSCRIPTVAR },
|
|
{ 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 },
|
|
|
|
{ TK_NONE, SY_DUMMY, PCD_NOP }
|
|
};
|
|
|
|
for(i = 0; incDecLookup[i].token != TK_NONE; i++)
|
|
{
|
|
if(incDecLookup[i].token == token
|
|
&& incDecLookup[i].symbol == symbol)
|
|
{
|
|
return incDecLookup[i].pcd;
|
|
}
|
|
}
|
|
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, MAX_ARRAY_DIMS*sizeof(dims[0]));
|
|
|
|
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
|
|
//
|
|
//==========================================================================
|
|
|
|
static void ParseArrayIndices(symbolNode_t *sym, int requiredIndices)
|
|
{
|
|
boolean warned = NO;
|
|
int i;
|
|
|
|
if(requiredIndices > 0)
|
|
{
|
|
TK_TokenMustBe(TK_LBRACKET, ERR_MISSING_LBRACKET);
|
|
}
|
|
i = 0;
|
|
while(tk_Token == TK_LBRACKET)
|
|
{
|
|
TK_NextToken();
|
|
if(((sym->type == SY_MAPARRAY || sym->type == SY_SCRIPTARRAY) && i == requiredIndices) ||
|
|
(sym->type != SY_MAPARRAY && sym->type != SY_SCRIPTARRAY && i > 0))
|
|
{
|
|
if (!warned)
|
|
{
|
|
warned = YES;
|
|
if(sym->info.array.ndim == requiredIndices)
|
|
{
|
|
ERR_Error(ERR_TOO_MANY_DIM_USED, YES,
|
|
sym->name, sym->info.array.ndim);
|
|
}
|
|
else
|
|
{
|
|
ERR_Error(ERR_NOT_A_CHAR_ARRAY, YES, sym->name,
|
|
sym->info.array.ndim, requiredIndices);
|
|
}
|
|
}
|
|
}
|
|
EvalExpression();
|
|
if(i < sym->info.array.ndim - 1 && sym->info.array.dimensions[i] > 1)
|
|
{
|
|
PC_AppendPushVal(sym->info.array.dimensions[i]);
|
|
PC_AppendCmd(PCD_MULTIPLY);
|
|
}
|
|
if(i > 0)
|
|
{
|
|
PC_AppendCmd(PCD_ADD);
|
|
}
|
|
i++;
|
|
TK_TokenMustBe(TK_RBRACKET, ERR_MISSING_RBRACKET);
|
|
TK_NextToken();
|
|
}
|
|
if(i < requiredIndices)
|
|
{
|
|
ERR_Error(ERR_TOO_FEW_DIM_USED, YES,
|
|
sym->name, requiredIndices - i);
|
|
}
|
|
// if there were unspecified indices, multiply the offset by their sizes [JB]
|
|
if(requiredIndices < sym->info.array.ndim - 1)
|
|
{
|
|
int i, mult = 1;
|
|
for(i = 0; i < sym->info.array.ndim - requiredIndices - 1; ++i)
|
|
{
|
|
mult *= sym->info.array.dimensions[sym->info.array.ndim - 2 - i];
|
|
}
|
|
if(mult > 1)
|
|
{
|
|
PC_AppendPushVal(mult);
|
|
PC_AppendCmd(PCD_MULTIPLY);
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ProcessArrayLevel
|
|
//
|
|
//==========================================================================
|
|
|
|
static void ProcessArrayLevel(int level, int *entry, int ndim,
|
|
int dims[MAX_ARRAY_DIMS], int muls[MAX_ARRAY_DIMS], char *name)
|
|
{
|
|
int warned_too_many = NO;
|
|
int i;
|
|
|
|
for(i = 0; ; ++i)
|
|
{
|
|
if(tk_Token == TK_COMMA)
|
|
{
|
|
entry += 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();
|
|
entry++;
|
|
}
|
|
else
|
|
{
|
|
int val;
|
|
|
|
if (i >= dims[level - 1] && !warned_too_many)
|
|
{
|
|
warned_too_many = YES;
|
|
ERR_Error(ERR_TOO_MANY_ARRAY_INIT, YES);
|
|
}
|
|
val = EvalConstExpression();
|
|
ArrayHasStrings |= pa_ConstExprIsString;
|
|
if (i < dims[level - 1])
|
|
{
|
|
entry[i] = val;
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
// Allow execution to continue without stray memory access
|
|
entry -= muls[level-1];
|
|
}
|
|
TK_TokenMustBe(TK_LBRACE, ERR_MISSING_LBRACE_ARR);
|
|
TK_NextToken();
|
|
ProcessArrayLevel(level+1, entry, ndim, dims, muls, name);
|
|
assert(level > 0);
|
|
entry += 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();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// InitializeArray
|
|
//
|
|
//==========================================================================
|
|
|
|
static void InitializeArray(symbolNode_t *sym, int dims[MAX_ARRAY_DIMS], int size)
|
|
{
|
|
static int *entries = NULL;
|
|
static int lastsize = -1;
|
|
|
|
if(lastsize < size)
|
|
{
|
|
entries = MS_Realloc(entries, sizeof(int)*size, ERR_OUT_OF_MEMORY);
|
|
lastsize = size;
|
|
}
|
|
memset(entries, 0, sizeof(int)*size);
|
|
|
|
TK_NextTokenMustBe(TK_LBRACE, ERR_MISSING_LBRACE_ARR);
|
|
TK_NextToken();
|
|
ArrayHasStrings = NO;
|
|
ProcessArrayLevel(1, entries, sym->info.array.ndim, dims,
|
|
sym->info.array.dimensions, sym->name);
|
|
if(ImportMode != IMPORT_Importing)
|
|
{
|
|
PC_InitArray(sym->info.array.index, entries, ArrayHasStrings);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ProcessScriptArrayLevel
|
|
//
|
|
//==========================================================================
|
|
|
|
static void ProcessScriptArrayLevel(int level, int loc, symbolNode_t *sym, int ndim,
|
|
int dims[MAX_ARRAY_DIMS], int muls[MAX_ARRAY_DIMS], char *name)
|
|
{
|
|
int warned_too_many = NO;
|
|
int i;
|
|
|
|
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, loc, 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])
|
|
{
|
|
TK_NextTokenMustBe(TK_LBRACE, ERR_MISSING_LBRACE_ARR);
|
|
TK_NextToken();
|
|
ArrayHasStrings = NO;
|
|
ProcessScriptArrayLevel(1, 0, sym, sym->info.array.ndim, dims,
|
|
sym->info.array.dimensions, sym->name);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// DemandSymbol
|
|
//
|
|
//==========================================================================
|
|
|
|
static symbolNode_t *DemandSymbol(char *name)
|
|
{
|
|
symbolNode_t *sym;
|
|
|
|
if((sym = SY_Find(name)) == NULL)
|
|
{
|
|
ERR_Exit(ERR_UNKNOWN_IDENTIFIER, YES, name);
|
|
}
|
|
return sym;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// SpeculateSymbol
|
|
//
|
|
//==========================================================================
|
|
|
|
static symbolNode_t *SpeculateSymbol(char *name, boolean hasReturn)
|
|
{
|
|
symbolNode_t *sym;
|
|
|
|
sym = SY_Find(name);
|
|
if(sym == NULL)
|
|
{
|
|
char name[MAX_IDENTIFIER_LENGTH];
|
|
|
|
strcpy (name, tk_String);
|
|
TK_NextToken();
|
|
if(tk_Token == TK_LPAREN)
|
|
{ // Looks like a function call
|
|
sym = SpeculateFunction(name, hasReturn);
|
|
TK_Undo();
|
|
}
|
|
else
|
|
{
|
|
ERR_Exit(ERR_UNKNOWN_IDENTIFIER, YES, name);
|
|
}
|
|
}
|
|
return sym;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// SpeculateFunction
|
|
//
|
|
// Add a temporary symbol for a function that is used before it is defined.
|
|
//
|
|
//==========================================================================
|
|
|
|
static symbolNode_t *SpeculateFunction(const char *name, boolean hasReturn)
|
|
{
|
|
symbolNode_t *sym;
|
|
|
|
MS_Message(MSG_DEBUG, "---- SpeculateFunction %s ----\n", name);
|
|
sym = SY_InsertGlobal(tk_String, SY_SCRIPTFUNC);
|
|
sym->info.scriptFunc.predefined = YES;
|
|
sym->info.scriptFunc.hasReturnValue = hasReturn;
|
|
sym->info.scriptFunc.sourceLine = tk_Line;
|
|
sym->info.scriptFunc.sourceName = tk_SourceName;
|
|
sym->info.scriptFunc.argCount = -1;
|
|
sym->info.scriptFunc.funcNumber = 0;
|
|
return sym;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// UnspeculateFunction
|
|
//
|
|
// Fills in function calls that were made before this function was defined.
|
|
//
|
|
//==========================================================================
|
|
|
|
static void UnspeculateFunction(symbolNode_t *sym)
|
|
{
|
|
prefunc_t *fillin;
|
|
prefunc_t **prev;
|
|
|
|
prev = &FillinFunctions;
|
|
fillin = FillinFunctions;
|
|
while(fillin != NULL)
|
|
{
|
|
prefunc_t *next = fillin->next;
|
|
if(fillin->sym == sym)
|
|
{
|
|
if(fillin->argcount != sym->info.scriptFunc.argCount)
|
|
{
|
|
ERR_ErrorAt(fillin->source, fillin->line);
|
|
ERR_Error(ERR_FUNC_ARGUMENT_COUNT, YES, sym->name,
|
|
sym->info.scriptFunc.argCount,
|
|
sym->info.scriptFunc.argCount == 1 ? "" : "s");
|
|
}
|
|
|
|
if(pc_NoShrink)
|
|
{
|
|
PC_WriteInt(sym->info.scriptFunc.funcNumber, fillin->address);
|
|
}
|
|
else
|
|
{
|
|
PC_WriteByte((U_BYTE)sym->info.scriptFunc.funcNumber, fillin->address);
|
|
}
|
|
if(FillinFunctionsLatest == &fillin->next)
|
|
{
|
|
FillinFunctionsLatest = prev;
|
|
}
|
|
free (fillin);
|
|
*prev = next;
|
|
}
|
|
else
|
|
{
|
|
prev = &fillin->next;
|
|
}
|
|
fillin = next;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// AddScriptFuncRef
|
|
//
|
|
//==========================================================================
|
|
|
|
static void AddScriptFuncRef(symbolNode_t *sym, int address, int argcount)
|
|
{
|
|
prefunc_t *fillin = MS_Alloc(sizeof(*fillin), ERR_OUT_OF_MEMORY);
|
|
fillin->next = NULL;
|
|
fillin->sym = sym;
|
|
fillin->address = address;
|
|
fillin->argcount = argcount;
|
|
fillin->line = tk_Line;
|
|
fillin->source = tk_SourceName;
|
|
*FillinFunctionsLatest = fillin;
|
|
FillinFunctionsLatest = &fillin->next;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Check for undefined functions
|
|
//
|
|
//==========================================================================
|
|
|
|
static void CheckForUndefinedFunctions(void)
|
|
{
|
|
prefunc_t *fillin = FillinFunctions;
|
|
|
|
while(fillin != NULL)
|
|
{
|
|
ERR_ErrorAt(fillin->source, fillin->line);
|
|
ERR_Error(ERR_UNDEFINED_FUNC, YES, fillin->sym->name);
|
|
fillin = fillin->next;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// SkipBraceBlock
|
|
//
|
|
// If depth is 0, it scans for the first { and then starts going from there.
|
|
// At exit, the terminating } is left as the current token.
|
|
//
|
|
//==========================================================================
|
|
|
|
void SkipBraceBlock(int depth)
|
|
{
|
|
if (depth == 0)
|
|
{
|
|
// Find first {
|
|
while(tk_Token != TK_LBRACE)
|
|
{
|
|
if(tk_Token == TK_EOF)
|
|
{
|
|
ERR_Exit(ERR_EOF, YES, NULL);
|
|
}
|
|
TK_NextToken();
|
|
}
|
|
depth = 1;
|
|
}
|
|
// Match it with a }
|
|
do
|
|
{
|
|
TK_NextToken();
|
|
if(tk_Token == TK_EOF)
|
|
{
|
|
ERR_Exit(ERR_EOF, YES, NULL);
|
|
}
|
|
else if (tk_Token == TK_LBRACE)
|
|
{
|
|
depth++;
|
|
}
|
|
else if (tk_Token == TK_RBRACE)
|
|
{
|
|
depth--;
|
|
}
|
|
} while (depth > 0);
|
|
}
|