diff options
Diffstat (limited to 'engines/glk/jacl/parser.cpp')
-rw-r--r-- | engines/glk/jacl/parser.cpp | 1918 |
1 files changed, 1918 insertions, 0 deletions
diff --git a/engines/glk/jacl/parser.cpp b/engines/glk/jacl/parser.cpp new file mode 100644 index 0000000000..b9787c8ac7 --- /dev/null +++ b/engines/glk/jacl/parser.cpp @@ -0,0 +1,1918 @@ +/* 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "glk/jacl/jacl.h" +#include "glk/jacl/language.h" +#include "glk/jacl/types.h" +#include "glk/jacl/prototypes.h" + +namespace Glk { +namespace JACL { + +#define FIRST_LIST noun_number-2 +#define SECOND_LIST noun_number + +int object_list[4][MAX_OBJECTS]; +int multiple_resolved[MAX_OBJECTS]; + +// THIS IS THE NUMBER OF OBJECTS LEFT IN THE LIST +int list_size[4]; + +// THIS IS THE INDEX OF THE FINAL OBJECT +int max_size[4]; + +/* object_list[] HAS THE FOLLOWING INDEXES: + * noun1 : 0 + * noun2 : 1 + * noun1 EXCEPTIONS : 2 + * noun2 EXCEPTIONS : 3 + */ + +int selection; + +int matches = 0; +int highest_confidence = 0; +int prime_suspect = 0; +int done = 0; +int backup_pointer = 0; +int everything = 0; + +int confidence[MAX_OBJECTS]; +int possible_objects[MAX_OBJECTS]; + +int it; +int them[MAX_OBJECTS]; +int her; +int him; +int parent; + +int custom_error; + +int oops_word; +int last_exact; + +char *expected_scope[3]; + +// THIS ARRAY DEFINES THE OBJECTS THAT THE CURRENT OBJECTS ARE +// SUPPOSED TO BE A CHILD OF +int from_objects[MAX_OBJECTS]; + +int after_from; +char *from_word; + +int object_expected = FALSE; + +char default_function[84]; +char object_name[84]; + +char base_function[84]; +char before_function[84]; +char after_function[84]; +char local_after_function[84]; + +extern char text_buffer[]; +extern char function_name[]; +extern char temp_buffer[]; +extern char error_buffer[]; +extern char override[]; +extern char *word[]; + +extern int quoted[]; + +extern struct object_type *object[]; +extern int objects; + +extern int noun[]; +extern int wp; +extern int player; + +extern struct word_type *grammar_table; +extern struct function_type *executing_function; +extern struct object_type *object[]; +extern struct variable_type *variable[]; + +void parser() { + // THIS FUNCTION COMPARES THE WORDS IN THE PLAYER'S COMMAND TO THE + // GRAMMAR TREE OF POSSIBLE COMMANDS + + struct word_type *pointer; + struct word_type *matched_word = NULL; + + int index; + int current_noun = 0; + + // RESET TO START OF PROCESSING + + ////printf("--- in parser\n"); + + // THIS IS USED TO STORE THE LAST EXACT WORD MATCH IN THE PLAYERS COMMAND + last_exact = -1; + after_from = -1; + from_objects[0] = 0; + + noun[0] = FALSE; + noun[1] = FALSE; + + // RESET BOTH THE LISTS TO BE EMPTY + //printf("--- reset lists\n"); + for (index = 0; index < 4; index++) { + list_size[index] = 0; + max_size[index] = 0; + } + + //printf("--- clear $integer\n"); + clear_cinteger("$integer"); + //printf("--- clear $string\n"); + clear_cstring("$string"); + //printf("--- clear action\n"); + clear_cstring("action"); + + if (grammar_table == NULL) { + // THERE ARE NO USER DEFINED COMMANDS AVAILABLE, SO THE USER'S + // COMMAND IS INEVITABLY INVALID + //printf("--- no grammar table\n"); + INTERRUPTED->value = TRUE; + diagnose(); + return; + } + + //printf("--- set pointer to grammar table\n"); + // START AT THE TOP OF THE GRAMMAR TREE + pointer = grammar_table; + + while (word[wp] != NULL && pointer != NULL) { + //printf("--- wp = %d\n", wp); + //printf("--- word[%d] = %s\n", wp, word[wp]); + object_expected = FALSE; + + if (!strcmp(cstring_resolve("THEN_WORD")->value, word[wp])) { + // CONSIDER THIS THE END OF THIS COMMAND AS 'THEN' IS + // TREATED LIKE A FULL STOP + break; + } else if ((matched_word = exact_match(pointer)) != NULL) { + // THIS WORD WAS AN EXACT MATCH FOR ONE OF THE POSSIBLE WORDS + // AT THE CURRENT GRAMMAR TREE LEVEL - MOVE ON! + pointer = matched_word; + pointer = pointer->first_child; + } else if ((matched_word = object_match(pointer, current_noun)) != NULL) { + // THIS WAS AN OBJECT PLACE HOLDER AT THIS GRAMMAR LEVEL AND + // THIS POINT IN THE PLAYER'S COMMAND COULD BE RESOLVED TO + // AT LEAST ONE OBJECT + + if (list_size[current_noun] > 1) { + // MULTIPLE OBJECTS WERE RETURNED + if (matched_word->word[1] != '*') { + if (last_exact == -1) { + write_text(cstring_resolve("NO_MULTI_START")->value); + } else { + sprintf(error_buffer, cstring_resolve("NO_MULTI_VERB")->value, word[last_exact]); + write_text(error_buffer); + } + + INTERRUPTED->value = TRUE; + return; + } + } + + // STORE THE EXPECTED SCOPE FOR LATER CHECKING + expected_scope[current_noun] = (char *) &matched_word->word; + //printf("--- expected scope for noun%d is %s\n", current_noun, expected_scope[current_noun]); + + // THE NEXT MATCH OR GROUP OF MATCHES SHOULD BE IN THE SECOND + // LIST OF OBJECTS + current_noun++; + + // PUSH ON FROM THE POINT A MATCH WAS FOUND.... + pointer = matched_word; + pointer = pointer->first_child; + } else { + // THIS IS AN UNKNOWN WORD + if (oops_word == -1 && word[wp] != NULL) { + oops_word = wp; + } + + if (custom_error == TRUE) { + // THERE HAS BEEN SOME CUSTOM ERROR DISPLAYED ALREADY + // SO JUST RETURN + TIME->value = FALSE; + INTERRUPTED->value = TRUE; + return; + } else { + // THERE ARE NO MORE POSIBILITIES, THE WORD CAN'T BE + // USED IN THIS CONTEXT + INTERRUPTED->value = TRUE; + diagnose(); + return; + } + } + }; + + if (pointer == NULL) { + // THIS CAN ONLY HAPPEN IF MOVING THE POINTER TO ITS + // FIRST CHILD RESULTS IN A NULL - AN INCOMPLETE + // GRAMMAR STATEMENT. + log_error(INCOMPLETE_GRAMMAR, PLUS_STDOUT); + INTERRUPTED->value = TRUE; + return; + } + + //printf("--- first list has %d objects\n", list_size[0]); + //printf("--- first list has %d max\n", max_size[0]); + //printf("--- second list has %d objects\n", list_size[1]); + //printf("--- second list has %d max\n", max_size[1]); + + /* THE PLAYER'S MOVE HAS NO MORE WORDS, AND A BRANCH OF + * THE GRAMMAR TREE HAS BEEN FOUND THAT SO FAR MATCHES + * THIS MOVE. SCAN THROUGH ALL THE CHILDREN OF THE FINAL + * WORD OF THE PLAYER'S COMMAND AND FIND A FUNCTION BASE. + * (FUNCTION BASES BEGIN WITH '>'). IF THIS IS FOUND, + * CALL THE APPROPRIATE JACL FUNCTIONS IN THE GAME CODE + * IF NO FUNCTION BASE IS FOUND, MORE WORDS WERE EXPECTED + * IN ORDER TO CONSTRUCT A COMPLETE COMMAND. SHOW ERROR. */ + do { + if (pointer->word[0] == '>') { + //printf("--- found action %s\n", pointer->word); + /* CALL ALL THE APPROPRIATE FUNCTIONS FOR EACH OF */ + /* THE OBJECTS IN THE SET */ + add_cstring("action", &pointer->word[1]); + + if (list_size[0] > 1) { + /* FIRST IS MULTI, PRESUME SECOND IS SINGLE OR ZERO AS YOU + * CAN HAVE COMMANDS WITH TWO MULTIPLE OBJECT REFERENCES */ + noun[1] = first_available(1); + + for (index = 0; index < max_size[0]; index++) { + /* CALL ALL THE FUNCTIONS ONCE FOR EACH OJECT */ + if (object_list[0][index] != 0) { + noun[0] = object_list[0][index]; + + if (MULTI_PREFIX->value) { + // DISPLAY THE OBJECT BEING ACTED ON + plain_output(noun[0], FALSE); + write_text(temp_buffer); + write_text(": "); + } + + //printf("--- 1 calling functions for %s\n", object[noun[0]]->label); + call_functions(pointer->word); + + /* IF INTERRUPTED BY SOME SPECIAL CONDITION, DON'T + * PERFORM THIS COMMAND FOR THE REMAINING OBJECTS */ + if (INTERRUPTED->value) { + break; + } + } + } + } else { + // FIRST LIST ISN'T MULTI + //printf("--- first list isn't multi\n"); + if (list_size[1] > 1) { + //printf("--- second list is multi\n"); + // ONLY SECOND IS MULTI + noun[0] = first_available(0); + + for (index = 0; index < max_size[1]; index++) { + /* CALL ALL THE FUNCTIONS ONCE FOR EACH OJECT */ + if (object_list[1][index] != 0) { + noun[1] = object_list[1][index]; + + if (MULTI_PREFIX->value) { + // DISPLAY THE OBJECT BEING ACTED ON + plain_output(noun[0], FALSE); + write_text(temp_buffer); + write_text(": "); + } + + //printf("--- 2 calling functions for %s\n", object[noun[1]]->label); + call_functions(pointer->word); + + /* IF INTERRUPTED BY SOME SPECIAL CONDITION, DON'T + * PERFORM THIS COMMAND FOR THE REMAINING OBJECTS */ + if (INTERRUPTED->value) { + break; + } + } + } + } else { + //printf("--- second list isn't multi\n"); + // NEITHER OBJECT REFERENCE IS MULTI + if (list_size[0] == 0) { + noun[0] = 0; + noun[1] = 0; + + /* THIS IS AN OBJECT-LESS COMMAND */ + call_functions(pointer->word); + } else { + noun[0] = first_available(0); + noun[1] = first_available(1); + + //printf("--- 3 calling functions for %s and %s\n", object[noun[0]]->label, object[noun[1]]->label); + call_functions(pointer->word); + } + } + } + if (TIME->value == FALSE) { + /* IS THIS ENOUGH TO INDICATE THAT THE OTHER COMMANDS + * SHOULDN'T BE PERFORMED??? */ + //INTERRUPTED->value = TRUE; + } + return; + } else { + //printf("--- move to next grammar sibling\n"); + // MOVE THROUGH THE OPTIONS AT THIS LEVEL OF THE GRAMMAR TREE + // TO FIND THE ACTION THAT MATCHES THIS COMMAND + if (pointer->next_sibling == NULL) { + break; + } else { + pointer = pointer->next_sibling; + } + } + } while (TRUE); + + /* THERE IS NO FUNCTION AS A CHILD OF THE LAST WORD OF THE PLAYER'S + * COMMAND SO DISPLAY AN IN-GAME ERROR TO THE PLAYER. THIS IS LIKELY + * TO BE AN INCOMPLETE COMMAND */ + INTERRUPTED->value = TRUE; + diagnose(); + return; +} + +/* THIS FUNCTION RETURNS THE FIRST OBJECT IN THE SPECIFIED LIST + * OR 0 IF IT IS EMPTY. THE FIRST OBJECT IN THE LIST IS THE FIRST + * ELEMENT THAT IS NON-ZERO BEFORE THE max-size IS REACHED. */ +int first_available(int list_number) { + int index; + + if (list_size[list_number] == 0) return (0); + + //printf("--- looking for next object in list\n"); + for (index = 0; index < max_size[list_number]; index++) { + if (object_list[list_number][index] != 0) { + //printf("--- returning object %s\n", object[object_list[list_number][index]]->label); + return (object_list[list_number][index]); + } + } + + //printf("--- no objects left in list\n"); + /* NO OBJECTS LEFT IN THE LIST */ + return (0); +} + +void call_functions(char *base_name) { + /* THIS FUNCTION CALLS ALL THE APPROPRIATE JACL FUNCTIONS TO RESPOND + * TO A PLAYER'S COMMAND GIVEN A BASE FUNCTION NAME AND THE CURRENT + * VALUE OF noun1 AND noun2 */ + + /* THE DEFAULT IS THAT THE COMMAND IS SUCCESSFUL AND THAT TIME SHOULD + * PASS. IF THE COMMAND FAILS, 'TIME' WILL BE SET TO FALSE */ + TIME->value = TRUE; + + strncpy(base_function, base_name + 1, 80); + strcat(base_function, "_"); + + strncpy(override, base_function, 80); + + strcpy(before_function, "+before_"); + strcat(before_function, base_name + 1); + + strcpy(after_function, "+after_"); + strcat(after_function, base_name + 1); + + strcpy(local_after_function, "after_"); + strcat(local_after_function, base_name + 1); + if (noun[1] != FALSE) { + strcat(local_after_function, "_"); + strcat(local_after_function, object[noun[1]]->label); + } + if (noun[0] != FALSE) { + strcat(local_after_function, "_"); + strcat(local_after_function, object[noun[0]]->label); + } + + /* THIS IS CALLED IF AN 'override' COMMAND IS EXECUTED + * IN A LIBRARY FUNCTION BUT THE OBJECT-OR-LOCATION-SPECIFIC + * OVERRIDE DOES NOT EXIST. IT IS SET TO '+default_func' */ + strcpy(default_function, "+default_"); + strcat(default_function, base_name + 1); + + /* EXECUTE THE GLOBAL *DEFAULT* BEFORE FUNCTION + * AND RETURN IF IT RETURNS TRUE */ + if (execute("+before") != FALSE) + return; + + /* EXECUTE THE VERB-SPECIFIC BEFORE + FUNCTION AND RETURN IF IT RETURNS TRUE */ + if (execute(before_function) != FALSE) + return; + + if (noun[0] == FALSE) { /* USER'S COMMAND HAS NO NOUNS */ + strcat(base_function, object[HERE]->label); + /* EXECUTE THE FUNCTION 'func_here' */ + if (execute(base_function) == FALSE) { + /* THIS LOCATION-SPECIFIC FUNCTION DOES NOT + * EXIST OR HAS ISSUED A 'break false' COMMAND. + * EXECUTE THE FUNCTION '+func' + * WITH THE POSSIBLILITY OF + * EXECUTING THE FUNCTION 'func_override_here' + * IF AN 'override' COMMAND IS FOUND IN '+func' + * IF THIS OVERRIDE FUNCTION ISN'T FOUND + * THE DEFAULT FUNCTION WILL BE EXECUTED */ + + /* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT + * IS NEEDED */ + strcat(override, "override_"); + strcat(override, object[HERE]->label); + + /* CREATE THE FUNCTION NAME '+func' */ + strcpy(base_function, "+"); + strcat(base_function, base_name + 1); + + /* CALL THE LIBRARY'S DEFAULT BEHAVIOR */ + if (execute(base_function) == FALSE) + unkfunrun(base_function); + } + } else if (noun[1] == FALSE) { /* USER'S COMMAND HAS ONE NOUN */ + strcat(base_function, object[noun[0]]->label); + /* EXECUTE THE FUNCTION 'func_noun1' */ + if (execute(base_function) == FALSE) { + /* THIS OBJECT-SPECIFIC FUNCTION DOES NOT + * EXIST OR HAS ISSUED A 'break false' COMMAND. + * EXECUTE THE FUNCTION '+func' + * WITH THE POSSIBLILITY OF + * EXECUTING THE FUNCTION 'func_override_noun1' + * IF AN 'override' COMMAND IS FOUND IN '+func' + * IF THIS OVERRIDE FUNCTION ISN'T FOUND + * THE DEFAULT FUNCTION WILL BE EXECUTED */ + + /* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT + * IS NEEDED */ + strcat(override, "override_"); + strcat(override, object[noun[0]]->label); + + /* CREATE THE FUNCTION NAME '+func' */ + strcpy(base_function, "+"); + strcat(base_function, base_name + 1); + + /* CALL THE LIBRARY'S DEFAULT BEHAVIOR */ + if (execute(base_function) == FALSE) + unkfunrun(base_function); + } + } else { /* USER'S COMMAND HAS TWO NOUNS */ + strcat(base_function, object[noun[1]]->label); + strcat(base_function, "_"); + strcat(base_function, object[noun[0]]->label); + /* EXECUTE THE FUNCTION 'func_noun2_noun1' + * IE give_to_fred THAT IS ASSOCIATED WITH + * THE OBJECT flint_stone */ + if (execute(base_function) == FALSE) { + /* THIS OBJECTS-SPECIFIC FUNCTION DOES NOT + * EXIST OR HAS ISSUED A 'break false' COMMAND. + * EXECUTE THE FUNCTION '+func' + * WITH THE POSSIBLILITY OF + * EXECUTING THE FUNCTION 'func_override_noun2_noun1' + * IF AN 'override' COMMAND IS FOUND IN '+func' + * IF THIS OVERRIDE FUNCTION ISN'T FOUND + * THE DEFAULT FUNCTION WILL BE EXECUTED */ + + /* PREPARE THE OVERRIDE FUNCTION NAME IN CASE IT + * IS NEEDED */ + strcat(override, object[noun[1]]->label); + strcat(override, "_override_"); + strcat(override, object[noun[0]]->label); + + /* CREATE THE FUNCTION NAME '+func' */ + strcpy(base_function, "+"); + strcat(base_function, base_name + 1); + + /* CALL THE LIBRARY'S DEFAULT BEHAVIOR */ + if (execute(base_function) == FALSE) + unkfunrun(base_function); + } + } + + /* EXECUTE THE LOCAL AFTER FUNCTION + * AND RETURN IF IT RETURNS TRUE */ + if (execute(local_after_function) != FALSE) + return; + + /* EXECUTE THE VERB-SPECIFIC AFTER + * FUNCTION AND RETURN IF IT RETURNS TRUE */ + if (execute(after_function) != FALSE) + return; + + /* EXECUTE THE GLOBAL *DEFAULT* AFTER FUNCTION + * AND RETURN IF IT RETURNS TRUE */ + if (execute("+after") != FALSE) + return; + + + //sprintf(temp_buffer, "TIME = %d", TIME->value); + //log_error(temp_buffer, PLUS_STDERR); + + if (TIME->value) { + //printf("--- %s\n", base_function); + eachturn(); + } + + return; +} + +struct word_type *object_match(struct word_type *iterator, int noun_number) { + /* THIS FUNCTION LOOPS THROUGH ALL THE POSIBILITIES IN THE CURRENT LEVEL + * OF THE GRAMMAR TREE TO SEE IF THERE ARE ANY OBJECT PLACE HOLDERS */ + + /* STORE WHETHER BY THE END AN OBJECT PLACEHOLDER WAS ENCOUNTERED AND + * DISPLAY A MESSAGE IF NO OBJECTS MATCHED */ + int object_was_option = FALSE; + + do { + /* THIS LOOP MEANS THAT CERTAIN ERRORS SUCH AS TAKING FROM A + * CLOSED CONTAINER CAN OCCUR MORE THAN ONCE */ + if ((iterator->word[0] == '*')) { + object_was_option = TRUE; + if (build_object_list(iterator, noun_number)) { + /* RETURN THE POINT IN THE GRAMMAR TREE THAT MATCHED TO + * CONTINUE ON FROM */ + //printf("--- returned TRUE from build_object_list\n"); + return (iterator); + } + } + + if (custom_error == TRUE) { + /* AN ERROR OCCURED IN THE FIRST OBJECT PLACEHOLDER, DON'T + * TRY ANY OTHERS */ + return (NULL); + } + } while ((iterator = iterator->next_sibling) != NULL); + + /* THERE WERE NO OBJECT PLACE HOLDERS OR, IF THERE WERE, NO + * MATCHING OBJECTS COULD BE RESOLVED */ + //printf("--- returning null from object_match\n"); + + if (object_was_option) { + /* NO OBJECT MATCHED, BUT AN OBJECT WAS VALID AT THIS POINT IN THE + * PLAYER'S COMMAND */ + diagnose(); + custom_error = TRUE; + } + + return (NULL); +} + +struct word_type *exact_match(struct word_type *pointer) { + /* THIS FUNCTION LOOPS THROUGH ALL THE POSIBILITIES IN THE CURRENT LEVEL + * OF THE GRAMMAR TREE TO SEE IF THERE ARE ANY EXACT MATCHES WITH THE + * CURRENT WORD IN THE PLAYER'S COMMAND. + * AN EXACT MATCH IS ANYTHING THAT ISN'T AN OBJECT PLACE HOLDER. */ + struct word_type *iterator = pointer; + + do { + if (iterator->word[0] == '*') { + /* THIS IS AN OBJECT PLACE HOLDER, THEREFORE IGNORE AND + * KEEP LOOKING FOR EXACT MATCHES */ + } else if (!strcmp(iterator->word, "$string")) { + add_cstring("$string", word[wp]); + last_exact = wp; + wp++; + return (iterator); + } else if (!strcmp(iterator->word, "$integer") && + validate(word[wp])) { + add_cinteger("$integer", atoi(word[wp])); + last_exact = wp; + wp++; + return (iterator); + } else if (!strcmp(word[wp], iterator->word)) { + last_exact = wp; + wp++; + return (iterator); + } + } while ((iterator = iterator->next_sibling) != NULL); + + /* THERE WERE NO EXACT MATCHES, SO RETURN FALSE */ + return (NULL); +} + +int is_terminator(struct word_type *scope_word) { + struct word_type *terminator = scope_word->first_child; + + if (terminator != NULL) { + /* THERE MAY NO BE ANY MORE POSSIBLE WORDS IN THIS COMMAND + * BUT THERE SHOULD ALWAYS AT LEAST BE A BASE FUNCTION NAME */ + do { + /* LOOP THROUGH ALL WORDS IN THE NEXT LEVEL OF THE + * GRAMMAR TABLE. THESE ARE THE WORDS THAT MARK THE END + * OF THE OBJECT REFERENCE PART OF THE COMMAND */ + if (!strcmp(word[wp], terminator->word) + || (!strcmp(terminator->word, "$integer") + && validate(word[wp]))) { + return (TRUE); + } + } while ((terminator = terminator->next_sibling) != NULL); + } + + return (FALSE); +} + +int build_object_list(struct word_type *scope_word, int noun_number) { + /* THIS FUNCTION BUILDS A LIST OF OBJECTS FROM THE PLAYER'S COMMAND + * AND RETURNS THE NUMBER OF OBJECTS IN THAT LIST */ + + int index, counter; + int resolved_object; + char *except_word; + + //printf("--- entering build object list starting at %s with a scope_word of %s\n", word[wp], scope_word->word); + /* LOOK AHEAD FOR A FROM CLAUSE AND STORE from_object IF SO */ + if (get_from_object(scope_word, noun_number) == FALSE) { + /* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN + * DISPLAYED */ + //printf("--- from had an error\n"); + return (FALSE); + } + + while (word[wp] != NULL) { + /* LOOP THROUGH WORDS IN THE PLAYER'S INPUT ENDING WHEN EITHER + * THERE ARE NO MORE WORDS OR ONE OF THE CHILD NODES OF THE + * CURRENT scope_word NODE IS REACHED INDICATING THERE ARE NO + * MORE OBJECTS TO RESOLVE */ + + if (!strcmp(word[wp], cstring_resolve("BUT_WORD")->value) || + !strcmp(word[wp], cstring_resolve("EXCEPT_WORD")->value)) { + /* START ADDING ALL FUTURE RESOLVED OBJECTS TO A SECOND LIST + * TO REMOVE FROM THE FIRST */ + except_word = word[wp]; + + wp++; + + if (word[wp] != NULL && !strcmp(word[wp], cstring_resolve("FOR_WORD")->value)) { + /* SKIP PAST THE WORD 'FOR' */ + wp++; + } + + /* LOOK FORWARD FOR A FROM CLAUSE */ + if (get_from_object(scope_word, noun_number) == FALSE) { + /* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN + * DISPLAYED */ + return (FALSE); + } + + /* MOVE TO THE SECOND LIST THAT WILL ULTIMATELY BE SUBTRACTED + * FROM THE FIRST LIST */ + if (noun_number < 2) { + /* CREATE A 'them' SET THAT CAN BE REFERRED TO IN THE + * 'except' CLAUSE */ + set_them(noun_number); + /* JUMP TO THE 'EXCEPT' LIST THAT CORRESPONDS TO THIS + * RESOLVED LIST */ + noun_number = noun_number + 2; + } else { + sprintf(error_buffer, cstring_resolve("DOUBLE_EXCEPT")->value, except_word); + write_text(error_buffer); + custom_error = TRUE; + return (FALSE); + } + } else if (after_from != -1 && !strcmp(word[wp], cstring_resolve("FROM_WORD")->value)) { + /* SET THE WORD POINTER TO AFTER THE ALREADY-PROCESSED FROM + * CLAUSE (IF ONE EXISTED) AND CONTINUE */ + wp = after_from; + //printf("--- hit from in processing moving on to word '%s'\n", word[wp]); + + /* IF NO OBJECTS WERE MATCHED BY THE TIME WE HIT FROM THEN EITHER + * PRESUME THE PLAYER MEANT 'ALL FROM' OR PRINT AN ERROR */ + if (list_size[noun_number] == 0) { + //printf("--- adding all due to empty list.\n"); + add_all(scope_word, noun_number); + } + + /* LOOK FOR THE NEXT FROM CLAUSE */ + if (get_from_object(scope_word, noun_number) == FALSE) { + /* THERE WAS AN ERROR, AND A MESSAGE HAS ALREADY BEEN + * DISPLAYED */ + return (FALSE); + } + } else if (!strcmp("then", word[wp])) { + break; + } else if (is_terminator(scope_word)) { + /* THERE ARE NO MORE OBJECTS TO RESOLVE */ + + //printf("--- %s is a build list terminator\n", word[wp]); + break; + } else if (!strcmp(word[wp], "comma") || + !strcmp(word[wp], cstring_resolve("AND_WORD")->value)) { + /* JUST MOVE ONTO THE NEXT WORD AND SEE WHAT COME NEXT */ + wp++; + } else { + /* CALL noun_resolve TO FETCH THE FIRST MATCHING OBJECT */ + /* FALSE INDICATES THAT WE ARE NOT LOOKING FOR A FROM OBJECT */ + resolved_object = noun_resolve(scope_word, FALSE, noun_number); + + if (resolved_object == -1) { + /* THERE WERE MULTIPLE MATCHES DUE TO PLURAL NAME + * BEING USED */ + index = 0; + + while (multiple_resolved[index] != 0) { + /* ADD ALL THE RESOLVED OBJECTS TO THE LIST */ + add_to_list(noun_number, multiple_resolved[index]); + index++; + } + } else if (resolved_object) { + /* ADD IT TO THE LIST */ + add_to_list(noun_number, resolved_object); + } else { + /* NO OBJECTS COULD BE RESOLVED, THIS MIGHT NOT BE A BAD + * THING YET IF THERE ARE OTHER OBJECT PLACEHOLDERS TO + * COME THAT ARE LESS RESTRICTIVE */ + return (FALSE); + } + } + } + + if (noun_number > 1 && list_size[noun_number] != 0) { + /* A SECOND EXCEPTION LIST EXISTS, SUBTRACT IT FROM THE FIRST */ + //printf("--- there are some exceptions.\n"); + + //printf("--- first list: %d, second list: %d\n", max_size[FIRST_LIST], max_size[SECOND_LIST]); + /* LOOP THROUGH ALL THE ITEMS IN THE SECOND LIST */ + for (index = 0; index < max_size[SECOND_LIST]; index++) { + if (object_list[SECOND_LIST][index] != 0) { + /* THIS OBJECT IS A REAL OBJECT SO LOOP THROUGH ALL THE ITEMS + * IN THE FIRST LIST */ + //printf("--- exception object is %s\n", object[object_list[SECOND_LIST][index]]->label); + for (counter = 0; counter < max_size[FIRST_LIST]; counter++) { + /* LOOP THROUGH ALL THE OBJECTS IN THE FIRST LIST + * IF AN OBJECT FROM THE SECOND LIST EXISTS IN THE FIRST + * LIST, REMOVE IT */ + //printf("--- comparing %s = %s\n", object[object_list[FIRST_LIST][counter]]->label, object[object_list[SECOND_LIST][index]]->label); + if (object_list[FIRST_LIST][counter] == + object_list[SECOND_LIST][index]) { + + //printf("--- removing object %s\n", object[object_list[FIRST_LIST][counter]]->label); + object_list[FIRST_LIST][counter] = 0; + list_size[FIRST_LIST]--; + } + } + } + } + } + + if (noun_number > 1) { + /* IF THERE WERE EXCEPTIONS, MOVE BACK TO THE FIRST LIST */ + noun_number = FIRST_LIST; + } + + /* THE RETURN TRUE IF AN OBJECT COULD BE RESOLVED */ + if (list_size[noun_number] != 0) { + /* SET THEM ARRAY */ + set_them(noun_number); + + return (TRUE); + } else { + /* THE LIST IS NOW EMPTY, DISPLAY ' I DON'T SEE WHAT YOU + * ARE REFERRING TO.' ERROR */ + if (!strcmp(scope_word->word, "*held") || + !strcmp(scope_word->word, "**held")) { + write_text(cstring_resolve("NONE_HELD")->value); + } else { + write_text(cstring_resolve("NO_OBJECTS")->value); + } + + custom_error = TRUE; + return (FALSE); + } +} + +void set_them(int noun_number) { + int index, counter; + + if (list_size[noun_number] == 1) { + /* THERE IS ONLY ONE OBJECT LEFT IN THE LIST, FIND IT AND ADD + * TO THEM IF REQUIRED */ + for (index = 0; index < max_size[noun_number]; index++) { + if (object_list[noun_number][index] != 0) { + if (object[object_list[noun_number][index]]->attributes & PLURAL) { + them[0] = object_list[noun_number][index]; + them[1] = 0; + break; + } + } + } + } else { + /* THERE IS MORE THAN ONE OBJECT IN THE LIST SO COPY IT TO THE + * 'THEM' LIST */ + counter = 0; + + for (index = 0; index < max_size[noun_number]; index++) { + if (object_list[noun_number][index] != 0) { + them[counter] = object_list[noun_number][index]; + //printf("--- storing %s in them list\n", object[them[counter]]->label); + counter++; + } + } + + /* NULL TERMINATE THE LIST */ + them[counter] = 0; + } +} + +void add_all(struct word_type *scope_word, int noun_number) { + int index, counter; + + //printf("--- trying to add all\n"); + counter = 0; + + for (index = 1; index <= objects; index++) { + if ((object[index]->MASS < HEAVY) && + !(object[index]->attributes & LOCATION)) { + if (is_direct_child_of_from(index) && + scope(index, scope_word->word, RESTRICT)) { + //printf("--- objects parent is %s\n", object[object[index]->PARENT]->label); + add_to_list(noun_number, index); + } + } + } +} + +int is_child_of_from(int child) { + /* THIS FUNCTION DETERMINES IF THE PASSED OBJECT IS A CHILD OF ANY OF + * THE RESOLVED 'FROM' OBJECTS, OR ANY OBJECT IN A FROM OBJECT */ + int index = 0; + + if (from_objects[0] == 0) { + /* THERE HAS BEEN NO FROM CLAUSE */ + return (TRUE); + } + + while (from_objects[index] != 0) { + //printf("--- in is_child from object is %s, child is %s\n", object[from_objects[index]]->label, object[child]->label); + /* THIS OLD WAY OF DOING THINGS ALLOWS SPECIFIC 'take thing from box' + * WHEN thing IS INSIDE SOMETHING ELSE INSIDE box. THAT IS KINDA COOL + * BUT NOT PARTICULARLY NECESSARY. BY ONLY CHECKING IMMEDIATE CHILDREN + * THE NON-RESTRICTIVE VERSION OF parent_of CAN BE USED FROM SCOPE + * WHEN SAYING take all from box BECAUSE THIS FROM STOPS OBJECT IN + * OBJECTS FROM BEING TAKING FROM SOMETHING, SO scope DOESN'T HAVE + * TO. THIS BEHAVIOUR WOULD NOT BE NECESSARY WHEN NOT taking all from */ + + //if (parent_of(from_objects[index], child, RESTRICT)) { + if (object[child]->PARENT == from_objects[index]) { + //printf("--- %s is in %s\n", object[child]->label, object[from_objects[index]]->label); + return (TRUE); + } + index++; + } + + return (FALSE); +} + +int is_direct_child_of_from(int child) { + /* THIS FUNCTION DETERMINES IF THE PASSED OBJECT IS A CHILD OF ANY OF + * THE RESOLVED 'FROM' OBJECTS, OR ANY OBJECT IN A FROM OBJECT */ + int index = 0; + + if (from_objects[0] == 0) { + /* THERE HAS BEEN NO FROM CLAUSE */ + return (TRUE); + } + + while (from_objects[index] != 0) { + //printf("--- in is_direct from object is %s\n", object[from_objects[index]]->label); + if (object[child]->PARENT == from_objects[index]) { + //printf("--- object %s is in the from object\n", object[child]->label); + return (TRUE); + } + index++; + } + + return (FALSE); +} + +int get_from_object(struct word_type *scope_word, int noun_number) { + /* THIS FUNCTION LOOKS AHEAD TO FIND IF THE CURRENT OBJECT REFERENCE + * IS QUALIFIED BY A 'FROM' WORD. IT RETURNS FALSE ON AN ERROR + * CONDITION AND TRUE OTHERWISE, REGARDLESS OF WHETHER A FROM OBJECT + * IS SPECIFIED */ + + int index, counter, from_object; + + /* TAKE A COPY OF THE CURRENT VALUE OF wp */ + int backup = wp; + + /* SET TERMINATOR TO THE FIRST OF THE TERMINATING WORDS */ + struct word_type *terminator = scope_word->first_child; + + /* SEE IF 'FROM' IS ONE OF THE TERMINATORS OF THIS CURRENT OBJECT + * PLACEHOLDER. IF SO, DON'T LOOK FOR A FROM OBJECT */ + if (terminator != NULL) { + //printf("--- checking if terminator word (%s) is from\n", terminator->word); + if (!strcmp(cstring_resolve("FROM_WORD")->value, terminator->word)) { + //printf("--- from is a terminator, don't get a from object\n"); + return (TRUE); + } + while ((terminator = terminator->next_sibling) != NULL); + } + + /* LOOP FROM THE CURRENT WORD TO THE NEXT TERMINATOR AND LOOK FOR THE + * WORD 'FROM' AND STORE THE FOLLOWING OBJECT */ + while (word[wp] != NULL) { + //printf("--- from loop checking %s\n", word[wp]); + if (!strcmp(word[wp], cstring_resolve("FROM_WORD")->value)) { + from_word = word[wp]; + wp++; + + /* scope_word FOR THE CURRENT OBJECT IS PASSED ONLY SO + * noun_resolve CAN FIND OUT THE APPROPRIATE TERMINATORS + * THE ACCEPTABLE SCOPE FOR THE FROM OBJECT SHOULD BE + * AT LEAST *present */ + /* TRUE INDICATES THAT WE ARE LOOKING FOR A FROM OBJECT */ + from_object = noun_resolve(scope_word, TRUE, noun_number); + + /* STORE THE wp FROM AFTER THE RESOLVED FROM OBJECT SO + * WE CAN JUMP FORWARD TO HERE AGAIN WHEN WE HIT THIS + * FROM CLAUSE IN PROCESSING (THIS FUNCTION IS A LOOK-AHEAD */ + after_from = wp; + + //printf("--- looked forward and found a from object of %s\n", object[from_object]->label); + + if (from_object == -1) { + /* THERE WERE MULTIPLE MATCHES DUE TO PLURAL NAME + * BEING USED */ + index = 0; + counter = 0; + /* LOOK THROUGH ALL THE OBJECTS RESOLVED AND CHECK THAT + * THEY ARE ALL VALID FROM OBJECTS */ + while (multiple_resolved[index] != 0) { + if (verify_from_object(multiple_resolved[index]) == FALSE) { + /* AS SOON AS ONE IS BAD, ABORT THE FROM CLAUSE */ + return (FALSE); + } else { + from_objects[counter] = multiple_resolved[index]; + counter++; + } + index++; + } + + /* NULL TERMINATE THE LIST */ + from_objects[counter] = 0; + + /* OBJECTS HAVE BEEN SET AND ARE VALID, RESTORE THE WORD + * POINTER */ + wp = backup; + return (TRUE); + } else if (from_object) { + if (verify_from_object(from_object) == FALSE) { + return (FALSE); + } else { + /* ADD THIS OBJECT TO THE NULL TERMINATED LIST */ + from_objects[0] = from_object; + from_objects[1] = 0; + + /* OBJECT HAS BEEN SET AND IS VALID, RESTORE THE WORD + * POINTER */ + wp = backup; + return (TRUE); + } + } else { + /* FROM UNKNOWN OBJECT, DISPLAY ERROR */ + diagnose(); + custom_error = TRUE; + return (FALSE); + } + //printf("--- finished processing from clause, next word is %s\n", word[wp]); + } else if (!strcmp(cstring_resolve("EXCEPT_WORD")->value, word[wp]) || + !strcmp(cstring_resolve("BUT_WORD")->value, word[wp])) { + /* THIS IS THE LIMIT OF THE EFFECT ANY FROM OBJECT ANYWAY, + * SO TREAT IT LIKE A TERMINATOR */ + //printf("--- %s is a get_from_object except word\n", word[wp]); + wp = backup; + return (TRUE); + } else if (is_terminator(scope_word) || !strcmp("then", word[wp])) { + /* THERE ARE NO MORE OBJECTS TO RESOLVE */ + + //printf("--- %s is a get_from_object terminator\n", word[wp]); + wp = backup; + return (TRUE); + } + wp++; + } + + /* HIT THE END OF THE PLAYER'S COMMAND BEFORE A 'FROM' WORD */ + //printf("--- no from clause specified in this block\n"); + wp = backup; + return (TRUE); +} + +int verify_from_object(int from_object) { + //printf("--- from object is %s\n", object[from_object]->label); + //if (!(object[from_object]->attributes & CONTAINER) && + // !(object[from_object]->attributes & SURFACE) && + // !(object[from_object]->attributes & ANIMATE)) { + // sprintf (error_buffer, FROM_NON_CONTAINER, from_word); + // write_text (error_buffer); + // custom_error = TRUE; + // return (FALSE); + //} else if (object[from_object]->attributes & CLOSED) { + if (object[from_object]->attributes & CONTAINER && object[from_object]->attributes & CLOSED) { + //printf("--- container is concealing\n"); + if (object[from_object]->attributes & FEMALE) { + sprintf(error_buffer, cstring_resolve("CONTAINER_CLOSED_FEM")->value, sentence_output(from_object, TRUE)); + } else { + sprintf(error_buffer, cstring_resolve("CONTAINER_CLOSED")->value, sentence_output(from_object, TRUE)); + } + write_text(error_buffer); + custom_error = TRUE; + return (FALSE); + /* IF THE PERSON IS CONCEALING, THEN THE OBJECT CAN'T BE REFERRED TO + * IF THE PERSON IS POSSESSIVE LET THE LIBRARY HANDLE THE RESPONSE + } else if (object[from_object]->attributes & POSSESSIVE) { + //printf("--- container is closed\n"); + sprintf (error_buffer, PERSON_POSSESSIVE, sentence_output(from_object, TRUE)); + write_text(error_buffer); + custom_error = TRUE; + return (FALSE); + } else if (object[from_object]->attributes & CONCEALING) { + //printf("--- container is closed\n"); + sprintf (error_buffer, PERSON_CONCEALING, sentence_output(from_object, TRUE)); + write_text(error_buffer); + custom_error = TRUE; + return (FALSE);*/ + } + + //printf("--- set from object just fine\n"); + return (TRUE); +} + +void add_to_list(int noun_number, int resolved_object) { + /* ADD THIS OBJECT TO THE OBJECT LIST DEPENDING */ + /* AND SET IT, THEM, HER AND HIM */ + if (!(object[resolved_object]->attributes & ANIMATE)) + it = resolved_object; + if (object[resolved_object]->attributes & ANIMATE + && object[resolved_object]->attributes & FEMALE) + her = resolved_object; + if (object[resolved_object]->attributes & ANIMATE + && !(object[resolved_object]->attributes & FEMALE)) + him = resolved_object; + + //printf("--- adding_object %s to list %d at index %d\n", object[resolved_object]->label, noun_number, max_size[noun_number]); + object_list[noun_number][max_size[noun_number]] = resolved_object; + list_size[noun_number]++; + max_size[noun_number]++; +} + +int noun_resolve(struct word_type *scope_word, int finding_from, int noun_number) { + /* THIS FUNCTION STARTS LOOKING AT THE PLAYER'S COMMAND FROM wp ONWARDS + * AND LOOKS FOR OBJECTS IN THE SCOPE SPECIFIED BY THE GRAMMAR ELEMENT + * POINTED TO BY THE PASSED pointer */ + + /* THIS IS SET TO TRUE WHEN THE CURRENT WORD MATCHES THE CURRENT OBJECT */ + int object_matched; + + /* THIS IS SET TO > 0 WHEN THE PLURAL FORM OF AN OBJECT IS USED */ + int return_limit = 0; + + int index; + int counter; + int first_word = TRUE; + + struct word_type *terminator = scope_word->first_child; + struct name_type *current_name; + + matches = 0; + highest_confidence = 0; + prime_suspect = 0; + done = FALSE; + backup_pointer = wp; + everything = FALSE; + + if (word[wp] == NULL) { + /* NOTHING TO RESOLVE... */ + return (FALSE); + } + + /* SET THE CONFIDENCE FOR EACH OBJECT TO 1 TO INDICATE THAT THEY ARE + * ALL EQUALLY POSSIBLE AT THE MOMENT. SET TO ZERO TO DISCOUNT */ + for (index = 1; index <= objects; index++) + confidence[index] = 1; + + /* CLEAR THE OBJECT NAME BEFORE BUILDING IT WAS WE GO ALONG */ + object_name[0] = 0; + + /* CHECK FOR A QUANTITY QUALIFIER */ + if (validate(word[wp])) { + /* FIRST WORD IS AN INTEGER AND SECOND WORD IS 'OF' SO + * TREAT THIS AS A LIMIT QUALIFIER BEFORE STARTING TO + * PROCESS THE REST OF THE WORDS */ + if (word[wp + 1] != NULL && !strcmp(word[wp + 1], cstring_resolve("OF_WORD")->value)) { + return_limit = atoi(word[wp]); + + /* MAKE SURE THE RETURN LIMIT IS SOMETHING SENSIBLE */ + if (return_limit < 1) { + return_limit = 1; + } + + object_expected = TRUE; + strcpy(object_name, word[wp]); + strcat(object_name, " "); + strcat(object_name, cstring_resolve("OF_WORD")->value); + + /* MOVE THE WORD POINTER TO AFTER THE 'OF' */ + wp = wp + 2; + } + /* IF AN INTEGER IS NOT FOLLOWED BY 'OF', PRESUME IT IS AN OBJECT + * NAME */ + } + + // CLEAR THE ERROR BUFFER AND USE IT TO STORE ALL THE WORDS THE PLAYER + // HAS USED TO REFER TO THE OBJECT + error_buffer[0] = 0; + + while (word[wp] != NULL) { + // ADD THE WORDS USED TO error_buffer FOR POSSIBLE USE + // IN A DISABMIGUATE EMESSAGE + if (first_word == FALSE) { + strcat(error_buffer, " "); + } + strcat(error_buffer, word[wp]); + first_word = FALSE; + + /* LOOP THROUGH WORDS IN THE PLAYER'S INPUT */ + + /* RESET TERMINATOR TO THE FIRST OF THE TERMINATING WORDS */ + terminator = scope_word->first_child; + + if (terminator != NULL) { + /* THERE MAY NO BE ANY MORE POSSIBLE WORDS IN THIS COMMAND + * BUT THERE SHOULD ALWAYS AT LEAST BE A BASE FUNCTION NAME */ + do { + /* LOOP THROUGH ALL WORDS IN THE NEXT LEVEL OF THE + * GRAMMAR TABLE. THESE ARE THE WORDS THAT MARK THE END + * OF THE OBJECT REFERENCE PART OF THE COMMAND */ + //printf("--- checking terminator word %s\n", terminator->word); + if (!strcmp(word[wp], terminator->word) + || (!strcmp(word[wp], cstring_resolve("FROM_WORD")->value)) + || (!strcmp(word[wp], cstring_resolve("AND_WORD")->value)) + || (!strcmp(word[wp], "comma")) + || (!strcmp(word[wp], cstring_resolve("BUT_WORD")->value)) + || (!strcmp(word[wp], cstring_resolve("THEN_WORD")->value)) + || (!strcmp(word[wp], cstring_resolve("EXCEPT_WORD")->value)) + || (!strcmp(terminator->word, "$integer") + && validate(word[wp]))) { + if (!matches) { + /* A TERMINATOR HAS BEEN FOUND BEFORE A + * SINGLE MATCHING OBJECT NAME. */ + return (FALSE); + } else { + /* A TERMINATOR HAS BEEN FOUND, BUT NOT + * BEFORE MATCHING OBJECT NAMES. JUMP FORWARD TO + * RESOLVING INTO OBJECT NUMBER(S) */ + done = TRUE; + break; + } + } + } while ((terminator = terminator->next_sibling) != NULL); + } + + if (done == TRUE) { + /* A TERMINATING WORD HAS BEEN REACHED, SO DON'T TEST THIS + * WORD AGAINST THE OBJECT NAMES, JUST MOVE ON TO CHOSING + * AN OBJECT. */ + //printf("--- %s is a terminator\n", word[wp]); + break; + } + + //puts("--- passed checking for a terminator"); + + /* ADD THE CURRENT WORD TO THE NAME OF THE OBJECT THE PLAYER + * IS TRYING TO REFER TO FOR USE IN AN ERROR MESSAGE IF + * LATER REQUIRED */ + if (object_name[0] != 0) + strcat(object_name, " "); + + strcat(object_name, word[wp]); + + if (!strcmp("everything", word[wp])) { + /* ALL THIS NEEDS TO SIGNIFY IS THAT IT IS OKAY TO RETURN MULTIPLE + * RESULTS. EVERYTHING ELSE SHOULD TAKE CARE OF ITSELF GIVEN ALL + * OBJECTS START OF WITH A CONFIDENCE OF 1, AND ALL OBJECTS WITH + * A CONFIDENCE > 0 ARE RETURNED WHEN return_limit > 1 */ + if (return_limit == FALSE) { + return_limit = MAX_OBJECTS; + } + + /* THIS IS USED TO ALTER THE BEHAVIOUR OF SCOPE SELECTION LATER */ + everything = TRUE; + + matches = 0; + + //printf("--- entering for loop\n"); + for (index = 1; index <= objects; index++) { + if (confidence[index] != FALSE) { + matches++; + } + } + //printf("--- exiting for loop\n"); + + if (word[wp + 1] != NULL && !strcmp(cstring_resolve("OF_WORD")->value, word[wp + 1])) { + /* MOVE PAST THE 'OF' IF IT IS NEXT */ + wp++; + } + //printf("--- finished setting matches to %d for all\n", matches); + } else { + /* THE CURRENT WORD IS NOT ONE OF THE NEXT POSSIBLE WORDS IN THE + * PLAYER'S COMMAND OR ANY OF THE SPECIAL MEANING WORDS, THEREFORE + * TEST IT AGAINST ALL OF THE NAMES OF ALL OF THE OBJECTS. */ + for (index = 1; index <= objects; index++) { + if (!confidence[index]) { + /* SKIP OBJECTS THAT HAVE ALREADY + * BEEN EXCLUDED BY A PREVIOUS WORD */ + continue; + } + + /* NEXT WORD IN PLAYERS INPUT IS YET TO + * BE TESTED AGAINST ALL THIS OBJECT'S NAMES */ + object_matched = FALSE; + + if (!strcmp(cstring_resolve("IT_WORD")->value, word[wp]) || + !strcmp(cstring_resolve("ITSELF_WORD")->value, word[wp])) { + if (it == FALSE) { + no_it(); + return (FALSE); + } else { + if (index == it) { + object_matched = TRUE; + } + } + } else if (!strcmp(cstring_resolve("HER_WORD")->value, word[wp]) || + !strcmp(cstring_resolve("HERSELF_WORD")->value, word[wp])) { + if (her == FALSE) { + no_it(); + return (FALSE); + } else { + if (index == her) { + object_matched = TRUE; + } + } + } else if (!strcmp(cstring_resolve("HIM_WORD")->value, word[wp]) || + !strcmp(cstring_resolve("HIMSELF_WORD")->value, word[wp])) { + if (him == FALSE) { + no_it(); + return (FALSE); + } else { + if (index == him) { + object_matched = TRUE; + } + } + } else if (!strcmp(cstring_resolve("THEM_WORD")->value, word[wp]) || + !strcmp(cstring_resolve("THEMSELVES_WORD")->value, word[wp]) || + !strcmp(cstring_resolve("ONES_WORD")->value, word[wp])) { + /* THIS NEED ONLY BE THE SIZE OF 'THEM', BUT NO HARM + * IN MAKING IT THE FULL SIZE */ + if (return_limit == FALSE) { + return_limit = MAX_OBJECTS; + } + + /* LOOP THROUGH ALL THE OBJECT IN THE 'THEM' ARRAY AND + * SEE IF THIS OBJECT IS PRESENT */ + counter = 0; + + while (them[counter] != 0) { + if (them[counter] == index) { + //printf("--- found previous them object %s\n", object[index]->label); + object_matched = TRUE; + break; + } + counter++; + } + /*} else if (!strcmp("1", word[wp])) { + return_limit = 1; + + // LOOP THROUGH ALL THE OBJECT IN THE 'THEM' ARRAY AND + // SEE IF THIS OBJECT IS PRESENT + counter = 0; + + while (them[counter] != 0) { + if (them[counter] == index) { + //printf("--- found previous them object %s\n", object[index]->label); + object_matched = TRUE; + break; + } + counter++; + }*/ + } else { + current_name = object[index]->first_name; + + while (current_name != NULL) { + /* LOOP THROUGH ALL THE CURRENT OBJECTS NAMES */ + if (!strcmp(word[wp], current_name->name)) { + /* CURRENT WORD MATCHES THE CURRENT NAME + *OF THE CURRENT OBJECT */ + //printf("--- %s is a name match of object %d\n", word[wp], index); + object_matched = TRUE; + + /* MOVE ON TO NEXT OBJECT, THIS OBJECT SHOULD + * NOT HAVE THE SAME NAME TWICE */ + break; + } + current_name = current_name->next_name; + } + + /* NOW LOOP THROUGH ALL THE OJBECTS PLURAL NAMES */ + current_name = object[index]->first_plural; + + while (current_name != NULL) { + /* LOOP THROUGH ALL THE CURRENT OBJECTS NAMES */ + if (!strcmp(word[wp], current_name->name)) { + /* CURRENT WORD MATCHES THE CURRENT NAME + *OF THE CURRENT OBJECT */ + //printf("--- %s is a plural name match of object %d\n", word[wp], index); + object_matched = TRUE; + + /* IT IS NOW OKAY FOR THIS FUNCTION TO RETURN MORE + * THAT ONE MATCHING OBJECT AS THE PLURAL FORM + * HAS BEEN USED. THIS IS INDICATED BY RETURNING + * -1 TO INDICATED THAT THE OBJECT LIST IS STORED + * IN A GLOBALLY ACCESSIBLE ARRAY */ + if (return_limit == FALSE) { + return_limit = MAX_OBJECTS; + } + + /* MOVE ON TO NEXT OBJECT, THIS OBJECT SHOULD + * NOT HAVE THE SAME NAME TWICE */ + break; + } + current_name = current_name->next_name; + } + } + + if (object_matched) { + /* THE CURRENT WORD MATCHES ONE OF THE NAMES OF THE + * CURRENT OBJECT. */ + if (confidence[index] == 1) { + /* OBJECT HAD NOT YET BEEN COUNTED AS MATCH SO INCREMENT + * THE NUMBER OF OBJECTS THAT MATCH SO FAR. */ + matches++; + } + if (confidence[index] != FALSE) { + /* IF OBJECT HAS NOT BEEN EXCLUDED, INCREMENT THE + * NUMBER OF NAMES IT HAS MATCHING. */ + confidence[index]++; + } + } else { + /* THE CURRENT WORD IS NOT ONE OF THE NAMES OF THE + * CURRENT OBJECT. + * IF THE OBJECT HAD PREVIOUSLY BEEN A CANDIDATE, DECREMENT + * THE NUMBER OF MATCHING OBJECTS. */ + if (confidence[index] > 1) + matches--; + + /* AS THE CURRENT WORD DID NOT MATCH ANY OF THE NAMES OF + * THE THIS OBJECT, EXCLUDE IT FROM CONTENTION. */ + confidence[index] = FALSE; + } + } /* MOVE ON TO NEXT OBJECT FOR IN LOOP */ + } + + if (!matches) { + /* IF THERE ARE NO REMAINING MATCHES DON'T MOVE ON TO THE NEXT + * WORD IN THE PLAYER'S INPUT. */ + //printf("--- %s isnt a name match for any object\n", word[wp]); + + /* THIS WORD IS A LIKELY BE INCORRECT AS IT DIDN'T MATCH + * ANY OBJECTS */ + if (oops_word == -1 && word[wp] != NULL) { + oops_word = wp; + } + + break; + } + + /* MOVE ON TO THE NEXT WORD IN THE PLAYER'S INPUT. */ + //printf("--- moving on to next word\n"); + wp++; + } + + //printf("--- finished processing words\n"); + + /*************************************************** + * FINISHED LOOKING THROUGH THE PLAYER'S INPUT NOW * + * MOVE THE POINTER BACK FOR PARSER TO PROCESS * + * THIS SHOULDN'T BE DONE FOR A WORD LIKE 'AND' * + ***************************************************/ + + if (return_limit == FALSE) { + /* THE RETURN LIMIT WAS NEVER SET, SO TREAT THIS AS AN IMPLICIT 1 */ + return_limit = 1; + } + + if (matches == 0) { + /* THE PLAYER'S INPUT COULD NOT BE RESOLVED TO A SINGLE OBJECT + * BUT NO TERMINATING WORD WAS USED BEFORE A NON-TERMINATING + * WORD IN A PLACE WHERE AN OBJECT COULD BE SPECIFIED --- + * IN OTHER WORDS, PRESUME THE PLAYER WAS TRYING TO REFER TO + * AN OBJECT BUT SOMEHOW GOT IT WRONG */ + + //printf("--- matches = 0\n"); + /* THIS VARIABLE IS USED TO CHANGE THE BEHAVIOR OF diagnose() + * SHOULD THIS COMMAND TURN OUT TO BE INVALID */ + object_expected = TRUE; + + wp = backup_pointer; /* BACK UP THE CURRENT WORD POINTER. + * SO THE PARSER CAN INVESTIAGE THE + * POSIBILITY THAT THIS ISN'T A + * REFERENCE TO AN OBJECT AT ALL */ + + return (FALSE); /* RETURN TO PARSER WITH NO MATCHING + * OBJECT HAVING BEEN FOUND. */ + } + + //printf("--- starting with %d matches\n", matches); + + /* LOOP THROUGH ALL OBJECTS AND REMOVE FROM ANY OBJECT THAT IS NOT IN THE + * CURRENT LOCATION, PROVIDED THE VERB DOESN'T ALLOW THE OBJECT TO + * BE *ANYWHERE. */ + for (index = 1; index <= objects; index++) { + if (confidence[index] != FALSE && strcmp(scope_word->word, "*anywhere") && strcmp(scope_word->word, "**anywhere") && strcmp(scope_word->word, "*location")) { + if (scope(index, "*present", UNRESTRICT) == FALSE) { + matches--; + confidence[index] = FALSE; + //printf("--- removing %s for not being present\n", object[index]->label); + } + } + + /* IF THE OBJECT IS IMPLICITLY INCLUDED DUE TO A MULTIPLE RETURN + * LIMIT, REMOVE IT IF IT IS PART OF THE SCENERY */ + if (confidence[index] == 1 && return_limit > 1) { + if ((object[index]->MASS >= HEAVY) || + (object[index]->attributes & LOCATION)) { + matches--; + confidence[index] = 0; + //printf("--- removing %s for being scenery\n", object[index]->label); + } + } + + /* FOR ALL CONTENDERS, CALCULATE THE CONFIDENCE AS A PERCENTAGE, + * SO LONG AS WE ARE STILL LOOKING FOR A SINGLE OBJECT */ + if (confidence[index] != FALSE && return_limit == 1) { + current_name = object[index]->first_name; + counter = 0; + while (current_name != NULL) { + counter++; + current_name = current_name->next_name; + } + confidence[index] = ((confidence[index] - 1) * 100) / counter; + } + } + + // CALCULATE THE HIGHEST CONFIDENCE OF ALL THE POSSIBLE OBJECTS + highest_confidence = 0; + + for (index = 1; index <= objects; index++) { + if (confidence[index] > highest_confidence) { + highest_confidence = confidence[index]; + } + } + + /* REMOVE ANY OBJECTS THAT ARE NOT IN THEIR VERB'S SPECIFIED SCOPE */ + for (index = 1; index <= objects; index++) { + if (confidence[index] != FALSE) { + if (scope(index, "*present", UNRESTRICT) != FALSE) { + // TAKE SPECIAL NOT OF AN OBJECT THAT IS PRESENT + // AND AT LEAST EQUAL FOR THE HIGHEST CONFIDENCE + // IN CASE NO OBJECT ARE LEFT AFTER SPECIFIED SCOPE + // IS USED TO FILTER THE LIST */ + if (confidence[index] == highest_confidence) { + prime_suspect = index; + } + //printf("--- storing %s as prime_suspect\n", object[index]->label); + } + + if (finding_from) { + if (strcmp(scope_word->word, "*anywhere") && strcmp(scope_word->word, "**anywhere")) { + if (scope(index, "*present") == FALSE) { + matches--; + confidence[index] = FALSE; + continue; + } + } + } else if (scope(index, scope_word->word, (everything && !from_objects[0])) == FALSE) { + /* IF everything IS TRUE, scope IS RESTRICTED */ + //printf("--- removing %s due to regular scope\n", object[index]->label); + matches--; + confidence[index] = FALSE; + continue; + } + + /* CHECK IF THIS OBJECT IS IN ACCORDANCE WITH ANY FROM CLAUSE + * THAT MAY OR MAY NOT HAVE USED */ + if (is_child_of_from(index) == FALSE) { + matches--; + confidence[index] = FALSE; + //printf("--- removing %s due to from clause\n", object[index]->label); + } + } + } + + //printf("--- there are %d matches left\n", matches); + + /* THIS LOOP REMOVES ANY OBJECT THAT ARE NOT EQUAL TO THE HIGHEST + * CONFIDENCE UNLESS A PLURAL NAME WAS USED. IN THAT CASE, ONLY + * EXCLUDE OBJECTS THAT DO NOT HAVE ONE OF THE NAMES SUPPLIED */ + if (matches > 1 && return_limit == 1) { + // CALCULATE THE HIGHEST CONFIDENCE NOW THAT OBJECTS + // NOT IN SCOPE HAVE BEEN REMOVED + highest_confidence = 0; + + for (index = 1; index <= objects; index++) { + if (confidence[index] > highest_confidence) { + highest_confidence = confidence[index]; + } + } + + //printf("--- removing lower confidence objects\n"); + for (index = 1; index <= objects; index++) { + /* REMOVE ANY OBJECTS THAT ARE NOT EQUAL + TO THE HIGHEST CONFIDENCE. */ + if (confidence[index] != FALSE) { + if (confidence[index] < highest_confidence) { + //printf("--- removing %s due to confidence of %d being under %d\n", object[index]->label, confidence[index], highest_confidence); + confidence[index] = FALSE; + matches--; + } + } + } + } + + if (matches == 0) { + /* IF THIS LEAVES NO OBJECT, RETURN THE OBJECT THAT WAS PRESENT + * AND SET THE APPROPRIATE POINTERS. */ + if (prime_suspect != FALSE) { + return (prime_suspect); + } else { + object_expected = TRUE; + wp = backup_pointer; + return (FALSE); + } + } + + if (matches == 1) { + /* IF ONLY ONE POSSIBILITY REMAINS, RETURN THIS OBJECT AND SET THE + * APPROPRIATE POINTERS. */ + //printf("--- only one object left\n"); + for (index = 1; index <= objects; index++) { + if (confidence[index] != FALSE) { + return (index); + } + } + } + + if (return_limit > 1) { + /* THE PLURAL NAME OF AN OBJECT WAS USED OR A QUANTITY QUALIFIER */ + + counter = 0; + + //printf("--- return_limit == TRUE\n"); + + for (index = 1; index <= objects; index++) { + if (confidence[index] != FALSE) { + /* ADD EACH OBJECT TO multiple_resolved UNTIL + * THE return_limit IS REACHED */ + multiple_resolved[counter] = index; + return_limit--; + //printf("--- adding %s to multiple_resolved\n", object[index]->label); + counter++; + + if (return_limit == 0) { + break; + } + } + } + + /* NULL TERMINATE THE LIST */ + multiple_resolved[counter] = 0; + + //printf("--- returning multiple objects\n"); + /* RETURN -1 TO INDICATED THERE ARE MULTIPLE OBJECTS */ + return (-1); + } + + /* AN AMBIGUOUS REFERENCE WAS MADE. ATTEMPT TO CALL ALL THE disambiguate + * FUNCTIONS TO SEE IF ANY OF THE OBJECT WANTS TO TAKE PREFERENCE IN + * THIS CIRCUMSTANCE */ + int situation = noun_number; + + if (finding_from) { + situation += 4; + } + + /* + for (index = 1; index <= objects; index++) { + if (confidence[index] != FALSE) { + strcpy(function_name, "disambiguate"); + strcat(function_name, "_"); + strcat(function_name, object[index]->label); + strcat(function_name, "<"); + sprintf(temp_buffer, "%d", situation); + strcat(function_name, temp_buffer); + + // CALL THE DISAMBIGUATION FUNCTION ATTACHED TO EACH OF THE + // POSSIBLE OBJECTS + int return_code = execute (function_name); + + if (return_code == 1) { + // THIS OBJECT CAN CLAIMED OWNERSHIP OF THIS COMMAND + return (index); + } else if (return_code == -1) { + // THIS OBJECT HAS REJECTED OWNERSHIP OF THIS COMMAND + confidence[index] = FALSE; + matches--; + } + } + } + */ + + // CHECK IF ALL THE OBJECTS WERE REJECTED + if (matches == 0) { + return (prime_suspect); + } + + // CHECK IF ONLY ONE OBJECT NOW REMAINS + if (matches == 1) { + /* IF ONLY ONE POSSIBILITY REMAINS, RETURN THIS OBJECT AND SET THE + * APPROPRIATE POINTERS. */ + //printf("--- only one object left\n"); + for (index = 1; index <= objects; index++) { + if (confidence[index] != FALSE) { + return (index); + } + } + } + +#if defined GLK || defined __NDS__ + /* NO OBJECT HAS CLAIMED OWNERSHIP, PROMPT THE PLAYER TO SPECIFY + * WHICH ONE THEY REQUIRE. */ + counter = 1; + write_text(cstring_resolve("BY")->value); + write_text(error_buffer); + write_text(cstring_resolve("REFERRING_TO")->value); + for (index = 1; index <= objects; index++) { + if (confidence[index] != FALSE) { + possible_objects[counter] = index; + sprintf(text_buffer, " [%d] ", counter); + write_text(text_buffer); + sentence_output(index, 0); + write_text(temp_buffer); + matches--; + if (counter < 9) + counter++; + write_text("^"); + } + } + + /* GET A NUMBER: don't insist, low = 1, high = counter */ + selection = get_number(FALSE, 1, counter - 1); + + if (selection == -1) { + write_text(cstring_resolve("INVALID_SELECTION")->value); + custom_error = TRUE; + return (FALSE); + } else { + write_text("^"); + return (possible_objects[selection]); + } +#else + /* IF MORE THAT ONE OBJECT STILL REMAINS, PROMPT THE PLAYER TO SPECIFY + * WHICH ONE THEY REQUIRE. */ + write_text(cstring_resolve("WHEN_YOU_SAY")->value); + write_text(error_buffer); + write_text(cstring_resolve("MUST_SPECIFY")->value); + for (index = 1; index <= objects; index++) { + if (confidence[index] != FALSE) { + sentence_output(index, 0); + write_text(temp_buffer); + matches--; + if (matches == 0) { + write_text("."); + break; + } else if (matches == 1) { + write_text(cstring_resolve("OR_WORD")->value); + } else + write_text(", "); + } + } + write_text("^"); + + custom_error = TRUE; + return (FALSE); +#endif +} + +void diagnose() { + if (custom_error) { + TIME->value = FALSE; + return; + } + if (word[wp] == NULL) + write_text(cstring_resolve("INCOMPLETE_SENTENCE")->value); + else if (object_expected && wp != 0) { + write_text(cstring_resolve("UNKNOWN_OBJECT")->value); + write_text(object_name); + write_text(cstring_resolve("UNKNOWN_OBJECT_END")->value); + } else { + write_text(cstring_resolve("CANT_USE_WORD")->value); + write_text(word[wp]); + write_text(cstring_resolve("IN_CONTEXT")->value); + } + TIME->value = FALSE; +} + +int scope(int index, char *expected, int restricted) { + /* THIS FUNCTION DETERMINES IF THE SPECIFIED OBJECT IS IN THE SPECIFIED + * SCOPE - IT RETURNS TRUE IF SO, FALSE IF NOT. */ + + int temp = 0; + + /* WHEN THE ARGUMENT restricted IS TRUE IT HAS A MORE LIMITED + * SENSE OF WHAT IS ACCEPTABLE */ + + if (!strcmp(expected, "*held") || !strcmp(expected, "**held")) { + if (object[index]->PARENT == HELD) { + return (TRUE); + } else if (object[index]->MASS >= HEAVY) { + /* ALLOW AND OBJECT TO BE CONSIDERED HELD IF IT HAS A + * MASS OF HEAVY OR GREATER AND ITS DIRECT PARENT IS + * BEING HELD. IE, A LABEL ON A JAR CAN BE SHOWN BECAUSE + * THE JAR IS BEING HELD. */ + temp = object[index]->PARENT; + if (temp > 0 && temp < objects) { + if (object[temp]->PARENT == HELD) { + return (TRUE); + } + } + return (FALSE); + } else { + return (FALSE); + } + } else if (!strcmp(expected, "*location")) { + if (object[index]->attributes & LOCATION) { + return (TRUE); + } else { + return (FALSE); + } + } else if (!strcmp(expected, "*here") || !strcmp(expected, "**here")) { + if (object[index]->PARENT == HERE || index == HERE) { + /* THE OBJECT IN QUESTION IS IN THE PLAYER'S CURRENT LOCATION + * OR IS THE PLAYER'S CURRENT LOCATION. */ + return (TRUE); + } else if (object[index]->PARENT == HELD) { + /* IT IS ONLY A PROBLEM FOR THE OBJECT TO BE AN IMMEDIATE CHILD + * OF THE PLAYER. THE PLAYER CAN STILL TAKE AN OBJECT THAT IS IN + * SOMETHING THEY ARE CARRYING */ + return (FALSE); + } else { + /* IS THE OBJECT A CHILD OF THE CURRENT LOCATION SOMEHOW */ + return (parent_of(HERE, index, restricted)); + } + } else if (!strcmp(expected, "*anywhere") || !strcmp(expected, "**anywhere")) { + return (TRUE); + } else if (!strcmp(expected, "*inside") || !strcmp(expected, "**inside")) { + if (object_list[0][0] > 0 && object_list[0][0] < objects) { + return (parent_of(object_list[0][0], index, restricted)); + } else { + // THERE IS NO PREVIOUS OBJECT SO TREAT THIS LIKE A *here + return (parent_of(HERE, index, restricted)); + } + } else if (!strcmp(expected, "*present") || !strcmp(expected, "**present")) { + if (index == HERE) { + return (TRUE); + } else { + if (find_parent(index)) { + return (TRUE); + } else { + return (FALSE); + } + } + } else { + unkscorun(expected); + return (FALSE); + } +} + +int find_parent(int index) { + /* THIS FUNCTION WILL SET THE GLOBAL VARIABLE parent TO + * THE OBJECT THAT IS AT THE TOP OF THE POSSESSION TREE. + * IT WILL RETURN TRUE IF THE OBJECT IS VISIBLE TO THE + * PLAYER */ + //printf("--- find parent of %s\n", object[index]->label); + + if (!(object[index]->attributes & LOCATION) && + object[index]->PARENT != NOWHERE) { + + parent = object[index]->PARENT; + //printf("--- parent is %s\n", object[parent]->label); + + if (index == parent) { + /* THIS OBJECT HAS ITS PARENT SET TO ITSELF */ + sprintf(error_buffer, SELF_REFERENCE, executing_function->name, object[index]->label); + log_error(error_buffer, PLUS_STDOUT); + return (FALSE); + } else if (!(object[parent]->attributes & LOCATION) + && ((object[parent]->attributes & CLOSED && object[parent]->attributes & CONTAINER) + || object[parent]->attributes & CONCEALING)) { + //printf("--- %s is closed, so return FALSE\n", object[parent]->label); + return (FALSE); + } else if (parent == HERE || parent == HELD) { + /* THE OBJECT IS THE PLAYER'S CURRENT LOCATION OR BEING HELD */ + return (TRUE); + } else { + if (object[parent]->attributes & LOCATION) { + //printf("--- %s is a location, so dont recuse\n", object[parent]->label); + return (FALSE); + } else { + //printf("--- %s isnt a location, so recuse\n", object[parent]->label); + return (find_parent(parent)); + } + } + } else { + if (index == HERE) + /* THE OBJECT IS THE PLAYER'S CURRENT LOCATION. */ + return (TRUE); + else + return (FALSE); + } +} + +int parent_of(int parent, int child, int restricted) { + /* THIS FUNCTION WILL CLIMB THE OBJECT TREE STARTING AT 'CHILD' UNTIL + * 'PARENT' IS REACHED (RETURN TRUE), OR THE TOP OF THE TREE OR A CLOSED + * OR CONCEALING OBJECT IS REACHED (RETURN FALSE). */ + + /* restricted ARGUMENT TELLS FUNCTION TO IGNORE OBJECT IF IT IS IN AN + * OBJECT WITH A mass OF heavy OR LESS THAT IS NOT THE SUPPLIED + * PARENT ie. DON'T ACCEPT OBJECTS IN SUB OBJECTS */ + + int index; + + //printf("--- parent is %s, child is %s\n", object[parent]->label, object[child]->label); + if (child == parent) { + return (TRUE); + } else if (!(object[child]->attributes & LOCATION) && + object[child]->PARENT != NOWHERE) { + /* STORE THE CHILDS PARENT OBJECT */ + index = object[child]->PARENT; + //printf("--- %s is the parent of %s\n", object[index]->label, object[child]->label); + + if (index == child) { + /* THIS CHILD HAS IT'S PARENT SET TO ITSELF */ + sprintf(error_buffer, SELF_REFERENCE, executing_function->name, object[index]->label); + log_error(error_buffer, PLUS_STDOUT); + //printf("--- self parent.\n"); + return (FALSE); + } else if (!(object[index]->attributes & LOCATION) + && ((object[index]->attributes & CLOSED && object[index]->attributes & CONTAINER) + || object[index]->attributes & CONCEALING)) { + /* THE CHILDS PARENT IS CLOSED OR CONCEALING - CANT BE SEEN */ + //printf("--- parent %s is closed\n", object[index]->label); + return (FALSE); + } else if (restricted && object[index]->MASS < HEAVY && index != parent) { + //printf("--- scenery object.\n"); + return (FALSE); + } else { + //printf("--- comparing %s with %s\n", object[index]->label, object[parent]->label); + if (index == parent) { + /* YES, IS PARENT OF CHILD */ + return (TRUE); + } else if (object[index]->attributes & LOCATION) { + return (FALSE); + } else { + /* KEEP LOOKING UP THE TREE TILL THE CHILD HAS NO MORE + * PARENTS */ + return (parent_of(parent, index, restricted)); + } + } + } else { + /* THE SPECIFIED OBJECT HAS NO PARENT */ + return (FALSE); + } +} + +} // End of namespace JACL +} // End of namespace Glk |