Files
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

516 lines
14 KiB
C++

//***************************************************************************
//
// ABLEXPR.CPP
//
//***************************************************************************
#include "MW4Headers.hpp"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#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
namespace ABL
{
//***************************************************************************
extern TokenCodeType curToken;
extern char tokenString[];
extern char wordString[];
extern Literal curLiteral;
extern SymTableNodePtr SymTableDisplay[];
extern long level;
extern TypePtr IntegerTypePtr, CharTypePtr, RealTypePtr, BooleanTypePtr;
extern Type DummyType;
extern TokenCodeType statementEndList[];
extern UserHeapPtr AblSymTableHeap;
//***************************************************************************
TokenCodeType relationalOperatorList[] = {
TKN_LT,
TKN_LE,
TKN_EQUALEQUAL,
TKN_NE,
TKN_GE,
TKN_GT,
TKN_NONE
};
TokenCodeType addOperatorList[] = {
TKN_PLUS,
TKN_MINUS,
TKN_OR,
TKN_NONE
};
TokenCodeType multiplyOperatorList[] = {
TKN_STAR,
TKN_FSLASH,
TKN_DIV, // we'll probably want to make this covered with FSLASH
TKN_MOD,
TKN_AND,
TKN_NONE
};
//***************************************************************************
// MISC
//***************************************************************************
/*
inline void ifTokenGet (TokenCodeType tokenCode) {
if (curToken == tokenCode)
getToken();
}
//***************************************************************************
inline void ifTokenGetElseError (TokenCodeType tokenCode, SyntaxErrorType errCode) {
if (curToken == tokenCode)
getToken();
else
syntaxError(errCode);
}
//***************************************************************************
*/
/*inline*/ TypePtr baseType (TypePtr thisType) {
//---------------------------------------------------------------------
// Since we don't have a sub-range type, block this out for now.
// However, if we decide to put a subrange type into ABL, the following
// three lines should be included...
//if (thisType->form == FRM_SUBRANGE)
// return(thisType->info.subrange.rangeTypePtr;
//else
return(thisType);
}
//***************************************************************************
inline bool integerOperands (TypePtr type1, TypePtr type2) {
return((type1 == IntegerTypePtr) && (type2 == IntegerTypePtr));
}
//***************************************************************************
inline bool realOperands (TypePtr type1, TypePtr type2) {
if (type1 == RealTypePtr)
return((type2 == RealTypePtr) || (type2 == IntegerTypePtr));
else if (type2 == RealTypePtr)
return(type1 == IntegerTypePtr);
else
return(false);
}
//***************************************************************************
inline bool booleanOperands (TypePtr type1, TypePtr type2) {
return((type1 == BooleanTypePtr) && (type2 == BooleanTypePtr));
}
//***************************************************************************
void checkRelationalOpTypes (TypePtr type1, TypePtr type2) {
if ((type1 == NULL) || (type2 == NULL))
{
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
return;
}
if ((type1 == type2) && ((type1->form == FRM_SCALAR) || (type1->form == FRM_ENUM)))
return;
if (((type1 == IntegerTypePtr) && (type2 == RealTypePtr)) ||
((type2 == IntegerTypePtr) && (type1 == RealTypePtr)))
return;
if ((type1->form == FRM_ARRAY) && (type2->form == FRM_ARRAY) &&
(type1->info.array.elementTypePtr == CharTypePtr) && (type2->info.array.elementTypePtr == CharTypePtr) &&
(type1->info.array.elementCount == type2->info.array.elementCount))
return;
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
}
//***************************************************************************
long isAssignTypeCompatible (TypePtr type1, TypePtr type2) {
type1 = baseType(type1);
type2 = baseType(type2);
if (type1 == type2)
return(1);
if ((type1 == RealTypePtr) && (type2 == IntegerTypePtr))
return(1);
if ((type1->form == FRM_ARRAY) && (type2->form == FRM_ARRAY) &&
(type1->info.array.elementTypePtr == CharTypePtr) && (type2->info.array.elementTypePtr == CharTypePtr) &&
(type1->info.array.elementCount >= type2->info.array.elementCount))
return(1);
return(0);
}
//***************************************************************************
// EXPRESSION routines
//***************************************************************************
TypePtr variable (SymTableNodePtr variableIdPtr) {
TypePtr typePtr = (TypePtr)(variableIdPtr->typePtr);
DefinitionType defnKey = variableIdPtr->defn.key;
crunchSymTableNodePtr(variableIdPtr);
switch (defnKey) {
case DFN_VAR:
case DFN_VALPARAM:
case DFN_REFPARAM:
case DFN_FUNCTION:
//case DFN_MODULE:
case DFN_UNDEFINED:
break;
default:
typePtr = &DummyType;
syntaxError(ABL_ERR_SYNTAX_INVALID_IDENTIFIER_USAGE);
}
getToken();
//---------------------------------------------------------------------
// There should not be a parameter list. However, if there is, parse it
// for error recovery...
if (curToken == TKN_LPAREN) {
syntaxError(ABL_ERR_SYNTAX_UNEXPECTED_TOKEN);
actualParamList(variableIdPtr, 0);
return(typePtr);
}
//--------------------------------------------------------------------
// Subscripts or field designators (when/if we support record types :)
while ((curToken == TKN_LBRACKET) || (curToken == TKN_PERIOD)) {
if (curToken == TKN_LBRACKET)
typePtr = arraySubscriptList(typePtr);
else
STOP(("666???"));
//typePtr = recordField(typePtr);
}
return(typePtr);
}
//***************************************************************************
TypePtr arraySubscriptList (TypePtr typePtr) {
TypePtr indexTypePtr = NULL;
TypePtr elementTypePtr = NULL;
TypePtr subscriptTypePtr = NULL;
do {
if (typePtr->form == FRM_ARRAY) {
indexTypePtr = typePtr->info.array.indexTypePtr;
elementTypePtr = typePtr->info.array.elementTypePtr;
getToken();
subscriptTypePtr = expression();
//-------------------------------------------------------------
// If the subscript expression isn't assignment type compatible
// with its corresponding subscript type, we're screwed...
if (!isAssignTypeCompatible(indexTypePtr, subscriptTypePtr))
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
typePtr = elementTypePtr;
}
else {
syntaxError(ABL_ERR_SYNTAX_TOO_MANY_SUBSCRIPTS);
while ((curToken != TKN_RBRACKET) && !tokenIn(statementEndList))
getToken();
}
} while (curToken == TKN_COMMA);
ifTokenGetElseError(TKN_RBRACKET, ABL_ERR_SYNTAX_MISSING_RBRACKET);
return(typePtr);
}
//***************************************************************************
TypePtr factor (void) {
TypePtr thisType = NULL;
switch (curToken) {
case TKN_IDENTIFIER: {
SymTableNodePtr IdPtr = NULL;
searchAndFindAllSymTables(IdPtr);
switch (IdPtr->defn.key) {
case DFN_FUNCTION:
crunchSymTableNodePtr(IdPtr);
getToken();
thisType = routineCall(IdPtr, 1);
break;
case DFN_CONST:
crunchSymTableNodePtr(IdPtr);
getToken();
thisType = (TypePtr)(IdPtr->typePtr);
break;
default:
thisType = (TypePtr)variable(IdPtr);
break;
}
}
break;
case TKN_NUMBER: {
SymTableNodePtr thisNode = searchSymTable(tokenString, SymTableDisplay[1]);
if (!thisNode)
//--------------------------------------------------------------------------
// NOTE: The following commented-out line is from MAK. However, I believe we
// want the ampersand, as inserted in the following line...
// thisNode = enterSymTable(tokenString, SymTableDisplay[1]);
thisNode = enterSymTable(tokenString, &SymTableDisplay[1]);
if (curLiteral.type == LIT_INTEGER) {
thisNode->typePtr = IntegerTypePtr;
thisType = (TypePtr)(thisNode->typePtr);
thisNode->defn.info.constant.value.integer = curLiteral.value.integer;
}
else {
thisNode->typePtr = RealTypePtr;
thisType = (TypePtr)(thisNode->typePtr);
thisNode->defn.info.constant.value.real = curLiteral.value.real;
}
crunchSymTableNodePtr(thisNode);
getToken();
}
break;
case TKN_STRING: {
long length = strlen(curLiteral.value.string);
SymTableNodePtr thisNode = searchSymTable(tokenString, SymTableDisplay[1]);
if (!thisNode)
//--------------------------------------------------------------------------
// NOTE: The following commented-out line is from MAK. However, I believe we
// want the ampersand, as inserted in the following line...
// thisNode = enterSymTable(tokenString, SymTableDisplay[1]);
thisNode = enterSymTable(tokenString, &SymTableDisplay[1]);
if (length == 1) {
thisNode->defn.info.constant.value.character = curLiteral.value.string[0];
thisType = CharTypePtr;
}
else {
thisNode->typePtr = thisType = makeStringType(length);
thisNode->info = (char*)AblSymTableHeap->Malloc(length + 1);
if (!thisNode->info)
ABL_Fatal(0, " ABL: Unable to AblSymTableHeap->malloc string literal ");
strcpy(thisNode->info, curLiteral.value.string);
}
crunchSymTableNodePtr(thisNode);
getToken();
}
break;
case TKN_NOT:
getToken();
thisType = factor();
break;
case TKN_LPAREN:
getToken();
thisType = expression();
ifTokenGetElseError(TKN_RPAREN, ABL_ERR_SYNTAX_MISSING_RPAREN);
break;
default:
syntaxError(ABL_ERR_SYNTAX_INVALID_EXPRESSION);
thisType = &DummyType;
break;
}
return(thisType);
}
//***************************************************************************
TypePtr term (void) {
//-------------------------
// Grab the first factor...
TypePtr resultType = factor();
//------------------------------------------------------------------
// Now, continue grabbing factors separated by multiply operators...
while (tokenIn(multiplyOperatorList)) {
TokenCodeType op = curToken;
resultType = baseType(resultType);
getToken();
TypePtr secondType = baseType(factor());
switch (op) {
case TKN_STAR:
if (integerOperands(resultType, secondType)) {
//---------------------------------------------------
// Both operands are integer, so result is integer...
resultType = IntegerTypePtr;
}
else if (realOperands(resultType, secondType)) {
//----------------------------------------------------
// Both real operands, or mixed (real and integer)...
resultType = RealTypePtr;
}
else {
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
resultType = &DummyType;
}
break;
case TKN_FSLASH:
if (integerOperands(resultType, secondType)) {
//---------------------------------------------------
// Both operands are integer, so result is integer...
resultType = IntegerTypePtr;
}
else if (realOperands(resultType, secondType)) {
//----------------------------------------------------
// Both real operands, or mixed (real and integer)...
resultType = RealTypePtr;
}
else {
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
resultType = &DummyType;
}
break;
case TKN_DIV:
case TKN_MOD:
//----------------------------------------------------------
// Both operands should be integer, and result is integer...
if (!integerOperands(resultType, secondType))
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
resultType = IntegerTypePtr;
break;
case TKN_AND:
if (!booleanOperands(resultType, secondType))
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
resultType = BooleanTypePtr;
break;
}
}
return(resultType);
}
//***************************************************************************
TypePtr simpleExpression (void) {
bool usedUnaryOp = false;
TokenCodeType unaryOp = TKN_PLUS;
if ((curToken == TKN_PLUS) || (curToken == TKN_MINUS)) {
unaryOp = curToken;
usedUnaryOp = true;
getToken();
}
//------------------------------------------------
// Grab the first term in the simple expression...
TypePtr resultType = term();
if (usedUnaryOp && (baseType(resultType) != IntegerTypePtr) && (resultType != RealTypePtr))
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
//---------------------------------------------------
// Continue to process all terms in the expression...
while (tokenIn(addOperatorList)) {
TokenCodeType op = curToken;
resultType = baseType(resultType);
getToken();
TypePtr secondType = baseType(term());
switch (op) {
case TKN_PLUS:
case TKN_MINUS:
if (integerOperands(resultType, secondType)) {
//---------------------------------------------------
// Both operands are integer, so result is integer...
resultType = IntegerTypePtr;
}
else if (realOperands(resultType, secondType)) {
//----------------------------------------------------
// Both real operands, or mixed (real and integer)...
resultType = RealTypePtr;
}
else {
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
resultType = &DummyType;
}
break;
case TKN_OR:
if (!booleanOperands(resultType, secondType))
syntaxError(ABL_ERR_SYNTAX_INCOMPATIBLE_TYPES);
resultType = BooleanTypePtr;
break;
}
}
return(resultType);
}
//***************************************************************************
TypePtr expression (void) {
//------------------------------------
// Grab the first simple expression...
TypePtr resultType = simpleExpression();
if (tokenIn(relationalOperatorList)) {
resultType = baseType(resultType);
//---------------------------------------
// Snatch the second simple expression...
getToken();
TypePtr secondType = baseType(simpleExpression());
checkRelationalOpTypes(resultType, secondType);
resultType = BooleanTypePtr;
}
return(resultType);
}
//***************************************************************************
}