1521 lines
53 KiB
C++
1521 lines
53 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 "common/array.h"
|
|
#include "glk/alan3/parse.h"
|
|
#include "glk/alan3/act.h"
|
|
#include "glk/alan3/alt_info.h"
|
|
#include "glk/alan3/class.h"
|
|
#include "glk/alan3/compatibility.h"
|
|
#include "glk/alan3/current.h"
|
|
#include "glk/alan3/dictionary.h"
|
|
#include "glk/alan3/glkio.h"
|
|
#include "glk/alan3/instance.h"
|
|
#include "glk/alan3/inter.h"
|
|
#include "glk/jumps.h"
|
|
#include "glk/alan3/lists.h"
|
|
#include "glk/alan3/literal.h"
|
|
#include "glk/alan3/location.h"
|
|
#include "glk/alan3/memory.h"
|
|
#include "glk/alan3/msg.h"
|
|
#include "glk/alan3/options.h"
|
|
#include "glk/alan3/output.h"
|
|
#include "glk/alan3/parameter_position.h"
|
|
#include "glk/alan3/syntax.h"
|
|
#include "glk/alan3/scan.h"
|
|
#include "glk/alan3/syserr.h"
|
|
#include "glk/alan3/utils.h"
|
|
#include "glk/alan3/word.h"
|
|
|
|
namespace Glk {
|
|
namespace Alan3 {
|
|
|
|
/* PRIVATE TYPES */
|
|
/* To remember parameter/pronoun relations */
|
|
struct Pronoun {
|
|
int pronoun;
|
|
int instance;
|
|
};
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void clearPronounList(Pronoun list[]) {
|
|
implementationOfSetEndOfArray((Aword *)list);
|
|
}
|
|
|
|
|
|
typedef Aint *(*ReferencesFinder)(int wordIndex);
|
|
typedef void (*ParameterParser)(CONTEXT, Parameter parameters[]);
|
|
|
|
|
|
|
|
/* PRIVATE DATA */
|
|
static Pronoun *pronouns = nullptr;
|
|
|
|
|
|
/* Syntax Parameters */
|
|
static Parameter *previousMultipleParameters; /* Previous multiple list */
|
|
|
|
|
|
/* For parameters that are literals we need to trick message handling to
|
|
* output the word and create a string literal instance if anyone wants to
|
|
* refer to an attribute of it (literals inherit from entity so application
|
|
* can have added an attribute) */
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void addParameterForWord(Parameter *parameters, int wordIndex) {
|
|
Parameter *parameter = findEndOfParameterArray(parameters);
|
|
|
|
createStringLiteral((char *)pointerTo(dictionary[playerWords[wordIndex].code].string));
|
|
parameter->instance = instanceFromLiteral(litCount); /* A faked literal */
|
|
parameter->useWords = TRUE;
|
|
parameter->firstWord = parameter->lastWord = wordIndex;
|
|
setEndOfArray(parameter + 1);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static Pronoun *allocatePronounArray(Pronoun *currentList) {
|
|
if (currentList == nullptr)
|
|
currentList = (Pronoun *)allocate(sizeof(Pronoun) * (MAXPARAMS + 1));
|
|
clearPronounList(currentList);
|
|
return currentList;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool endOfWords(int wordIndex) {
|
|
return isEndOfArray(&playerWords[wordIndex]);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void handleDirectionalCommand(CONTEXT) {
|
|
currentWordIndex++;
|
|
if (!endOfWords(currentWordIndex) && !isConjunctionWord(currentWordIndex)) {
|
|
CALL1(error, M_WHAT)
|
|
} else {
|
|
CALL2(go, current.location, dictionary[playerWords[currentWordIndex - 1].code].code)
|
|
}
|
|
|
|
if (!endOfWords(currentWordIndex))
|
|
currentWordIndex++;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void errorWhichOne(CONTEXT, Parameter alternative[]) {
|
|
int p; /* Index into the list of alternatives */
|
|
ParameterArray parameters = newParameterArray();
|
|
|
|
parameters[0] = alternative[0];
|
|
setEndOfArray(¶meters[1]);
|
|
printMessageWithParameters(M_WHICH_ONE_START, parameters);
|
|
for (p = 1; !isEndOfArray(&alternative[p + 1]); p++) {
|
|
clearParameterArray(parameters);
|
|
addParameterToParameterArray(parameters, &alternative[p]);
|
|
printMessageWithParameters(M_WHICH_ONE_COMMA, parameters);
|
|
}
|
|
clearParameterArray(parameters);
|
|
addParameterToParameterArray(parameters, &alternative[p]);
|
|
printMessageWithParameters(M_WHICH_ONE_OR, parameters);
|
|
freeParameterArray(parameters);
|
|
CALL0(abortPlayerCommand) /* Return with empty error message */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void errorWhichPronoun(CONTEXT, int pronounWordIndex, Parameter alternatives[]) {
|
|
int p; /* Index into the list of alternatives */
|
|
Parameter *messageParameters = newParameterArray();
|
|
|
|
addParameterForWord(messageParameters, pronounWordIndex);
|
|
printMessageWithParameters(M_WHICH_PRONOUN_START, messageParameters);
|
|
|
|
clearParameterArray(messageParameters);
|
|
addParameterToParameterArray(messageParameters, &alternatives[0]);
|
|
|
|
printMessageWithParameters(M_WHICH_PRONOUN_FIRST, messageParameters);
|
|
|
|
for (p = 1; !isEndOfArray(&alternatives[p + 1]); p++) {
|
|
clearParameterArray(messageParameters);
|
|
addParameterToParameterArray(messageParameters, &alternatives[p]);
|
|
printMessageWithParameters(M_WHICH_ONE_COMMA, messageParameters);
|
|
}
|
|
clearParameterArray(messageParameters);
|
|
addParameterToParameterArray(messageParameters, &alternatives[p]);
|
|
printMessageWithParameters(M_WHICH_ONE_OR, messageParameters);
|
|
freeParameterArray(messageParameters);
|
|
CALL0(abortPlayerCommand)
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void errorWhat(CONTEXT, int playerWordIndex) {
|
|
Parameter *messageParameters = newParameterArray();
|
|
|
|
addParameterForWord(messageParameters, playerWordIndex);
|
|
printMessageWithParameters(M_WHAT_WORD, messageParameters);
|
|
freeParameterArray(messageParameters);
|
|
CALL0(abortPlayerCommand)
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void errorAfterExcept(CONTEXT, int butWordIndex) {
|
|
Parameter *messageParameters = newParameterArray();
|
|
addParameterForWord(messageParameters, butWordIndex);
|
|
printMessageWithParameters(M_AFTER_BUT, messageParameters);
|
|
freeParameterArray(messageParameters);
|
|
CALL0(abortPlayerCommand)
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static int fakePlayerWordForAll() {
|
|
/* Look through the dictionary and find any ALL_WORD, then add a
|
|
player word so that it can be used in the message */
|
|
int p, d;
|
|
|
|
for (p = 0; !isEndOfArray(&playerWords[p]); p++)
|
|
;
|
|
setEndOfArray(&playerWords[p + 1]); /* Make room for one more word */
|
|
for (d = 0; d < dictionarySize; d++)
|
|
if (isAll(d)) {
|
|
playerWords[p].code = d;
|
|
return p;
|
|
}
|
|
syserr("No ALLWORD found");
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void errorButAfterAll(CONTEXT, int butWordIndex) {
|
|
Parameter *messageParameters = newParameterArray();
|
|
addParameterForWord(messageParameters, butWordIndex);
|
|
addParameterForWord(messageParameters, fakePlayerWordForAll());
|
|
printMessageWithParameters(M_BUT_ALL, messageParameters);
|
|
freeParameterArray(messageParameters);
|
|
CALL0(abortPlayerCommand)
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static Aint findInstanceForNoun(int wordIndex) {
|
|
DictionaryEntry *d = &dictionary[wordIndex];
|
|
if (d->nounRefs == 0 || d->nounRefs == EOD)
|
|
syserr("No references for noun");
|
|
return *(Aint *) pointerTo(d->nounRefs);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void errorNoSuch(CONTEXT, Parameter parameter) {
|
|
|
|
/* If there was no instance, assume the last word used is the noun,
|
|
* then find any instance with the noun he used */
|
|
if (parameter.instance == (Aid) - 1)
|
|
parameter.instance = 0;
|
|
if (parameter.instance == 0)
|
|
parameter.instance = findInstanceForNoun(playerWords[parameter.lastWord].code);
|
|
parameter.useWords = TRUE; /* Indicate to use words and not names */
|
|
|
|
clearParameterArray(globalParameters);
|
|
addParameterToParameterArray(globalParameters, ¶meter);
|
|
CALL1(error, M_NO_SUCH)
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void buildAllHere(CONTEXT, Parameter list[]) {
|
|
uint instance;
|
|
bool found = FALSE;
|
|
int word = list[0].firstWord;
|
|
|
|
for (instance = 1; instance <= header->instanceMax; instance++)
|
|
if (isHere(instance, /*FALSE*/ TRANSITIVE)) {
|
|
Parameter *parameter = newParameter(instance);
|
|
addParameterToParameterArray(list, parameter);
|
|
deallocate(parameter);
|
|
found = TRUE;
|
|
}
|
|
if (!found)
|
|
errorWhat(context, word);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool endOfPronouns(int pronounIndex) {
|
|
return isEndOfArray(&pronouns[pronounIndex]);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static int getPronounInstances(int word, Parameter instanceParameters[]) {
|
|
/* Find the instance that the pronoun word could refer to, return 0
|
|
if none or multiple */
|
|
int p;
|
|
int instanceCount = 0;
|
|
|
|
clearParameterArray(instanceParameters);
|
|
for (p = 0; !endOfPronouns(p); p++)
|
|
if (pronouns[p].instance != 0 && dictionary[word].code == (Aword)pronouns[p].pronoun) {
|
|
instanceParameters[instanceCount].instance = pronouns[p].instance;
|
|
instanceParameters[instanceCount].useWords = FALSE; /* Can't use words since they are gone, pronouns
|
|
refer to parameters in previous command */
|
|
setEndOfArray(&instanceParameters[++instanceCount]);
|
|
}
|
|
return instanceCount;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool inOpaqueContainer(int originalInstance) {
|
|
int instance = admin[originalInstance].location;
|
|
|
|
while (isAContainer(instance)) {
|
|
// TODO : isOpaque()
|
|
if (getInstanceAttribute(instance, OPAQUEATTRIBUTE))
|
|
return TRUE;
|
|
instance = admin[instance].location;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool reachable(int instance) {
|
|
if (isA(instance, THING) || isA(instance, LOCATION))
|
|
return isHere(instance, TRANSITIVE) && !inOpaqueContainer(instance);
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static Aint *nounReferencesForWord(int wordIndex) {
|
|
return (Aint *) pointerTo(dictionary[playerWords[wordIndex].code].nounRefs);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static Aint *adjectiveReferencesForWord(int wordIndex) {
|
|
return (Aint *) pointerTo(dictionary[playerWords[wordIndex].code].adjectiveRefs);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void parseLiteral(Parameter parameters[]) {
|
|
parameters[0].firstWord = parameters[0].lastWord = currentWordIndex++;
|
|
parameters[0].instance = 0;
|
|
parameters[0].isLiteral = TRUE;
|
|
setEndOfArray(¶meters[1]);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void parsePronoun(Parameter parameters[]) {
|
|
parameters[0].firstWord = parameters[0].lastWord = currentWordIndex++;
|
|
parameters[0].instance = 0;
|
|
parameters[0].isPronoun = TRUE;
|
|
setEndOfArray(¶meters[1]);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool anotherAdjective(int wordIndex) {
|
|
return !endOfWords(wordIndex) && isAdjectiveWord(wordIndex);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool lastPossibleNoun(int wordIndex) {
|
|
return isNounWord(wordIndex) && (endOfWords(wordIndex + 1) || !isNounWord(wordIndex + 1));
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void updateWithReferences(Parameter result[], int wordIndex, Aint * (*referenceFinder)(int wordIndex)) {
|
|
static Parameter *references = nullptr; /* Instances referenced by a word */
|
|
references = ensureParameterArrayAllocated(references);
|
|
|
|
copyReferencesToParameterArray(referenceFinder(wordIndex), references);
|
|
if (lengthOfParameterArray(result) == 0)
|
|
copyParameterArray(result, references);
|
|
else
|
|
intersectParameterArrays(result, references);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void filterOutNonReachable(Parameter filteredCandidates[], bool (*reachable)(int)) {
|
|
int i;
|
|
for (i = 0; !isEndOfArray(&filteredCandidates[i]); i++)
|
|
if (!reachable(filteredCandidates[i].instance))
|
|
filteredCandidates[i].instance = 0;
|
|
compressParameterArray(filteredCandidates);
|
|
}
|
|
|
|
|
|
/*
|
|
* There are various ways the player can refer to things, some are
|
|
* explicit, in which case they should be kept in the input. If he said
|
|
* 'them', 'all' or some such the list is inferred so we must filter
|
|
* it w.r.t what it can mean. A special case is when he said 'the ball'
|
|
* and there is only one ball here, but multiple in the game. We need
|
|
* to be able to distinguish between these cases!!! 'them' is
|
|
* explicit, 'all' is inferred, exceptions can never be inferred,
|
|
* maybe 'all' is the only inferred?
|
|
*/
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void disambiguateCandidatesForPosition(CONTEXT, ParameterPosition parameterPositions[],
|
|
int position, Parameter candidates[]) {
|
|
int i;
|
|
Parameter *parameters = newParameterArray();
|
|
|
|
convertPositionsToParameters(parameterPositions, parameters);
|
|
for (i = 0; !isEndOfArray(&candidates[i]); i++) {
|
|
if (candidates[i].instance != 0) { /* Already empty? */
|
|
copyParameter(¶meters[position], &candidates[i]);
|
|
// DISAMBIGUATION!!
|
|
if (!reachable(candidates[i].instance)) {
|
|
// Then remove this candidate from list
|
|
candidates[i].instance = 0;
|
|
} else {
|
|
bool flag;
|
|
FUNC3(possible, flag, current.verb, parameters, parameterPositions)
|
|
if (!flag)
|
|
candidates[i].instance = 0;
|
|
}
|
|
}
|
|
}
|
|
compressParameterArray(candidates);
|
|
freeParameterArray(parameters);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool parseAnyAdjectives(Parameter parameters[]) {
|
|
bool adjectiveOrNounFound = FALSE;
|
|
while (anotherAdjective(currentWordIndex)) {
|
|
if (lastPossibleNoun(currentWordIndex))
|
|
break;
|
|
adjectiveOrNounFound = TRUE;
|
|
currentWordIndex++;
|
|
}
|
|
return adjectiveOrNounFound;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
/* Parse the input and note the word indices in the parameters,
|
|
matching will be done by the match* functions */
|
|
static void parseAdjectivesAndNoun(CONTEXT, Parameter parameters[]) {
|
|
int wordFirst, wordLast;
|
|
bool adjectiveOrNounFound = FALSE;
|
|
|
|
wordFirst = currentWordIndex;
|
|
|
|
adjectiveOrNounFound = parseAnyAdjectives(parameters);
|
|
|
|
if (!endOfWords(currentWordIndex)) {
|
|
if (isNounWord(currentWordIndex)) {
|
|
adjectiveOrNounFound = TRUE;
|
|
currentWordIndex++;
|
|
} else
|
|
CALL1(error, M_NOUN)
|
|
} else if (adjectiveOrNounFound) {
|
|
/* Perhaps the last word could also be interpreted as a noun? */
|
|
if (isNounWord(currentWordIndex - 1)) {
|
|
// TODO When does this get executed? Maybe if conjunctions can be nouns? Or nouns be adjectives?
|
|
printf("DEBUG: When does this get executed? Found adjective or Noun and the previous word could also be a noun...\n");
|
|
} else
|
|
CALL1(error, M_NOUN)
|
|
}
|
|
|
|
if (adjectiveOrNounFound) {
|
|
wordLast = currentWordIndex - 1;
|
|
|
|
parameters[0].firstWord = wordFirst;
|
|
parameters[0].lastWord = wordLast;
|
|
parameters[0].instance = 0; /* No instance yet! */
|
|
setEndOfArray(¶meters[1]);
|
|
} else
|
|
setEndOfArray(¶meters[0]);
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
static void parseReference(CONTEXT, Parameter parameters[]) {
|
|
clearParameterArray(parameters);
|
|
|
|
if (isLiteralWord(currentWordIndex)) {
|
|
parseLiteral(parameters);
|
|
} else if (isPronounWord(currentWordIndex)) {
|
|
parsePronoun(parameters);
|
|
} else {
|
|
CALL1(parseAdjectivesAndNoun, parameters)
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void getPreviousMultipleParameters(Parameter parameters[]) {
|
|
int i;
|
|
for (i = 0; !isEndOfArray(&previousMultipleParameters[i]); i++) {
|
|
parameters[i].candidates = ensureParameterArrayAllocated(parameters[i].candidates);
|
|
setEndOfArray(¶meters[i].candidates[0]); /* No candidates */
|
|
if (!reachable(previousMultipleParameters[i].instance))
|
|
parameters[i].instance = 0;
|
|
else
|
|
parameters[i].instance = previousMultipleParameters[i].instance;
|
|
}
|
|
setEndOfArray(¶meters[i]);
|
|
compressParameterArray(parameters);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void parseReferenceToPreviousMultipleParameters(Parameter parameters[]) {
|
|
parameters[0].firstWord = parameters[0].lastWord = currentWordIndex++;
|
|
parameters[0].instance = 0;
|
|
parameters[0].isThem = TRUE;
|
|
setEndOfArray(¶meters[1]);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool parseOneParameter(CONTEXT, Parameter parameters[], int parameterIndex) {
|
|
Parameter *parameter = newParameterArray();
|
|
|
|
// TODO Maybe this should go in the complex()?
|
|
if (isThemWord(currentWordIndex) && (!isPronounWord(currentWordIndex) ||
|
|
(isPronounWord(currentWordIndex) && lengthOfParameterArray(previousMultipleParameters) > 0))) {
|
|
// "them" is also a common pronoun for some instances, but if there
|
|
// are previous multiple parameters we give precedence to those
|
|
parseReferenceToPreviousMultipleParameters(parameter);
|
|
} else {
|
|
R0CALL1(parseReference, parameter)
|
|
if (lengthOfParameterArray(parameter) == 0) { /* Failed to find any exceptions! */
|
|
freeParameterArray(parameter);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Add the one we found to the parameters */
|
|
parameters[parameterIndex] = parameter[0];
|
|
setEndOfArray(¶meters[parameterIndex + 1]);
|
|
freeParameterArray(parameter);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* A "simple" parameter is in one of three forms:
|
|
*
|
|
* 1) adjectives and nouns referencing a single instance (might
|
|
* actually match multiple instances in the game...)
|
|
*
|
|
* 2) multiple of 1) separated by conjunctions ("a and b and c")
|
|
*
|
|
* 3) a pronoun referencing a single instance
|
|
|
|
* We also need to handle "them" here since it is also a common
|
|
* pronoun for some instances, e.g. scissor, trouser, money,
|
|
* glasses, but it can also refer to the set of instances from the
|
|
* previous command. If it is a pronoun but there are also multiple
|
|
* parameter from the previous command, we give precedence to the
|
|
* multiple previous.
|
|
*/
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
static void simpleParameterParser(CONTEXT, Parameter parameters[]) {
|
|
bool flag;
|
|
|
|
/* This will loop until all references are collected (typically "a and b and c") */
|
|
int parameterIndex;
|
|
for (parameterIndex = 0;; parameterIndex++) {
|
|
FUNC2(parseOneParameter, flag, parameters, parameterIndex)
|
|
if (!flag)
|
|
return;
|
|
|
|
if (!endOfWords(currentWordIndex)
|
|
&& (isConjunctionWord(currentWordIndex) && (isAdjectiveWord(currentWordIndex + 1)
|
|
|| isNounWord(currentWordIndex + 1)))) {
|
|
/* Since this is a conjunction and the next seems to be another instance reference,
|
|
let's continue with that by eating the conjunction */
|
|
currentWordIndex++;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void parseExceptions(CONTEXT, ParameterPosition *parameterPosition, ParameterParser simpleParameterParser) {
|
|
int exceptWordIndex = currentWordIndex;
|
|
currentWordIndex++;
|
|
parameterPosition->exceptions = ensureParameterArrayAllocated(parameterPosition->exceptions);
|
|
CALL1(simpleParameterParser, parameterPosition->exceptions)
|
|
|
|
if (lengthOfParameterArray(parameterPosition->exceptions) == 0)
|
|
errorAfterExcept(context, exceptWordIndex);
|
|
}
|
|
|
|
|
|
/*
|
|
* Complex instance references are of the form:
|
|
*
|
|
* 1) all
|
|
*
|
|
* 2) all except
|
|
*
|
|
* 3) a simple (see above) instance reference(s)
|
|
*/
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
static void complexParameterParserDelegate(CONTEXT, ParameterPosition *parameterPosition,
|
|
ParameterParser simpleParameterParser) {
|
|
parameterPosition->parameters = ensureParameterArrayAllocated(parameterPosition->parameters);
|
|
|
|
parameterPosition->all = FALSE;
|
|
parameterPosition->them = FALSE;
|
|
parameterPosition->explicitMultiple = FALSE;
|
|
|
|
if (isAllWord(currentWordIndex)) {
|
|
parameterPosition->all = TRUE;
|
|
parameterPosition->explicitMultiple = TRUE;
|
|
parameterPosition->parameters[0].firstWord = currentWordIndex;
|
|
parameterPosition->parameters[0].lastWord = currentWordIndex;
|
|
currentWordIndex++;
|
|
parameterPosition->parameters[0].instance = 0;
|
|
setEndOfArray(¶meterPosition->parameters[1]);
|
|
if (!endOfWords(currentWordIndex) && isExceptWord(currentWordIndex))
|
|
CALL2(parseExceptions, parameterPosition, simpleParameterParser)
|
|
} else {
|
|
CALL1(simpleParameterParser, parameterPosition->parameters)
|
|
if (lengthOfParameterArray(parameterPosition->parameters) > 1)
|
|
parameterPosition->explicitMultiple = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void complexReferencesParser(CONTEXT, ParameterPosition *parameterPosition) {
|
|
complexParameterParserDelegate(context, parameterPosition, simpleParameterParser);
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static char *classNameAndId(int classId) {
|
|
static char buffer[1000] = "";
|
|
|
|
if (classId != -1)
|
|
Common::sprintf_s(buffer, "%s[%d]", idOfClass(classId), classId);
|
|
else
|
|
Common::sprintf_s(buffer, "Container");
|
|
return buffer;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static char *parameterNumberAndName(int parameterNumber) {
|
|
static char buffer[1000] = "";
|
|
/* HERE SHOULD BE current.syntax */
|
|
char *parameterName = parameterNameInSyntax(current.syntax, parameterNumber);
|
|
|
|
if (parameterName != nullptr)
|
|
Common::sprintf_s(buffer, "%s(#%d)", parameterName, parameterNumber);
|
|
else
|
|
Common::sprintf_s(buffer, "#%d", parameterNumber);
|
|
return buffer;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void traceRestriction(RestrictionEntry *restriction, int classId, bool condition) {
|
|
printf("\n<SYNTAX RESTRICTION WHERE parameter %s Isa %s, %s>\n",
|
|
parameterNumberAndName(restriction->parameterNumber),
|
|
classNameAndId(classId), condition ? "PASSED" : "FAILED:");
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool restrictionCheck(RestrictionEntry *restriction, int instance) {
|
|
if (restriction->_class == RESTRICTIONCLASS_CONTAINER) {
|
|
if (traceSectionOption)
|
|
traceRestriction(restriction, -1, isAContainer(instance));
|
|
return isAContainer(instance);
|
|
} else {
|
|
if (traceSectionOption)
|
|
traceRestriction(restriction, restriction->_class, isA(instance, restriction->_class));
|
|
return isA(instance, restriction->_class);
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void runRestriction(CONTEXT, RestrictionEntry *restriction, Parameter parameters[]) {
|
|
if (restriction->stms) {
|
|
setGlobalParameters(parameters);
|
|
interpret(context, restriction->stms);
|
|
} else {
|
|
error(context, M_CANT0);
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static int remapParameterOrder(int syntaxNumber, ParameterPosition parameterPositions[]) {
|
|
/* Find the syntax map, use the verb code from it and remap the parameters */
|
|
ParameterMapEntry *parameterMapTable;
|
|
Aword *parameterMap;
|
|
Aint parameterNumber;
|
|
Common::Array<ParameterPosition> savedParameterPositions;
|
|
savedParameterPositions.resize(MAXPARAMS + 1);
|
|
|
|
for (parameterMapTable = (ParameterMapEntry *)pointerTo(header->parameterMapAddress); !isEndOfArray(parameterMapTable); parameterMapTable++)
|
|
if (parameterMapTable->syntaxNumber == syntaxNumber)
|
|
break;
|
|
if (isEndOfArray(parameterMapTable))
|
|
syserr("Could not find syntax in mapping table.");
|
|
|
|
parameterMap = (Aword *)pointerTo(parameterMapTable->parameterMapping);
|
|
|
|
copyParameterPositions(parameterPositions, &savedParameterPositions[0]);
|
|
|
|
for (parameterNumber = 1; !savedParameterPositions[parameterNumber - 1].endOfList; parameterNumber++) {
|
|
parameterPositions[parameterNumber - 1] = savedParameterPositions[parameterMap[parameterNumber - 1] - 1];
|
|
}
|
|
|
|
return parameterMapTable->verbCode;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool hasBit(Aword flags, Aint bit) {
|
|
return (flags & bit) != 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool multipleAllowed(Aword flags) {
|
|
return hasBit(flags, MULTIPLEBIT);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* There are a number of ways that the number of parameters might
|
|
* be more than one:
|
|
*
|
|
* 1) Player used ALL and it matched more than one
|
|
*
|
|
* 2) Player explicitly referred to multiple objects
|
|
*
|
|
* 3) Player did a single (or multiple) reference that was ambiguous
|
|
* in which case we need to disambiguate it (them). If we want to do
|
|
* this after complete parsing we must be able to see the possible
|
|
* parameters for each of these references, e.g.:
|
|
*
|
|
* > take the vase and the book
|
|
*
|
|
* In this case it is a single parameterPosition, but multiple
|
|
* explicit references and each of those might match multiple
|
|
* instances in the game.
|
|
*/
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
static void parseParameterPosition(CONTEXT, ParameterPosition *parameterPosition,
|
|
Aword flags, void (*complexReferencesParser)(CONTEXT, ParameterPosition *parameterPosition)) {
|
|
parameterPosition->parameters = ensureParameterArrayAllocated(parameterPosition->parameters);
|
|
|
|
CALL1(complexReferencesParser, parameterPosition)
|
|
if (lengthOfParameterArray(parameterPosition->parameters) == 0) /* No object!? */
|
|
CALL1(error, M_WHAT)
|
|
|
|
if (parameterPosition->explicitMultiple && !multipleAllowed(flags))
|
|
CALL1(error, M_MULTIPLE)
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static ElementEntry *elementForParameter(ElementEntry *elms) {
|
|
/* Require a parameter if elms->code == 0! */
|
|
while (!isEndOfArray(elms) && elms->code != 0)
|
|
elms++;
|
|
if (isEndOfArray(elms))
|
|
return nullptr;
|
|
return elms;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static ElementEntry *elementForEndOfSyntax(ElementEntry *elms) {
|
|
while (!isEndOfArray(elms) && (Aword)elms->code != EOS)
|
|
elms++;
|
|
if (isEndOfArray(elms)) /* No match for EOS! */
|
|
return nullptr;
|
|
return elms;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static ElementEntry *elementForWord(ElementEntry *elms, Aint wordCode) {
|
|
while (!isEndOfArray(elms) && elms->code != wordCode)
|
|
elms++;
|
|
if (isEndOfArray(elms))
|
|
return nullptr;
|
|
return elms;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool isInstanceReferenceWord(int wordIndex) {
|
|
return isNounWord(wordIndex) || isAdjectiveWord(wordIndex) || isAllWord(wordIndex)
|
|
|| isLiteralWord(wordIndex) || isItWord(wordIndex) || isThemWord(wordIndex) || isPronounWord(wordIndex);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool endOfPlayerCommand(int wordIndex) {
|
|
return endOfWords(wordIndex) || isConjunctionWord(wordIndex);
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
static ElementEntry *parseInputAccordingToSyntax(CONTEXT, SyntaxEntry *syntax, ParameterPosition parameterPositions[]) {
|
|
ElementEntry *currentElement = elementTreeOf(syntax);
|
|
ElementEntry *nextElement = currentElement;
|
|
|
|
int parameterCount = 0;
|
|
while (nextElement != nullptr) {
|
|
/* Traverse the possible branches of currentElement to find a match, let the actual input control what we look for */
|
|
parameterPositions[parameterCount].endOfList = TRUE;
|
|
|
|
if (endOfPlayerCommand(currentWordIndex)) {
|
|
// TODO If a conjunction word is also some other type of word, like noun? What happens?
|
|
return elementForEndOfSyntax(currentElement);
|
|
}
|
|
|
|
/* Or an instance reference ? */
|
|
if (isInstanceReferenceWord(currentWordIndex)) {
|
|
/* If so, save word info for this parameterPosition */
|
|
nextElement = elementForParameter(currentElement);
|
|
if (nextElement != nullptr) {
|
|
// Create parameter structure for the parameter position based on player words
|
|
// but without resolving them
|
|
ParameterPosition *parameterPosition = ¶meterPositions[parameterCount];
|
|
R0CALL3(parseParameterPosition, parameterPosition, nextElement->flags, complexReferencesParser)
|
|
|
|
parameterPosition->flags = nextElement->flags;
|
|
parameterPosition->endOfList = FALSE;
|
|
|
|
currentElement = (ElementEntry *) pointerTo(nextElement->next);
|
|
parameterCount++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Or maybe preposition? */
|
|
if (isPrepositionWord(currentWordIndex) || isVerbWord(currentWordIndex)) {
|
|
/* A preposition? Or rather, an intermediate word? */
|
|
nextElement = elementForWord(currentElement, dictionary[playerWords[currentWordIndex].code].code);
|
|
if (nextElement != nullptr) {
|
|
currentWordIndex++; /* Word matched, go to next */
|
|
currentElement = (ElementEntry *) pointerTo(nextElement->next);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Anything else is an error, but we'll handle 'but' specially here */
|
|
if (isExceptWord(currentWordIndex))
|
|
R0CALL1(errorButAfterAll, currentWordIndex)
|
|
|
|
/* If we get here we couldn't match anything... */
|
|
nextElement = nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool anyExplicitMultiple(ParameterPosition parameterPositions[]) {
|
|
int i;
|
|
|
|
for (i = 0; !parameterPositions[i].endOfList; i++)
|
|
if (parameterPositions[i].explicitMultiple)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static bool anyAll(ParameterPosition parameterPositions[]) {
|
|
int i;
|
|
|
|
for (i = 0; !parameterPositions[i].endOfList; i++)
|
|
if (parameterPositions[i].all)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void checkRestrictedParameters(CONTEXT, ParameterPosition parameterPositions[], ElementEntry elms[]) {
|
|
RestrictionEntry *restriction;
|
|
static Parameter *localParameters = nullptr;
|
|
int i;
|
|
|
|
localParameters = ensureParameterArrayAllocated(localParameters);
|
|
clearParameterArray(localParameters);
|
|
|
|
for (i = 0; !parameterPositions[i].endOfList; i++)
|
|
addParameterToParameterArray(localParameters, ¶meterPositions[i].parameters[0]);
|
|
|
|
for (restriction = (RestrictionEntry *) pointerTo(elms->next); !isEndOfArray(restriction); restriction++) {
|
|
ParameterPosition *parameterPosition = ¶meterPositions[restriction->parameterNumber - 1];
|
|
if (parameterPosition->explicitMultiple) {
|
|
/* This was a multiple parameter position, so check all multipleCandidates */
|
|
for (i = 0; !isEndOfArray(¶meterPosition->parameters[i]); i++) {
|
|
copyParameter(&localParameters[restriction->parameterNumber - 1], ¶meterPosition->parameters[i]);
|
|
if (!restrictionCheck(restriction, parameterPosition->parameters[i].instance)) {
|
|
/* Multiple could be both an explicit list of instance references and an expansion of ALL */
|
|
if (!parameterPosition->all) {
|
|
char marker[80];
|
|
/* It wasn't ALL, we need to say something about it, so
|
|
* prepare a printout with $1/2/3
|
|
*/
|
|
Common::sprintf_s(marker, "($%ld)", (unsigned long) restriction->parameterNumber);
|
|
setGlobalParameters(localParameters);
|
|
output(marker);
|
|
CALL2(runRestriction, restriction, localParameters)
|
|
para();
|
|
}
|
|
parameterPosition->parameters[i].instance = 0; /* In any case remove it from the list */
|
|
}
|
|
}
|
|
} else {
|
|
if (!restrictionCheck(restriction, parameterPosition->parameters[0].instance)) {
|
|
CALL2(runRestriction, restriction, localParameters)
|
|
CALL0(abortPlayerCommand)
|
|
}
|
|
}
|
|
parameterPositions[restriction->parameterNumber - 1].checked = TRUE;
|
|
}
|
|
freeParameterArray(localParameters);
|
|
localParameters = nullptr;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void impossibleWith(CONTEXT, ParameterPosition parameterPositions[], int positionIndex) {
|
|
if (isPreBeta2(header->version)) {
|
|
error(context, M_CANT0);
|
|
} else {
|
|
printMessageWithInstanceParameter(M_IMPOSSIBLE_WITH, parameterPositions[positionIndex].parameters[0].instance);
|
|
error(context, NO_MSG);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void checkNonRestrictedParameters(CONTEXT, ParameterPosition parameterPositions[]) {
|
|
int positionIndex;
|
|
for (positionIndex = 0; !parameterPositions[positionIndex].endOfList; positionIndex++)
|
|
if (!parameterPositions[positionIndex].checked) {
|
|
if (parameterPositions[positionIndex].explicitMultiple) {
|
|
/* This was a multiple parameter position, check all multiple candidates and remove failing */
|
|
int i;
|
|
for (i = 0; !isEndOfArray(¶meterPositions[positionIndex].parameters[i]); i++)
|
|
if (parameterPositions[positionIndex].parameters[i].instance != 0) /* Skip any empty slots */
|
|
if (!isAObject(parameterPositions[positionIndex].parameters[i].instance))
|
|
parameterPositions[positionIndex].parameters[i].instance = 0;
|
|
} else if (!isAObject(parameterPositions[positionIndex].parameters[0].instance))
|
|
impossibleWith(context, parameterPositions, positionIndex);
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void restrictParametersAccordingToSyntax(CONTEXT, ParameterPosition parameterPositions[], ElementEntry *elms) {
|
|
uncheckAllParameterPositions(parameterPositions);
|
|
CALL2(checkRestrictedParameters, parameterPositions, elms)
|
|
CALL1(checkNonRestrictedParameters, parameterPositions)
|
|
|
|
int positionIndex;
|
|
for (positionIndex = 0; !parameterPositions[positionIndex].endOfList; positionIndex++)
|
|
compressParameterArray(parameterPositions[positionIndex].parameters);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void matchPronoun(CONTEXT, Parameter *parameter) {
|
|
static Parameter *pronounInstances = nullptr;
|
|
pronounInstances = ensureParameterArrayAllocated(pronounInstances);
|
|
|
|
int pronounCandidateCount = getPronounInstances(playerWords[parameter->firstWord].code, pronounInstances);
|
|
if (pronounCandidateCount == 0) {
|
|
CALL1(errorWhat, parameter->firstWord)
|
|
} else if (pronounCandidateCount > 1) {
|
|
CALL2(errorWhichPronoun, parameter->firstWord, pronounInstances)
|
|
} else {
|
|
parameter->candidates[0] = pronounInstances[0];
|
|
setEndOfArray(¶meter->candidates[1]);
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void matchNounPhrase(Parameter *parameter, ReferencesFinder adjectiveReferencesFinder, ReferencesFinder nounReferencesFinder) {
|
|
int i;
|
|
|
|
for (i = parameter->firstWord; i < parameter->lastWord; i++)
|
|
updateWithReferences(parameter->candidates, i, adjectiveReferencesFinder);
|
|
updateWithReferences(parameter->candidates, parameter->lastWord, nounReferencesFinder);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void instanceMatcher(CONTEXT, Parameter *parameter) {
|
|
Parameter *candidates = parameter->candidates;
|
|
int i;
|
|
|
|
if (parameter->isLiteral) {
|
|
candidates[0].instance = instanceFromLiteral(playerWords[parameter->firstWord].code - dictionarySize);
|
|
setEndOfArray(&candidates[1]);
|
|
} else if (parameter->isPronoun) {
|
|
CALL1(matchPronoun, parameter)
|
|
} else
|
|
matchNounPhrase(parameter, adjectiveReferencesForWord, nounReferencesForWord);
|
|
|
|
// Ensure that every candidate have the words, even if there where no candidates
|
|
candidates[0].firstWord = parameter->firstWord;
|
|
candidates[0].lastWord = parameter->lastWord;
|
|
for (i = 0; i < lengthOfParameterArray(candidates); i++) {
|
|
candidates[i].firstWord = parameter->firstWord;
|
|
candidates[i].lastWord = parameter->lastWord;
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void findCandidates(CONTEXT, Parameter parameters[], void (*instanceMatcher)(CONTEXT, Parameter *parameter)) {
|
|
int i;
|
|
|
|
for (i = 0; i < lengthOfParameterArray(parameters); i++) {
|
|
parameters[i].candidates = ensureParameterArrayAllocated(parameters[i].candidates);
|
|
CALL1(instanceMatcher, ¶meters[i])
|
|
|
|
parameters[i].candidates[0].isPronoun = parameters[i].isPronoun;
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void handleFailedParse(CONTEXT, ElementEntry *elms) {
|
|
if (elms == nullptr)
|
|
error(context, M_WHAT);
|
|
else if (elms->next == 0) { /* No verb code, verb not declared! */
|
|
/* TODO Does this ever happen? */
|
|
error(context, M_CANT0);
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void convertMultipleCandidatesToMultipleParameters(ParameterPosition parameterPositions[], Parameter multipleParameters[]) {
|
|
int parameterIndex;
|
|
for (parameterIndex = 0; !parameterPositions[parameterIndex].endOfList; parameterIndex++)
|
|
if (parameterPositions[parameterIndex].explicitMultiple) {
|
|
compressParameterArray(parameterPositions[parameterIndex].parameters);
|
|
copyParameterArray(multipleParameters, parameterPositions[parameterIndex].parameters);
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static ElementEntry *parseInput(CONTEXT, ParameterPosition *parameterPositions) {
|
|
ElementEntry *element;
|
|
SyntaxEntry *stx;
|
|
|
|
R0FUNC1(findSyntaxTreeForVerb, stx, verbWordCode)
|
|
R0FUNC2(parseInputAccordingToSyntax, element, stx, parameterPositions)
|
|
R0CALL1(handleFailedParse, element)
|
|
|
|
current.syntax = element->flags;
|
|
current.verb = remapParameterOrder(current.syntax, parameterPositions);
|
|
return element;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void findCandidatesForPlayerWords(CONTEXT, ParameterPosition *parameterPosition) {
|
|
ParameterArray parameters = parameterPosition->parameters;
|
|
|
|
if (!parameterArrayIsEmpty(parameters)) {
|
|
if (parameters[0].isThem) {
|
|
parameterPosition->them = TRUE;
|
|
getPreviousMultipleParameters(parameters);
|
|
if (lengthOfParameterArray(parameters) == 0)
|
|
CALL1(errorWhat, parameters[0].firstWord)
|
|
if (lengthOfParameterArray(parameters) > 1)
|
|
parameterPosition->explicitMultiple = TRUE;
|
|
} else if (parameterPosition->all) {
|
|
CALL1(buildAllHere, parameters)
|
|
if (!parameterArrayIsEmpty(parameterPosition->exceptions))
|
|
CALL2(findCandidates, parameterPosition->exceptions, instanceMatcher)
|
|
} else
|
|
CALL2(findCandidates, parameters, instanceMatcher)
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void handleMultiplePosition(CONTEXT, ParameterPosition parameterPositions[]) {
|
|
int multiplePosition = findMultipleParameterPosition(parameterPositions);
|
|
if (anyAll(parameterPositions)) {
|
|
/* If the player used ALL, try to find out what was applicable */
|
|
CALL3(disambiguateCandidatesForPosition, parameterPositions, multiplePosition,
|
|
parameterPositions[multiplePosition].parameters)
|
|
|
|
if (lengthOfParameterArray(parameterPositions[multiplePosition].parameters) == 0)
|
|
CALL1(errorWhat, parameterPositions[multiplePosition].parameters[0].firstWord)
|
|
|
|
subtractParameterArrays(parameterPositions[multiplePosition].parameters, parameterPositions[multiplePosition].exceptions);
|
|
if (lengthOfParameterArray(parameterPositions[multiplePosition].parameters) == 0)
|
|
CALL1(error, M_NOT_MUCH)
|
|
} else if (anyExplicitMultiple(parameterPositions)) {
|
|
compressParameterArray(parameterPositions[multiplePosition].parameters);
|
|
if (lengthOfParameterArray(parameterPositions[multiplePosition].parameters) == 0) {
|
|
/* If there where multiple parameters but non left, exit without a */
|
|
/* word, assuming we have already said enough */
|
|
CALL0(abortPlayerCommand)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Disambiguation is hard: there are a couple of different cases that
|
|
* we want to handle: Omnipotent parameter position, multiple present
|
|
* and non-present objects etc. The following table will show which
|
|
* message we would like to give in the various situations.
|
|
*
|
|
* p = present, n = non-present, 1 = single, m = multiple
|
|
* (1p1n = single present, single non-present)
|
|
*
|
|
* p, n, omni, result, why?
|
|
* -----------------------------------------------------------------
|
|
* 0, 0, no, errorNoSuch(w)/errorWhat(w)
|
|
* 0, 1, no, errorNoSuch(w)/errorWhat(w)
|
|
* 0, m, no, errorNoSuch(w)/errorWhat(w)
|
|
* 1, 0, no, ok(p)
|
|
* 1, 1, no, ok(p)
|
|
* 1, m, no, ok(p)
|
|
* m, 0, no, errorWhichOne(p)
|
|
* m, 1, no, errorWhichOne(p) only present objects should be revealed
|
|
* m, m, no, errorWhichOne(p) d:o
|
|
|
|
* 0, 0, yes, errorNoSuch(w)
|
|
* 0, 1, yes, ok(n)
|
|
* 0, m, yes, errorWhichOne(n) already looking "beyond" presence, might reveal undiscovered distant objects
|
|
* 1, 0, yes, ok(p)
|
|
* 1, 1, yes, ok(p) present objects have priority
|
|
* 1, m, yes, ok(p) present objects have priority
|
|
* m, 0, yes, errorWhichOne(p)
|
|
* m, 1, yes, errorWhichOne(p) present objects have priority, but only list present
|
|
* m, m, yes, errorWhichOne(p) present objects have priority, but only list present
|
|
*/
|
|
|
|
|
|
typedef Parameter *DisambiguationHandler(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]);
|
|
typedef DisambiguationHandler *DisambiguationHandlerTable[3][3][2];
|
|
|
|
static Parameter *disambiguate00N(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
if (allCandidates[0].isPronoun)
|
|
R0CALL1(errorWhat, allCandidates[0].firstWord)
|
|
else
|
|
R0CALL1(errorNoSuch, allCandidates[0]);
|
|
return nullptr;
|
|
}
|
|
|
|
static Parameter *disambiguate01N(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
if (allCandidates[0].isPronoun)
|
|
R0CALL1(errorWhat, allCandidates[0].firstWord)
|
|
else
|
|
R0CALL1(errorNoSuch, allCandidates[0])
|
|
return nullptr;
|
|
}
|
|
|
|
static Parameter *disambiguate0MN(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
if (allCandidates[0].isPronoun)
|
|
R0CALL1(errorWhat, allCandidates[0].firstWord)
|
|
else
|
|
R0CALL1(errorNoSuch, allCandidates[0])
|
|
return nullptr;
|
|
}
|
|
|
|
static Parameter *disambiguate10N(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
return presentCandidates;
|
|
}
|
|
|
|
static Parameter *disambiguate11N(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
return presentCandidates;
|
|
}
|
|
|
|
static Parameter *disambiguate1MN(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
return presentCandidates;
|
|
}
|
|
static Parameter *disambiguateM0N(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
R0CALL1(errorWhichOne, presentCandidates)
|
|
return nullptr;
|
|
}
|
|
|
|
static Parameter *disambiguateM1N(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
R0CALL1(errorWhichOne, presentCandidates)
|
|
return nullptr;
|
|
}
|
|
|
|
static Parameter *disambiguateMMN(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
R0CALL1(errorWhichOne, presentCandidates)
|
|
return nullptr;
|
|
}
|
|
|
|
static Parameter *disambiguate00Y(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
errorNoSuch(context, allCandidates[0]);
|
|
return nullptr;
|
|
}
|
|
static Parameter *disambiguate01Y(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
return allCandidates;
|
|
}
|
|
static Parameter *disambiguate0MY(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
R0CALL1(errorWhichOne, allCandidates)
|
|
return nullptr;
|
|
}
|
|
static Parameter *disambiguate10Y(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
return presentCandidates;
|
|
}
|
|
static Parameter *disambiguate11Y(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
return presentCandidates;
|
|
}
|
|
static Parameter *disambiguate1MY(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
return presentCandidates;
|
|
}
|
|
static Parameter *disambiguateM0Y(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
R0CALL1(errorWhichOne, presentCandidates)
|
|
return nullptr;
|
|
}
|
|
static Parameter *disambiguateM1Y(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
R0CALL1(errorWhichOne, presentCandidates)
|
|
return nullptr;
|
|
}
|
|
static Parameter *disambiguateMMY(CONTEXT, Parameter allCandidates[], Parameter presentCandidates[]) {
|
|
R0CALL1(errorWhichOne, presentCandidates)
|
|
return nullptr;
|
|
}
|
|
|
|
static DisambiguationHandlerTable disambiguationHandlerTable = {
|
|
{
|
|
// Present == 0
|
|
{
|
|
// Distant == 0
|
|
disambiguate00N, disambiguate00Y
|
|
},
|
|
{
|
|
// Distant == 1
|
|
disambiguate01N, disambiguate01Y
|
|
},
|
|
{
|
|
// Distant == M
|
|
disambiguate0MN, disambiguate0MY
|
|
}
|
|
},
|
|
{
|
|
// Present == 1
|
|
{
|
|
// Distant == 0
|
|
disambiguate10N, disambiguate10Y
|
|
},
|
|
{
|
|
// Distant == 1
|
|
disambiguate11N, disambiguate11Y
|
|
},
|
|
{
|
|
// Distant == M
|
|
disambiguate1MN, disambiguate1MY
|
|
}
|
|
},
|
|
{
|
|
// Present == M
|
|
{
|
|
// Distant == 0
|
|
disambiguateM0N, disambiguateM0Y
|
|
},
|
|
{
|
|
// Distant == 1
|
|
disambiguateM1N, disambiguateM1Y
|
|
},
|
|
{
|
|
// Distant == M
|
|
disambiguateMMN, disambiguateMMY
|
|
}
|
|
}
|
|
};
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void disambiguateCandidates(CONTEXT, Parameter *allCandidates, bool omnipotent, bool (*reachable)(int), DisambiguationHandlerTable handler) {
|
|
static Parameter *presentCandidates = nullptr;
|
|
int present;
|
|
int distant;
|
|
Parameter *result;
|
|
|
|
presentCandidates = ensureParameterArrayAllocated(presentCandidates);
|
|
|
|
copyParameterArray(presentCandidates, allCandidates);
|
|
filterOutNonReachable(presentCandidates, reachable);
|
|
|
|
present = lengthOfParameterArray(presentCandidates);
|
|
if (present > 1) present = 2; /* 2 = M */
|
|
|
|
distant = lengthOfParameterArray(allCandidates) - present;
|
|
if (distant > 1) distant = 2; /* 2 = M */
|
|
|
|
FUNC2(handler[present][distant][omnipotent], result, allCandidates, presentCandidates)
|
|
|
|
// If we returned then it's ok, use the single candidate found
|
|
allCandidates[0] = result[0];
|
|
setEndOfArray(&allCandidates[1]);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void disambiguate(CONTEXT, ParameterPosition parameterPositions[], ElementEntry *element) {
|
|
/* The New Strategy! Parsing has only collected word indications,
|
|
not built anything, so we need to match parameters to instances here */
|
|
|
|
int position;
|
|
for (position = 0; !parameterPositions[position].endOfList; position++) {
|
|
CALL1(findCandidatesForPlayerWords, ¶meterPositions[position])
|
|
}
|
|
|
|
/* Now we have candidates for everything the player said, except
|
|
if he used ALL or THEM, then we have built those as parameters, or he
|
|
referred to the multiple parameters of the previous command
|
|
using 'them, if so, they too are stored as parameters */
|
|
|
|
for (position = 0; !parameterPositions[position].endOfList; position++) {
|
|
ParameterPosition *parameterPosition = ¶meterPositions[position];
|
|
bool omni = hasBit(parameterPosition->flags, OMNIBIT);
|
|
if (!parameterPosition->all && !parameterPosition->them) {
|
|
Parameter *parameters = parameterPosition->parameters;
|
|
int p;
|
|
for (p = 0; p < lengthOfParameterArray(parameters); p++) {
|
|
Parameter *parameter = ¶meters[p];
|
|
Parameter *candidates = parameter->candidates;
|
|
CALL4(disambiguateCandidates, candidates, omni, reachable, disambiguationHandlerTable)
|
|
|
|
parameter->instance = candidates[0].instance;
|
|
}
|
|
}
|
|
if (parameterPosition->all) {
|
|
Parameter *exceptions = parameterPosition->exceptions;
|
|
int p;
|
|
for (p = 0; p < lengthOfParameterArray(exceptions); p++) {
|
|
Parameter *parameter = &exceptions[p];
|
|
Parameter *candidates = parameter->candidates;
|
|
CALL4(disambiguateCandidates, candidates, omni, reachable, disambiguationHandlerTable)
|
|
|
|
parameter->instance = candidates[0].instance;
|
|
}
|
|
}
|
|
}
|
|
|
|
CALL1(handleMultiplePosition, parameterPositions)
|
|
|
|
/* Now perform restriction checks */
|
|
CALL2(restrictParametersAccordingToSyntax, parameterPositions, element)
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
static void tryParam(CONTEXT, Parameter parameters[], Parameter multipleParameters[]) {
|
|
ElementEntry *element; /* Pointer to element list */
|
|
static ParameterPosition *parameterPositions = nullptr;
|
|
if (parameterPositions != nullptr)
|
|
deallocateParameterPositions(parameterPositions);
|
|
|
|
// TODO newParameterPositionArray()!!!! Or even reallocatePP.. or cleanPP..
|
|
parameterPositions = (ParameterPosition *)allocate(sizeof(ParameterPosition) * (MAXPARAMS + 1));
|
|
parameterPositions[0].endOfList = TRUE;
|
|
|
|
FUNC1(parseInput, element, parameterPositions)
|
|
CALL2(disambiguate, parameterPositions, element)
|
|
|
|
// TODO: Now we need to convert back to legacy parameter and multipleParameter format
|
|
convertPositionsToParameters(parameterPositions, parameters);
|
|
markExplicitMultiple(parameterPositions, parameters);
|
|
convertMultipleCandidatesToMultipleParameters(parameterPositions, multipleParameters);
|
|
|
|
deallocateParameterPositions(parameterPositions);
|
|
parameterPositions = nullptr;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
|
|
static void parseOneCommand(CONTEXT, Parameter parameters[], Parameter multipleParameters[]) {
|
|
// ... to understand what he said
|
|
CALL2(tryParam, parameters, multipleParameters)
|
|
|
|
/* More on this line? */
|
|
if (!endOfWords(currentWordIndex)) {
|
|
if (isConjunctionWord(currentWordIndex))
|
|
currentWordIndex++; /* If so skip the conjunction */
|
|
else
|
|
error(context, M_WHAT);
|
|
}
|
|
}
|
|
|
|
/*======================================================================*/
|
|
void initParsing(void) {
|
|
currentWordIndex = 0;
|
|
continued = FALSE;
|
|
ensureSpaceForPlayerWords(0);
|
|
clearWordList(playerWords);
|
|
|
|
pronouns = allocatePronounArray(pronouns);
|
|
globalParameters = ensureParameterArrayAllocated(globalParameters);
|
|
previousMultipleParameters = ensureParameterArrayAllocated(previousMultipleParameters);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static int pronounWordForInstance(int instance) {
|
|
/* Scan through the dictionary to find any pronouns that can be used
|
|
for this instance */
|
|
int w;
|
|
|
|
for (w = 0; w < dictionarySize; w++)
|
|
if (isPronoun(w)) {
|
|
Aword *reference = (Aword *)pointerTo(dictionary[w].pronounRefs);
|
|
while (*reference != EOD) {
|
|
if (*reference == (Aword)instance)
|
|
return dictionary[w].code;
|
|
reference++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void addPronounForInstance(int thePronoun, int instanceCode) {
|
|
int i;
|
|
|
|
for (i = 0; !endOfPronouns(i); i++)
|
|
if (pronouns[i].pronoun == thePronoun && pronouns[i].instance == instanceCode)
|
|
// Don't add the same instance twice for the same pronoun
|
|
return;
|
|
pronouns[i].pronoun = thePronoun;
|
|
pronouns[i].instance = instanceCode;
|
|
setEndOfArray(&pronouns[i + 1]);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void notePronounsForParameters(Parameter parameters[]) {
|
|
/* For all parameters note which ones can be referred to by a pronoun */
|
|
Parameter *p;
|
|
|
|
clearPronounList(pronouns);
|
|
for (p = parameters; !isEndOfArray(p); p++) {
|
|
int pronoun = pronounWordForInstance(p->instance);
|
|
if (pronoun > 0)
|
|
addPronounForInstance(pronoun, p->instance);
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void parseVerbCommand(CONTEXT, Parameter parameters[], Parameter multipleParameters[]) {
|
|
verbWord = playerWords[currentWordIndex].code;
|
|
verbWordCode = dictionary[verbWord].code;
|
|
if (isPreBeta2(header->version))
|
|
/* Pre-beta2 didn't generate syntax elements for verb words,
|
|
need to skip first word which should be the verb */
|
|
currentWordIndex++;
|
|
CALL2(parseOneCommand, parameters, multipleParameters)
|
|
notePronounsForParameters(parameters);
|
|
fail = FALSE;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static void parseInstanceCommand(CONTEXT, Parameter parameters[], Parameter multipleParameters[]) {
|
|
/* Pick up the parse tree for the syntaxes that start with an
|
|
instance reference and parse according to that. The
|
|
verbWord code is set to 0 to indicate that it is not a verb
|
|
but an instance that starts the command. */
|
|
verbWordCode = 0;
|
|
CALL2(parseOneCommand, parameters, multipleParameters)
|
|
notePronounsForParameters(parameters);
|
|
fail = FALSE;
|
|
}
|
|
|
|
|
|
/*======================================================================*/
|
|
void parse(CONTEXT) {
|
|
/* longjmp's ahead so these need to survive to not leak memory */
|
|
static Parameter *parameters = nullptr;
|
|
static Parameter *multipleParameters = nullptr;
|
|
parameters = ensureParameterArrayAllocated(parameters);
|
|
multipleParameters = ensureParameterArrayAllocated(multipleParameters);
|
|
|
|
if (endOfWords(currentWordIndex)) {
|
|
currentWordIndex = 0;
|
|
CALL0(scan)
|
|
} else if (anyOutput) {
|
|
para();
|
|
}
|
|
|
|
capitalize = TRUE;
|
|
|
|
firstWord = currentWordIndex;
|
|
if (isVerbWord(currentWordIndex)) {
|
|
CALL2(parseVerbCommand, parameters, multipleParameters);
|
|
CALL3(action, current.verb, parameters, multipleParameters)
|
|
} else if (isDirectionWord(currentWordIndex)) {
|
|
clearParameterArray(previousMultipleParameters);
|
|
clearPronounList(pronouns);
|
|
CALL0(handleDirectionalCommand)
|
|
} else if (isInstanceReferenceWord(currentWordIndex)) {
|
|
CALL2(parseInstanceCommand, parameters, multipleParameters)
|
|
CALL3(action, current.verb, parameters, multipleParameters)
|
|
} else
|
|
CALL1(error, M_WHAT)
|
|
|
|
if (fail)
|
|
CALL1(error, NO_MSG)
|
|
|
|
lastWord = currentWordIndex - 1;
|
|
if (isConjunctionWord(lastWord))
|
|
lastWord--;
|
|
|
|
if (lengthOfParameterArray(parameters) > 0)
|
|
copyParameterArray(previousMultipleParameters, multipleParameters);
|
|
else
|
|
clearParameterArray(previousMultipleParameters);
|
|
|
|
freeParameterArray(parameters);
|
|
parameters = nullptr;
|
|
freeParameterArray(multipleParameters);
|
|
multipleParameters = nullptr;
|
|
}
|
|
|
|
} // End of namespace Alan3
|
|
} // End of namespace Glk
|