Files
firestorm/Gameleap/code/mw4/Code/MW4/Ablstmt.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

578 lines
16 KiB
C++

//***************************************************************************
//
// ABLSTMT.CPP
//
//***************************************************************************
#include "MW4Headers.hpp"
#ifndef ABLGEN_H
#include "ablgen.h"
#endif
#ifndef ABLERR_H
#include "ablerr.h"
#endif
#ifndef ABLSCAN_H
#include "ablscan.h"
#endif
#ifndef ABLSYMT_H
#include "ablsymt.h"
#endif
#ifndef ABLPARSE_H
#include "ablparse.h"
#endif
#ifndef ABLEXEC_H
#include "ablexec.h"
#endif
#ifndef ABLHEAP_H
#include "ablheap.h"
#endif
#include "ablfuncs.hpp"
namespace ABL
{
//***************************************************************************
extern TokenCodeType curToken;
extern char tokenString[];
extern char wordString[];
extern Literal curLiteral;
extern TokenCodeType statementStartList[];
extern TokenCodeType statementEndList[];
extern SymTableNodePtr symTableDisplay[];
extern long level;
extern long FSMLevel;
extern char* codeBuffer;
extern TypePtr IntegerTypePtr;
extern TypePtr RealTypePtr;
extern TypePtr BooleanTypePtr;
extern TypePtr CharTypePtr;
extern Type DummyType;
extern SymTableNodePtr CurRoutineIdPtr;
extern UserHeapPtr AblStackHeap;
extern SymTableNodePtr SymTableDisplay[MAX_NESTING_LEVEL];
extern bool AssertEnabled;
extern bool PrintEnabled;
extern bool StringFunctionsEnabled;
extern bool Crunch;
extern bool parsingFSM;
extern ABLModulePtr CurLibrary;
//--------
// GLOBALS
TokenCodeType FollowSwitchExpressionList[] = {
TKN_CASE,
TKN_SEMICOLON,
TKN_NONE
};
TokenCodeType FollowCaseLabelList[] = {
TKN_COLON,
TKN_SEMICOLON,
TKN_NONE
};
TokenCodeType CaseLabelStartList[] = {
TKN_IDENTIFIER,
TKN_NUMBER,
TKN_PLUS,
TKN_MINUS,
TKN_STRING,
TKN_NONE
};
//***************************************************************************
void assignmentStatement (SymTableNodePtr varIdPtr) {
//-----------------------------------
// Grab the variable we're setting...
TypePtr varType = variable(varIdPtr);
ifTokenGetElseError(TKN_EQUAL, ABL_ERR_SYNTAX_MISSING_EQUAL);
//---------------------------------------------------------
// Now, get the expression we're setting the variable to...
TypePtr exprType = expression();
//----------------------------------------
// They better be assignment compatible...
if (!isAssignTypeCompatible(varType, exprType))
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_ASSIGNMENT);
}
//***************************************************************************
void repeatStatement (void) {
getToken();
if (curToken != TKN_UNTIL) {
do {
statement();
while (curToken == TKN_SEMICOLON)
getToken();
if (curToken == TKN_UNTIL)
break;
} while (tokenIn(statementStartList));
}
ifTokenGetElseError(TKN_UNTIL, ABL_ERR_SYNTAX_MISSING_UNTIL);
TypePtr exprType = expression();
if (exprType != BooleanTypePtr)
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
}
//***************************************************************************
void whileStatement (void) {
// NEW STYLE, using endwhile keyword...
getToken();
char* loopEndLocation = crunchAddressMarker(NULL);
TypePtr exprType = expression();
if (exprType != BooleanTypePtr)
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
//---------------------------------------
// Let's not use a DO keyword, for now...
ifTokenGetElseError(TKN_DO, ABL_ERR_SYNTAX_MISSING_DO);
if (curToken != TKN_END_WHILE)
do {
statement();
while (curToken == TKN_SEMICOLON)
getToken();
if (curToken == TKN_END_WHILE)
break;
} while (tokenIn(statementStartList));
ifTokenGetElseError(TKN_END_WHILE, ABL_ERR_SYNTAX_MISSING_END_WHILE);
fixupAddressMarker(loopEndLocation);
}
//***************************************************************************
void ifStatement (void) {
getToken();
char* falseLocation = crunchAddressMarker(NULL);
TypePtr exprType = expression();
if (exprType != BooleanTypePtr)
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
ifTokenGetElseError(TKN_THEN, ABL_ERR_SYNTAX_MISSING_THEN);
if ((curToken != TKN_END_IF) && (curToken != TKN_ELSE))
do {
statement();
while (curToken == TKN_SEMICOLON)
getToken();
if ((curToken == TKN_END_IF) || (curToken == TKN_ELSE))
break;
} while (tokenIn(statementStartList));
fixupAddressMarker(falseLocation);
//-----------------------------
// ELSE branch, if necessary...
if (curToken == TKN_ELSE) {
getToken();
char* ifEndLocation = crunchAddressMarker(NULL);
if (curToken != TKN_END_IF)
do {
statement();
while (curToken == TKN_SEMICOLON)
getToken();
if (curToken == TKN_END_IF)
break;
} while (tokenIn(statementStartList));
fixupAddressMarker(ifEndLocation);
}
ifTokenGetElseError(TKN_END_IF, ABL_ERR_SYNTAX_MISSING_END_IF);
}
//***************************************************************************
void forStatement (void) {
getToken();
char* loopEndLocation = crunchAddressMarker(NULL);
TypePtr forType = NULL;
if (curToken == TKN_IDENTIFIER) {
SymTableNodePtr forIdPtr = NULL;
searchAndFindAllSymTables(forIdPtr);
crunchSymTableNodePtr(forIdPtr);
if (/*(forIdPtr->level != level) ||*/ (forIdPtr->defn.key != DFN_VAR))
syntaxError(ABL_ERR_SYNTAX_INVALID_FOR_CONTROL);
forType = baseType((TypePtr)(forIdPtr->typePtr));
getToken();
//------------------------------------------------------------------
// If we end up adding a CHAR type, this line needs to be changed...
if ((forType != IntegerTypePtr) && /*(forType != CharTypePtr) &&*/ (forType->form != FRM_ENUM))
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
}
else {
syntaxError(ABL_ERR_SYNTAX_MISSING_IDENTIFIER);
forType = &DummyType;
}
ifTokenGetElseError(TKN_EQUAL, ABL_ERR_SYNTAX_MISSING_EQUAL);
TypePtr exprType = expression();
if (!isAssignTypeCompatible(forType, exprType))
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
if (curToken == TKN_TO)
getToken();
else
syntaxError(ABL_ERR_SYNTAX_MISSING_TO);
exprType = expression();
if (!isAssignTypeCompatible(forType, exprType))
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
//-----------------------------------------
// For now, let's use the DO keyword...
ifTokenGetElseError(TKN_DO, ABL_ERR_SYNTAX_MISSING_DO);
if (curToken != TKN_END_FOR)
do {
statement();
while (curToken == TKN_SEMICOLON)
getToken();
if (curToken == TKN_END_FOR)
break;
} while (tokenIn(statementStartList));
ifTokenGetElseError(TKN_END_FOR, ABL_ERR_SYNTAX_MISSING_END_FOR);
fixupAddressMarker(loopEndLocation);
}
//***************************************************************************
TypePtr caseLabel (CaseItemPtr& caseItemHead, CaseItemPtr& caseItemTail, long& caseLabelCount) {
CaseItemPtr newCaseItem = (CaseItemPtr)AblStackHeap->Malloc(sizeof(CaseItem));
if (!newCaseItem)
ABL_Fatal(0, " ABL: Unable to AblStackHeap->malloc case item ");
if (caseItemHead) {
caseItemTail->next = newCaseItem;
caseItemTail = newCaseItem;
}
else
caseItemHead = caseItemTail = newCaseItem;
newCaseItem->next = NULL;
caseLabelCount++;
TokenCodeType sign = TKN_PLUS;
bool sawSign = false;
if ((curToken == TKN_PLUS) || (curToken == TKN_MINUS)) {
sign = curToken;
sawSign = true;
getToken();
}
if (curToken == TKN_NUMBER) {
SymTableNodePtr thisNode = searchSymTable(tokenString, SymTableDisplay[1]);
if (!thisNode)
thisNode = enterSymTable(tokenString, &SymTableDisplay[1]);
crunchSymTableNodePtr(thisNode);
if (curLiteral.type == LIT_INTEGER)
newCaseItem->labelValue = (sign == TKN_PLUS) ? curLiteral.value.integer : -curLiteral.value.integer;
else
syntaxError(ABL_ERR_SYNTAX_INVALID_CONSTANT);
return(IntegerTypePtr);
}
else if (curToken == TKN_IDENTIFIER) {
SymTableNodePtr idPtr;
searchAllSymTables(idPtr);
crunchSymTableNodePtr(idPtr);
if (!idPtr) {
syntaxError(ABL_ERR_SYNTAX_UNDEFINED_IDENTIFIER);
return(&DummyType);
}
else if (idPtr->defn.key != DFN_CONST) {
syntaxError(ABL_ERR_SYNTAX_NOT_A_CONSTANT_IDENTIFIER);
return(&DummyType);
}
else if (idPtr->typePtr == IntegerTypePtr) {
newCaseItem->labelValue = (sign == TKN_PLUS ? idPtr->defn.info.constant.value.integer : -idPtr->defn.info.constant.value.integer);
return(IntegerTypePtr);
}
else if (idPtr->typePtr == CharTypePtr) {
if (sawSign)
syntaxError(ABL_ERR_SYNTAX_INVALID_CONSTANT);
newCaseItem->labelValue = idPtr->defn.info.constant.value.character;
return(CharTypePtr);
}
else if (idPtr->typePtr->form == FRM_ENUM) {
if (sawSign)
syntaxError(ABL_ERR_SYNTAX_INVALID_CONSTANT);
newCaseItem->labelValue = idPtr->defn.info.constant.value.integer;
return(idPtr->typePtr);
}
else
return(&DummyType);
}
else if (curToken == TKN_STRING) {
// STRING/CHAR TYPE...
}
else {
syntaxError(ABL_ERR_SYNTAX_INVALID_CONSTANT);
return(&DummyType);
}
return(&DummyType);
}
//---------------------------------------------------------------------------
void caseBranch (CaseItemPtr& caseItemHead, CaseItemPtr& caseItemTail, long& caseLabelCount, TypePtr expressionType) {
//static CaseItemPtr oldCaseItemTail = NULL;
CaseItemPtr oldCaseItemTail = caseItemTail;
bool anotherLabel;
do {
TypePtr labelType = caseLabel(caseItemHead, caseItemTail, caseLabelCount);
if (expressionType != labelType)
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
getToken();
if (curToken == TKN_COMMA) {
getToken();
if (tokenIn(CaseLabelStartList))
anotherLabel = true;
else {
syntaxError(ABL_ERR_SYNTAX_MISSING_CONSTANT);
anotherLabel = false;
}
}
else
anotherLabel = false;
} while (anotherLabel);
//--------------
// Error sync...
synchronize(FollowCaseLabelList, statementStartList, NULL);
ifTokenGetElseError(TKN_COLON, ABL_ERR_SYNTAX_MISSING_COLON);
//-----------------------------------------------------------------
// Fill in the branch location for each CaseItem for this branch...
CaseItemPtr caseItem = (!oldCaseItemTail ? caseItemHead : oldCaseItemTail->next);
//oldCaseItemTail = CaseItemTail;
while (caseItem) {
caseItem->branchLocation = codeBufferPtr;
caseItem = caseItem->next;
}
if (curToken != TKN_END_CASE)
do {
statement();
while (curToken == TKN_SEMICOLON)
getToken();
if (curToken == TKN_END_CASE)
break;
} while (tokenIn(statementStartList));
ifTokenGetElseError(TKN_END_CASE, ABL_ERR_SYNTAX_MISSING_END_CASE);
ifTokenGetElseError(TKN_SEMICOLON, ABL_ERR_SYNTAX_MISSING_SEMICOLON);
}
//---------------------------------------------------------------------------
void switchStatement (void) {
//-------------------------
// Init the branch table...
getToken();
char* branchTableLocation = crunchAddressMarker(NULL);
CaseItemPtr caseItemHead = NULL;
CaseItemPtr caseItemTail = NULL;
long caseLabelCount = 0;
//CaseItemHead = CaseItemTail = NULL;
//CaseLabelCount = 0;
TypePtr expressionType = expression();
//-----------------------------------------------------------------------------
// NOTE: If we have subranges in ABL, we'll have to check in the following line
// for a subrange, as well...
if (((expressionType->form != FRM_SCALAR) && (expressionType->form != FRM_ENUM)) || (expressionType == RealTypePtr))
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
synchronize(FollowSwitchExpressionList, NULL, NULL);
//----------------------------
// Process each CASE branch...
bool moreBranches = (curToken == TKN_CASE);
char* caseEndChain = NULL;
while (moreBranches) {
getToken();
if (tokenIn(CaseLabelStartList))
caseBranch(caseItemHead, caseItemTail, caseLabelCount, expressionType);
//---------------------------------------------------
// Link another address marker at the end of the CASE
// branch to point to the end of the CASE block...
caseEndChain = crunchAddressMarker(caseEndChain);
moreBranches = (curToken == TKN_CASE);
}
//if (curToken == TKN_DEFAULT) {
//}
//-------------------------
// Emit the branch table...
fixupAddressMarker(branchTableLocation);
crunchInteger(caseLabelCount);
CaseItemPtr caseItem = caseItemHead;
while (caseItem) {
crunchInteger(caseItem->labelValue);
crunchOffset(caseItem->branchLocation);
CaseItemPtr nextCaseItem = caseItem->next;
AblStackHeap->Free(caseItem);
caseItem = nextCaseItem;
}
ifTokenGetElseError(TKN_END_SWITCH, ABL_ERR_SYNTAX_MISSING_END_SWITCH);
//--------------------------------------------
// Patch up the case branch address markers...
while (caseEndChain)
caseEndChain = fixupAddressMarker(caseEndChain);
}
//***************************************************************************
void statement (void) {
//-------------------------------------------------------------------
// NOTE: Since we currently don't support generic BEGIN/END (compound
// statement) blocks...
if ((curToken != TKN_CODE) /*&& (curToken != TKN_BEGIN)*/)
crunchStatementMarker();
switch (curToken) {
case TKN_IDENTIFIER: {
SymTableNodePtr IdPtr = NULL;
//--------------------------------------------------------------
// First, do we have an assignment statement or a function call?
searchAndFindAllSymTables(IdPtr);
if ((IdPtr->defn.key == DFN_FUNCTION)/* || (IdPtr->defn.key == DFN_MODULE)*/) {
RoutineKey key = IdPtr->defn.info.routine.key;
if ((key == RTN_ASSERT) || (key == RTN_PRINT) || (key == RTN_CONCAT)) {
bool uncrunch = ((key == RTN_ASSERT) && !AssertEnabled) ||
((key == RTN_PRINT) && !PrintEnabled) ||
((key == RTN_CONCAT) && !StringFunctionsEnabled);
if (uncrunch) {
uncrunchStatementMarker();
Crunch = false;
}
}
crunchSymTableNodePtr(IdPtr);
getToken();
SymTableNodePtr thisRoutineIdPtr = CurRoutineIdPtr;
routineCall(IdPtr, 1);
CurRoutineIdPtr = thisRoutineIdPtr;
Crunch = true;
}
else
assignmentStatement(IdPtr);
}
break;
case TKN_REPEAT:
repeatStatement();
break;
case TKN_WHILE:
whileStatement();
break;
case TKN_IF:
ifStatement();
break;
case TKN_FOR:
forStatement();
break;
case TKN_SWITCH:
switchStatement();
break;
case TKN_TRANS:
if (!parsingFSM)
{
syntaxError (ABL_ERR_SYNTAX_TRANS_IN_NONFSM);
break;
}
SymTableNodePtr IdPtr = NULL;
Crunch = false;
getToken ();
Crunch = true;
Verify (FSMLevel != 0);
IdPtr = searchSymTable(wordString, SymTableDisplay[FSMLevel]);
// searchLocalSymTable(IdPtr);
if (IdPtr == NULL)
{
Verify (FSMLevel != 0);
IdPtr = enterSymTable(wordString, &SymTableDisplay[FSMLevel]);
// enterLocalSymTable(IdPtr);
IdPtr->defn.key = DFN_STATE;
IdPtr->defn.info.routine.key = RTN_FORWARD;
IdPtr->defn.info.routine.paramCount = 0;
IdPtr->defn.info.routine.totalParamSize = 0;
IdPtr->defn.info.routine.totalLocalSize = 0;
IdPtr->defn.info.routine.params = NULL;
IdPtr->defn.info.routine.locals = NULL;
IdPtr->defn.info.routine.localSymTable = NULL;
IdPtr->defn.info.routine.codeSegment = NULL;
IdPtr->library = CurLibrary;
IdPtr->typePtr = &DummyType;
IdPtr->labelIndex = 0;
IdPtr->level = (unsigned char) FSMLevel;
}
if (IdPtr->defn.key == DFN_STATE)
{
crunchSymTableNodePtr(IdPtr);
getToken ();
}
else
{
syntaxError (ABL_ERR_SYNTAX_TRANS_TO_NON_STATE);
}
break;
}
//---------------------------------------------------------------------
// Now, make sure the statement is closed off with the proper block end
// statement, if necessary (which is usually the case :).
synchronize(statementEndList, NULL, NULL);
if (tokenIn(statementStartList))
syntaxError(ABL_ERR_SYNTAX_MISSING_SEMICOLON);
}
//***************************************************************************
}