Initial commit
This commit is contained in:
379
engines/glk/alan3/alt_info.cpp
Normal file
379
engines/glk/alan3/alt_info.cpp
Normal file
@@ -0,0 +1,379 @@
|
||||
/* 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 "glk/alan3/alt_info.h"
|
||||
#include "glk/alan3/types.h"
|
||||
#include "glk/alan3/checkentry.h"
|
||||
#include "glk/alan3/debug.h"
|
||||
#include "glk/alan3/inter.h"
|
||||
#include "glk/alan3/glkio.h"
|
||||
#include "glk/alan3/lists.h"
|
||||
#include "glk/alan3/instance.h"
|
||||
#include "glk/alan3/options.h"
|
||||
#include "glk/alan3/memory.h"
|
||||
#include "glk/alan3/current.h"
|
||||
#include "glk/alan3/class.h"
|
||||
#include "glk/alan3/params.h"
|
||||
#include "glk/alan3/literal.h"
|
||||
#include "glk/alan3/syntax.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Alan3 {
|
||||
|
||||
/* Types */
|
||||
typedef AltInfo *AltInfoFinder(int verb, Parameter parameters[]);
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
void primeAltInfo(AltInfo *altInfo, int level, int parameter, int instance, int cls) {
|
||||
altInfo->level = level;
|
||||
altInfo->parameter = parameter;
|
||||
altInfo->instance = instance;
|
||||
altInfo->_class = cls;
|
||||
altInfo->done = FALSE;
|
||||
altInfo->end = FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static void traceInstanceAndItsClass(CONTEXT, Aid instance, Aid cls) {
|
||||
CALL1(traceSay, instance)
|
||||
printf("[%d]", instance);
|
||||
if (cls != NO_CLASS)
|
||||
printf(", inherited from %s[%d]", idOfClass(cls), cls);
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static void traceAltInfo(CONTEXT, AltInfo *alt) {
|
||||
switch (alt->level) {
|
||||
case GLOBAL_LEVEL:
|
||||
printf("GLOBAL");
|
||||
break;
|
||||
case LOCATION_LEVEL:
|
||||
printf("in (location) ");
|
||||
CALL2(traceInstanceAndItsClass, current.location, alt->_class)
|
||||
break;
|
||||
case PARAMETER_LEVEL: {
|
||||
char *parameterName = parameterNameInSyntax(current.verb, alt->parameter);
|
||||
if (parameterName != nullptr)
|
||||
printf("in parameter %s(#%d)=", parameterName, alt->parameter);
|
||||
else
|
||||
printf("in parameter #%d=", alt->parameter);
|
||||
CALL2(traceInstanceAndItsClass, globalParameters[alt->parameter - 1].instance, alt->_class)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static void traceVerbCheck(CONTEXT, AltInfo *alt, bool execute) {
|
||||
if (traceSectionOption && execute) {
|
||||
printf("\n<VERB %d, ", current.verb);
|
||||
CALL1(traceAltInfo, alt)
|
||||
printf(", CHECK:>\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
bool checkFailed(CONTEXT, AltInfo *altInfo, bool execute) {
|
||||
if (altInfo->alt != nullptr && altInfo->alt->checks != 0) {
|
||||
R0CALL2(traceVerbCheck, altInfo, execute)
|
||||
|
||||
// TODO Why does this not generate a regression error with !
|
||||
// Need a new regression case?
|
||||
R0FUNC2(checksFailed, fail, altInfo->alt->checks, execute)
|
||||
return fail;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static void traceVerbExecution(CONTEXT, AltInfo *alt) {
|
||||
if (traceSectionOption) {
|
||||
printf("\n<VERB %d, ", current.verb);
|
||||
CALL1(traceAltInfo, alt)
|
||||
printf(", DOES");
|
||||
|
||||
switch (alt->alt->qual) {
|
||||
case Q_BEFORE:
|
||||
printf(" (BEFORE)");
|
||||
break;
|
||||
case Q_ONLY:
|
||||
printf(" (ONLY)");
|
||||
break;
|
||||
case Q_AFTER:
|
||||
printf(" (AFTER)");
|
||||
break;
|
||||
case Q_DEFAULT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
printf(":>\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
bool executedOk(CONTEXT, AltInfo *altInfo) {
|
||||
fail = FALSE;
|
||||
if (!altInfo->done && altInfo->alt->action != 0) {
|
||||
R0CALL1(traceVerbExecution, altInfo)
|
||||
current.instance = altInfo->instance;
|
||||
R0CALL1(interpret, altInfo->alt->action)
|
||||
}
|
||||
altInfo->done = TRUE;
|
||||
return !fail;
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
bool canBeExecuted(AltInfo *altInfo) {
|
||||
return altInfo->alt != nullptr && altInfo->alt->action != 0;
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
AltInfo *duplicateAltInfoArray(AltInfo original[]) {
|
||||
int size;
|
||||
AltInfo *duplicate;
|
||||
|
||||
for (size = 0; original[size].end != TRUE; size++)
|
||||
;
|
||||
size++;
|
||||
duplicate = (AltInfo *)allocate(size * sizeof(AltInfo));
|
||||
memcpy(duplicate, original, size * sizeof(AltInfo));
|
||||
return duplicate;
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
int lastAltInfoIndex(AltInfo altInfo[]) {
|
||||
int altIndex;
|
||||
|
||||
/* Loop to last alternative */
|
||||
for (altIndex = -1; !altInfo[altIndex + 1].end; altIndex++)
|
||||
;
|
||||
return altIndex;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static AltInfo *nextFreeAltInfo(AltInfoArray altInfos) {
|
||||
return &altInfos[lastAltInfoIndex(altInfos) + 1];
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static void addAlternative(AltInfoArray altInfos, int verb, int level, Aint parameterNumber, Aint theClass, Aid theInstance, AltEntryFinder finder) {
|
||||
AltInfo *altInfoP = nextFreeAltInfo(altInfos);
|
||||
|
||||
altInfoP->alt = (*finder)(verb, parameterNumber, theInstance, theClass);
|
||||
if (altInfoP->alt != nullptr) {
|
||||
primeAltInfo(altInfoP, level, parameterNumber, theInstance, theClass);
|
||||
altInfoP[1].end = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static void addGlobalAlternatives(AltInfoArray altInfos, int verb, AltEntryFinder finder) {
|
||||
addAlternative(altInfos, verb, GLOBAL_LEVEL, NO_PARAMETER, NO_CLASS, NO_INSTANCE, finder);
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static void addAlternativesFromParents(AltInfoArray altInfos, int verb, int level, Aint parameterNumber, Aint theClass, Aid theInstance, AltEntryFinder finder) {
|
||||
if (classes[theClass].parent != 0)
|
||||
addAlternativesFromParents(altInfos, verb, level,
|
||||
parameterNumber,
|
||||
classes[theClass].parent,
|
||||
theInstance,
|
||||
finder);
|
||||
|
||||
addAlternative(altInfos, verb, level, parameterNumber, theClass, theInstance, finder);
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static void addAlternativesFromLocation(AltInfoArray altInfos, int verb, Aid location, AltEntryFinder finder) {
|
||||
if (admin[location].location != 0)
|
||||
addAlternativesFromLocation(altInfos, verb, admin[location].location, finder);
|
||||
|
||||
addAlternativesFromParents(altInfos, verb,
|
||||
LOCATION_LEVEL,
|
||||
NO_PARAMETER,
|
||||
instances[location].parent,
|
||||
location,
|
||||
finder);
|
||||
|
||||
addAlternative(altInfos, verb, LOCATION_LEVEL, NO_PARAMETER, NO_CLASS, location, finder);
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static void addAlternativesFromParameter(AltInfoArray altInfos, int verb, Parameter parameters[], int parameterNumber, AltEntryFinder finder) {
|
||||
Aid parent;
|
||||
Aid theInstance = parameters[parameterNumber - 1].instance;
|
||||
|
||||
if (isLiteral(theInstance))
|
||||
parent = literals[literalFromInstance(theInstance)]._class;
|
||||
else
|
||||
parent = instances[theInstance].parent;
|
||||
addAlternativesFromParents(altInfos, verb, PARAMETER_LEVEL, parameterNumber, parent, theInstance, finder);
|
||||
|
||||
if (!isLiteral(theInstance))
|
||||
addAlternative(altInfos, verb, PARAMETER_LEVEL, parameterNumber, NO_CLASS, theInstance, finder);
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
bool anyCheckFailed(CONTEXT, AltInfoArray altInfo, bool execute) {
|
||||
int altIndex;
|
||||
bool flag;
|
||||
|
||||
if (altInfo != nullptr)
|
||||
for (altIndex = 0; !altInfo[altIndex].end; altIndex++) {
|
||||
current.instance = altInfo[altIndex].instance;
|
||||
|
||||
R0FUNC2(checkFailed, flag, &altInfo[altIndex], execute)
|
||||
return flag;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
bool anythingToExecute(AltInfo altInfo[]) {
|
||||
int altIndex;
|
||||
|
||||
/* Check for anything to execute... */
|
||||
if (altInfo != nullptr)
|
||||
for (altIndex = 0; !altInfo[altIndex].end; altIndex++)
|
||||
if (canBeExecuted(&altInfo[altIndex]))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static VerbEntry *findVerbEntry(int verbCode, VerbEntry *entries) {
|
||||
VerbEntry *verbEntry;
|
||||
for (verbEntry = entries; !isEndOfArray(verbEntry); verbEntry++) {
|
||||
if (verbEntry->code < 0) {
|
||||
/* Verb codes are negative for Meta verbs, if so they are also 1 off to avoid EOD */
|
||||
if (abs(verbEntry->code) - 1 == verbCode)
|
||||
return verbEntry;
|
||||
} else {
|
||||
if (verbEntry->code == verbCode)
|
||||
return verbEntry;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static AltEntry *findAlternative(Aaddr verbTableAddress, int verbCode, int parameterNumber) {
|
||||
AltEntry *alt;
|
||||
VerbEntry *verbEntry;
|
||||
|
||||
if (verbTableAddress == 0) return nullptr;
|
||||
|
||||
verbEntry = findVerbEntry(verbCode, (VerbEntry *) pointerTo(verbTableAddress));
|
||||
if (verbEntry != nullptr)
|
||||
for (alt = (AltEntry *) pointerTo(verbEntry->alts); !isEndOfArray(alt); alt++) {
|
||||
if (alt->param == parameterNumber || alt->param == 0) {
|
||||
if (verbEntry->code < 0) current.meta = TRUE;
|
||||
return alt;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static AltEntry *alternativeFinder(int verb, int parameterNumber, int theInstance, int theClass) {
|
||||
if ((Aword)theClass != NO_CLASS)
|
||||
return findAlternative(classes[theClass].verbs, verb, parameterNumber);
|
||||
else if ((Aword)theInstance != NO_INSTANCE)
|
||||
return findAlternative(instances[theInstance].verbs, verb, parameterNumber);
|
||||
else
|
||||
return findAlternative(header->verbTableAddress, verb, parameterNumber);
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
AltInfo *findAllAlternatives(int verb, Parameter parameters[]) {
|
||||
int parameterNumber;
|
||||
AltInfo altInfos[1000];
|
||||
altInfos[0].end = TRUE;
|
||||
|
||||
addGlobalAlternatives(altInfos, verb, &alternativeFinder);
|
||||
|
||||
addAlternativesFromLocation(altInfos, verb, current.location, &alternativeFinder);
|
||||
|
||||
for (parameterNumber = 1; !isEndOfArray(¶meters[parameterNumber - 1]); parameterNumber++) {
|
||||
addAlternativesFromParameter(altInfos, verb, parameters, parameterNumber, &alternativeFinder);
|
||||
}
|
||||
return duplicateAltInfoArray(altInfos);
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static bool possibleWithFinder(CONTEXT, int verb, Parameter parameters[], AltInfoFinder *finder) {
|
||||
bool anything;
|
||||
AltInfo *allAlternatives;
|
||||
bool flag;
|
||||
|
||||
allAlternatives = finder(verb, parameters);
|
||||
|
||||
// TODO Need to do this since anyCheckFailed() call execute() which assumes the global parameters
|
||||
setGlobalParameters(parameters);
|
||||
R0FUNC2(anyCheckFailed, flag, allAlternatives, DONT_EXECUTE_CHECK_BODY_ON_FAIL)
|
||||
if (flag)
|
||||
anything = FALSE;
|
||||
else
|
||||
anything = anythingToExecute(allAlternatives);
|
||||
|
||||
if (allAlternatives != nullptr)
|
||||
deallocate(allAlternatives);
|
||||
|
||||
return (anything);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
bool possible(CONTEXT, int verb, Parameter inParameters[], ParameterPosition parameterPositions[]) {
|
||||
// This is a wrapper for possibleWithFinder() which is used in unit tests
|
||||
// possible() should be used "for real".
|
||||
|
||||
return possibleWithFinder(context, verb, inParameters, findAllAlternatives);
|
||||
}
|
||||
|
||||
} // End of namespace Alan3
|
||||
} // End of namespace Glk
|
||||
Reference in New Issue
Block a user