Files
scummvm/engines/bagel/baglib/expression.cpp
T

741 lines
17 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "bagel/baglib/expression.h"
#include "bagel/baglib/master_win.h"
#include "bagel/baglib/storage_dev_win.h"
#include "bagel/baglib/sound_object.h"
#include "bagel/boflib/sound.h"
#include "bagel/bagel.h"
namespace Bagel {
CBagVar CBagExpression::m_xTempVar("CBagExpr::TempVar", "", false); // used as a default param
CBagExpression::CBagExpression(CBagExpression *pPrevExpr, bool bPrevNeg) {
m_bNegative = false;
m_xPrevExpression = pPrevExpr;
m_bPrevNegative = (byte)bPrevNeg;
}
CBagExpression::~CBagExpression() {
}
bool CBagExpression::Evaluate(CBagVar *xLHOper, CBagVar *xRHOper, OPERATION xOper, CBagVar &xResult) {
bool bRClocal = false;
// If the variable is named "RANDOM", generate a random number for its value
if (xLHOper->GetName() == "RANDOM")
xLHOper->SetValue(g_engine->getRandomNumber());
if (xRHOper->GetName() == "RANDOM")
xRHOper->SetValue(g_engine->getRandomNumber());
switch (xOper) {
case NONE:
break;
case ASSIGN:
bRClocal = OnAssign(xLHOper, xRHOper, xResult);
break;
case EQUAL:
bRClocal = OnEqual(xLHOper, xRHOper, xResult);
break;
case NOTEQUAL:
bRClocal = OnNotEqual(xLHOper, xRHOper, xResult);
break;
case LESSTHAN:
bRClocal = OnLessThan(xLHOper, xRHOper, xResult);
break;
case LESSTHANEQUAL:
bRClocal = OnLessThanEqual(xLHOper, xRHOper, xResult);
break;
case GREATERTHAN:
bRClocal = OnGreaterThan(xLHOper, xRHOper, xResult);
break;
case GREATERTHANEQUAL:
bRClocal = OnGreaterThanEqual(xLHOper, xRHOper, xResult);
break;
case PLUSASSIGN:
bRClocal = OnPlusAssign(xLHOper, xRHOper, xResult);
break;
case MINUSASSIGN:
bRClocal = OnMinusAssign(xLHOper, xRHOper, xResult);
break;
case PLUS:
bRClocal = OnPlus(xLHOper, xRHOper, xResult);
break;
case MINUS:
bRClocal = OnMinus(xLHOper, xRHOper, xResult);
break;
case MULTIPLY:
bRClocal = OnMultiply(xLHOper, xRHOper, xResult);
break;
case DIVIDE:
bRClocal = OnDivide(xLHOper, xRHOper, xResult);
break;
case MOD:
bRClocal = OnMod(xLHOper, xRHOper, xResult);
break;
case CONTAINS:
bRClocal = OnContains(xLHOper, xRHOper, xResult);
break;
case HAS:
bRClocal = OnHas(xLHOper, xRHOper, xResult);
break;
case STATUS:
bRClocal = OnStatus(xLHOper, xRHOper, xResult);
break;
default:
break;
}
return bRClocal;
}
CBagVar *CBagExpression::GetVariable(int nPos) {
CBagVar *pVar = m_cVarList.GetNodeItem(nPos);
// If the variable is a reference (OBJ.PROPERTY)
if (pVar->IsReference()) {
char *p, szFront[256];
Common::strcpy_s(szFront, pVar->GetName());
if ((p = strstr(szFront, "~~")) != nullptr) {
char szBack[256];
Common::strcpy_s(szBack, p + 2);
*p = '\0';
CBofString sObject(szFront, 256);
CBofString sProperty(szBack, 256);
int nVal = SDEVMNGR->GetObjectValue(sObject, sProperty);
pVar->SetValue(nVal);
}
}
return pVar;
}
CBagExpression::OPERATION CBagExpression::GetOperation(int nPos) {
Assert(false);
return m_cOperList.GetNodeItem(nPos);
}
bool CBagExpression::Evaluate(bool bNeg, CBagVar &xResult) {
bool bRClocal = false;
// There must be an expression for every variable after the first
Assert(m_cVarList.GetCount() - 1 == m_cOperList.GetCount());
int nVCount = 0;
CBagVar *xLHOper = GetVariable(nVCount++);
xResult = *xLHOper;
bool bRCparent = true;
if (m_xPrevExpression) {
bRCparent = m_xPrevExpression->Evaluate(m_bPrevNegative, xResult);
}
if (bRCparent) {
bool bVal = false;
int nECount = 0;
while (nVCount < m_cVarList.GetCount()) {
CBagVar *xRHOper = GetVariable(nVCount++);
OPERATION xOper = m_cOperList.GetNodeItem(nECount++);
CBagVar *xRHOper2;
switch (xOper) {
case AND:
xRHOper2 = GetVariable(nVCount++);
xOper = m_cOperList.GetNodeItem(nECount++);
bVal = Evaluate(xRHOper, xRHOper2, xOper, xResult);
bRClocal &= bVal;
break;
case OR:
xRHOper2 = GetVariable(nVCount++);
xOper = m_cOperList.GetNodeItem(nECount++);
bVal = Evaluate(xRHOper, xRHOper2, xOper, xResult);
bRClocal |= bVal;
break;
default:
bRClocal = Evaluate(xLHOper, xRHOper, xOper, xResult);
break;
}
}
if (bNeg)
// Evaluate before and with parent
bRClocal = !bRClocal;
bRClocal &= bRCparent;
}
return bRClocal;
}
bool CBagExpression::EvalLeftToRight(bool bNeg, CBagVar &xResult) {
bool bRClocal = false;
CBagVar stLHOper;
OPERATION xOper = NONE;
// There must be an expression for every variable after the first
Assert(m_cVarList.GetCount() - 1 == m_cOperList.GetCount());
int nVCount = 0;
CBagVar *xLHOper = GetVariable(nVCount++);
xResult = *xLHOper;
bool bRCparent = true;
if (m_xPrevExpression) {
bRCparent = m_xPrevExpression->Evaluate(m_bPrevNegative, xResult);
}
if (bRCparent) {
bool bFirstTime = true;
int nECount = 0;
while (nVCount < m_cVarList.GetCount()) {
CBagVar *xRHOper = GetVariable(nVCount++);
OPERATION xPrevOper = xOper; // save previous operator
xOper = m_cOperList.GetNodeItem(nECount++);
if (bFirstTime) {
stLHOper = *xLHOper;
bFirstTime = false;
} else {
// Based on what we have for a previous operator, either use
// the left hand expression or the result of the previous expression.
switch (xPrevOper) {
case MINUS:
case MULTIPLY:
case DIVIDE:
case MOD:
case PLUS:
stLHOper = xResult;
break;
case NONE:
case ASSIGN:
case EQUAL:
case NOTEQUAL:
case LESSTHAN:
case LESSTHANEQUAL:
case GREATERTHAN:
case GREATERTHANEQUAL:
case PLUSASSIGN:
case MINUSASSIGN:
case CONTAINS:
case HAS:
case STATUS:
default:
stLHOper = *xLHOper;
break;
}
}
bool bVal;
CBagVar *xRHOper2;
switch (xOper) {
case AND:
xRHOper2 = GetVariable(nVCount++);
xOper = m_cOperList.GetNodeItem(nECount++);
bVal = Evaluate(xRHOper, xRHOper2, xOper, xResult);
bRClocal &= bVal;
break;
case OR:
xRHOper2 = GetVariable(nVCount++);
xOper = m_cOperList.GetNodeItem(nECount++);
bVal = Evaluate(xRHOper, xRHOper2, xOper, xResult);
// or this don't not it!!!
bRClocal |= bVal;
break;
default:
bRClocal = Evaluate(&stLHOper, xRHOper, xOper, xResult);
break;
}
}
if (bNeg)
// Evaluate before and with parent
bRClocal = !bRClocal;
bRClocal &= bRCparent;
}
return bRClocal;
}
bool CBagExpression::NegEvaluate(CBagVar &xResult) {
return Evaluate(false, xResult);
}
bool CBagExpression::OnAssign(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar & /*xResultOper*/) {
//int nIndex;
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
char szBuf[256];
Common::strcpy_s(szBuf, xRHOper->GetValue());
Assert(strlen(szBuf) < 256);
CBofString sBuf(szBuf, 256);
xLHOper->SetValue(sBuf);
return true;
}
bool CBagExpression::OnEqual(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
bool bVal = xLHOper->GetValue() == xRHOper->GetValue();
xResultOper.SetBoolValue(bVal);
return bVal;
}
bool CBagExpression::OnNotEqual(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
bool bVal = xLHOper->GetValue() != xRHOper->GetValue();
xResultOper.SetBoolValue(bVal);
return bVal;
}
bool CBagExpression::OnLessThan(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
bool bVal = xLHOper->GetNumValue() < xRHOper->GetNumValue();
xResultOper.SetBoolValue(bVal);
return bVal;
}
bool CBagExpression::OnGreaterThan(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
bool bVal = xLHOper->GetNumValue() > xRHOper->GetNumValue();
xResultOper.SetBoolValue(bVal);
return bVal;
}
bool CBagExpression::OnLessThanEqual(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
bool bVal = xLHOper->GetNumValue() <= xRHOper->GetNumValue();
xResultOper.SetBoolValue(bVal);
return bVal;
}
bool CBagExpression::OnGreaterThanEqual(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
bool bVal = xLHOper->GetNumValue() >= xRHOper->GetNumValue();
xResultOper.SetBoolValue(bVal);
return bVal;
}
bool CBagExpression::OnPlusAssign(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
if (xLHOper->IsNumeric() && xRHOper->IsNumeric()) {
int nLHO = xLHOper->GetNumValue();
int nRHO = xRHOper->GetNumValue();
xLHOper->SetValue(nLHO + nRHO);
xResultOper.SetValue(xLHOper->GetNumValue());
}
return true;
}
bool CBagExpression::OnMinusAssign(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
if (xLHOper->IsNumeric() && xRHOper->IsNumeric()) {
int nLHO = xLHOper->GetNumValue();
int nRHO = xRHOper->GetNumValue();
xLHOper->SetValue(nLHO - nRHO);
xResultOper.SetValue(xLHOper->GetNumValue());
}
return true;
}
bool CBagExpression::OnContains(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar & /*xResultOper*/) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
CBagStorageDev *pSDev;
if ((pSDev = SDEVMNGR->GetStorageDevice(xLHOper->GetValue())) == nullptr)
return false;
CBagObject *pObj;
if ((pObj = pSDev->GetObject(xRHOper->GetValue())) == nullptr)
return false;
if (pObj->IsActive())
return true;
return false;
}
bool CBagExpression::OnHas(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar & /*xResultOper*/) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
CBagStorageDev *pSDev = SDEVMNGR->GetStorageDevice(xLHOper->GetValue());
if (pSDev == nullptr)
return false;
CBagObject *pObj = pSDev->GetObjectByType(xRHOper->GetValue(), true);
if (pObj == nullptr)
return false;
return true;
}
bool CBagExpression::OnStatus(CBagVar *pLHOper, CBagVar * /*pRHOper*/, CBagVar & /*xResultOper*/) {
Assert(pLHOper != nullptr);
CBagStorageDev *pSDev = SDEVMNGR->GetStorageDeviceContaining(pLHOper->GetValue());
if (pSDev == nullptr)
return false;
CBagObject *pObj = pSDev->GetObject(pLHOper->GetValue());
if (pObj == nullptr)
return false;
return false;
}
bool CBagExpression::OnCurrSDev(CBagVar * /*xLHOper*/, CBagVar * /*xRHOper*/, CBagVar & /*xResultOper*/) {
return true;
}
bool CBagExpression::OnPlus(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
if (xLHOper->IsNumeric() && xRHOper->IsNumeric()) {
int nLHO = xLHOper->GetNumValue();
int nRHO = xRHOper->GetNumValue();
xResultOper.SetValue(nLHO + nRHO);
}
return true;
}
bool CBagExpression::OnMinus(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
if (xLHOper->IsNumeric() && xRHOper->IsNumeric()) {
int nLHO = xLHOper->GetNumValue();
int nRHO = xRHOper->GetNumValue();
xResultOper.SetValue(nLHO - nRHO);
}
return true;
}
bool CBagExpression::OnMultiply(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
if (xLHOper->IsNumeric() && xRHOper->IsNumeric()) {
int nLHO = xLHOper->GetNumValue();
int nRHO = xRHOper->GetNumValue();
xResultOper.SetValue(nLHO * nRHO);
}
return true;
}
bool CBagExpression::OnDivide(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
if (xLHOper->IsNumeric() && xRHOper->IsNumeric()) {
int nLHO = xLHOper->GetNumValue();
int nRHO = xRHOper->GetNumValue();
// Divide by Zero error?
Assert(nRHO != 0);
xResultOper.SetValue(nLHO / nRHO);
}
return true;
}
bool CBagExpression::OnMod(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar &xResultOper) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
if (xLHOper->IsNumeric() && xRHOper->IsNumeric()) {
int nLHO = xLHOper->GetNumValue();
int nRHO = xRHOper->GetNumValue();
// Divide by Zero error?
Assert(nRHO != 0);
xResultOper.SetValue(nLHO % nRHO);
}
return true;
}
bool CBagExpression::OnAnd(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar & /*xResultOper*/) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
return (!xLHOper->GetValue().Find("true") && !xRHOper->GetValue().Find("true"));
}
bool CBagExpression::OnOr(CBagVar *xLHOper, CBagVar *xRHOper, CBagVar & /*xResultOper*/) {
Assert((xLHOper != nullptr) && (xRHOper != nullptr));
return (!xLHOper->GetValue().Find("true") || !xRHOper->GetValue().Find("true"));
}
PARSE_CODES CBagExpression::SetInfo(bof_ifstream &istr) {
char szBuf[256];
szBuf[0] = 0;
CBofString sStr(szBuf, 256);
char szErrStr[256];
Common::strcpy_s(szErrStr, "Error in expression ");
CBofString errStr(szErrStr, 256);
PARSE_CODES rc = PARSING_DONE;
bool bDone = false;
OPERATION xOper;
while (!bDone && rc == PARSING_DONE) {
istr.EatWhite();
int ch = istr.peek();
switch (ch) {
case '(': {
istr.Get();
istr.EatWhite();
GetAlphaNumFromStream(istr, sStr);
CBagVar *pVar = VARMNGR->GetVariable(sStr);
if (!pVar) { // This must be a reference, make a new variable
if (sStr.Find("~~") > 0) {
pVar = new CBagVar;
pVar->SetName(sStr);
pVar->SetReference();
} else { // This is an error condition, constants can only be rhopers
pVar = new CBagVar;
pVar->SetName(sStr);
pVar->SetValue(sStr);
pVar->SetConstant();
}
}
m_cVarList.AddToTail(pVar);
istr.EatWhite();
ch = istr.peek();
while ((ch != ')') && rc == PARSING_DONE) {
GetOperatorFromStream(istr, xOper);
if (xOper == NONE) {
rc = UNKNOWN_TOKEN;
errStr = "Bad operator:";
break;
}
m_cOperList.AddToTail(xOper);
istr.EatWhite();
GetAlphaNumFromStream(istr, sStr);
pVar = VARMNGR->GetVariable(sStr);
if (!pVar) {
if (sStr.Find("~~") > 0) { // This must be a reference, make a new variable
pVar = new CBagVar;
pVar->SetName(sStr);
pVar->SetReference();
} else { // This must be a constant, make a new variable
pVar = new CBagVar;
pVar->SetName(sStr);
pVar->SetValue(sStr);
pVar->SetConstant();
}
}
m_cVarList.AddToTail(pVar);
istr.EatWhite();
ch = istr.peek();
} // while parsing inner circle
if (ch == ')') {
istr.Get();
bDone = true;
}
break;
}
case 'N':
GetAlphaNumFromStream(istr, sStr);
if (!sStr.Find("NOT")) {
m_bNegative = (byte)!m_bNegative;
istr.EatWhite();
break;
}
default:
rc = UNKNOWN_TOKEN;
break;
}
}
if (rc != PARSING_DONE) {
ParseAlertBox(istr, "Error in expression:", __FILE__, __LINE__);
}
istr.EatWhite();
return rc;
}
ErrorCode CBagExpression::GetOperatorFromStream(bof_ifstream &istr, OPERATION &xOper) {
ErrorCode rc = ERR_NONE;
char szLocalBuff[256];
szLocalBuff[0] = 0;
CBofString sStr(szLocalBuff, 256);
xOper = NONE;
istr.EatWhite();
GetOperStrFromStream(istr, sStr);
if (sStr.IsEmpty()) {
GetAlphaNumFromStream(istr, sStr);
istr.EatWhite();
}
if (!sStr.Find("-=")) {
xOper = MINUSASSIGN;
} else if (!sStr.Find("+=")) {
xOper = PLUSASSIGN;
} else if (!sStr.Find(">=")) {
xOper = GREATERTHANEQUAL;
} else if (!sStr.Find("<=")) {
xOper = LESSTHANEQUAL;
} else if (!sStr.Find("!=")) {
xOper = NOTEQUAL;
} else if (!sStr.Find("==")) {
xOper = EQUAL;
} else if (!sStr.Find(">")) {
xOper = GREATERTHAN;
} else if (!sStr.Find("<")) {
xOper = LESSTHAN;
} else if (!sStr.Find("=")) {
xOper = ASSIGN;
} else if (!sStr.Find("+")) {
xOper = PLUS;
} else if (!sStr.Find("-")) {
xOper = MINUS;
} else if (!sStr.Find("*")) {
xOper = MULTIPLY;
} else if (!sStr.Find("/")) {
xOper = DIVIDE;
} else if (!sStr.Find("%")) {
xOper = MOD;
} else if (!sStr.Find("CONTAINS")) {
// Sdev contains object
xOper = CONTAINS;
} else if (!sStr.Find("HAS")) {
// Sdev has type of object
xOper = HAS;
} else if (!sStr.Find("OR")) {
xOper = OR;
} else if (!sStr.Find("STATUS")) {
xOper = STATUS;
} else if (!sStr.Find("AND")) {
xOper = AND;
}
if (xOper == NONE)
rc = ERR_UNKNOWN;
return rc;
}
} // namespace Bagel