aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorPaul Gilbert2019-05-11 13:59:44 +1000
committerPaul Gilbert2019-05-11 13:59:44 +1000
commit1bbfcca229a3aa39854d495b0ce497d958d37b2e (patch)
tree6bd70a2f39746e604fa19995d1f8828290871b6c /engines
parentabb7b22b2e4a9bd24058e0221c85680aed71d03f (diff)
downloadscummvm-rg350-1bbfcca229a3aa39854d495b0ce497d958d37b2e.tar.gz
scummvm-rg350-1bbfcca229a3aa39854d495b0ce497d958d37b2e.tar.bz2
scummvm-rg350-1bbfcca229a3aa39854d495b0ce497d958d37b2e.zip
GLK: HUGO: Add heparse
Diffstat (limited to 'engines')
-rw-r--r--engines/glk/hugo/hemisc.cpp10
-rw-r--r--engines/glk/hugo/heparse.cpp2579
-rw-r--r--engines/glk/hugo/hugo.cpp4
-rw-r--r--engines/glk/hugo/hugo.h233
-rw-r--r--engines/glk/hugo/hugo_defines.h2
-rw-r--r--engines/glk/module.mk1
6 files changed, 2787 insertions, 42 deletions
diff --git a/engines/glk/hugo/hemisc.cpp b/engines/glk/hugo/hemisc.cpp
index 9e560570d4..4a2ea2557a 100644
--- a/engines/glk/hugo/hemisc.cpp
+++ b/engines/glk/hugo/hemisc.cpp
@@ -977,13 +977,14 @@ char *Hugo::GetText(long textaddr) {
return g;
}
-const char *Hugo::GetWord(unsigned int w) {
- static const char *b;
+char *Hugo::GetWord(unsigned int w) {
+ char *b;
unsigned short a;
+ static char *EMPTY = "";
a = w;
- if (a==0) return "";
+ if (a==0) return EMPTY;
if (a==PARSE_STRING_VAL) return parseerr;
if (a==SERIAL_STRING_VAL) return serial;
@@ -991,8 +992,7 @@ const char *Hugo::GetWord(unsigned int w) {
/* bounds-checking to avoid some sort of memory arena error */
if ((long)(a+dicttable*16L) > codeend)
{
- b = "";
- return b;
+ return EMPTY;
}
defseg = dicttable;
diff --git a/engines/glk/hugo/heparse.cpp b/engines/glk/hugo/heparse.cpp
new file mode 100644
index 0000000000..2a70fed89d
--- /dev/null
+++ b/engines/glk/hugo/heparse.cpp
@@ -0,0 +1,2579 @@
+/* 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/hugo/hugo.h"
+
+namespace Glk {
+namespace Hugo {
+
+#define STARTS_AS_NUMBER(a) (((a[0]>='0' && a[0]<='9') || a[0]=='-')?1:0)
+
+void Hugo::AddAllObjects(int loc) {
+ int i;
+
+ if (loc==var[player] && domain!=loc)
+ return;
+
+ /* Try to add everything in the specified domain
+ to objlist[]
+ */
+ for (i=Child(loc); i!=0; i=Sibling(i))
+ {
+ if (i==var[xobject]) continue;
+
+ TryObj(i);
+ if (domain==0)
+ {
+ if (Child(i)) AddAllObjects(i);
+ }
+ }
+}
+
+void Hugo::AddObj(int obj) {
+ int i;
+
+ for (i=0; i<objcount; i++)
+ {
+ if (objlist[i]==obj)
+ return;
+ }
+
+ objlist[(int)objcount] = obj;
+ if (++objcount> MAXOBJLIST) objcount = MAXOBJLIST;
+}
+
+void Hugo::AddPossibleObject(int obj, char type, unsigned int w) {
+ int i;
+
+ if (pobjcount==MAXPOBJECTS)
+ return;
+
+ for (i=0; i<pobjcount; i++)
+ {
+ /* If it is already in the list */
+ if (pobjlist[i].obj==obj)
+ {
+ /* Being referred to with a noun outweighs being
+ referred to previously with an adjective
+ */
+ if (type==(char)noun || ObjWordType(obj, w, noun))
+ pobjlist[i].type = (char)noun;
+
+ return;
+ }
+ }
+
+ /* Getting this to point is presuming that we're adding an object
+ referred to with an adjective, but check just to be sure it isn't
+ also a noun for that same object
+ */
+ if (ObjWordType(obj, w, noun)) type = (char)noun;
+
+ pobjlist[pobjcount].obj = obj;
+ pobjlist[pobjcount].type = type;
+
+ pobjcount++;
+#ifdef DEBUG_PARSER
+{
+ char buf[100];
+ sprintf(buf, "AddPossibleObject(%d:\"%s\")", obj, Name(obj));
+ Printout(buf);
+}
+#endif
+}
+
+void Hugo::AdvanceGrammar() {
+ int a;
+
+ defseg = gameseg;
+
+ switch (a = Peek(grammaraddr))
+ {
+ case FORWARD_SLASH_T:
+ case HELD_T:
+ case MULTI_T:
+ case MULTIHELD_T:
+ case ANYTHING_T:
+ case NUMBER_T:
+ case PARENT_T:
+ case NOTHELD_T:
+ case MULTINOTHELD_T:
+ case WORD_T:
+ case OBJECT_T:
+ case XOBJECT_T:
+ case STRING_T:
+ grammaraddr++;
+ break;
+
+ case ASTERISK_T:
+ case ATTR_T:
+ grammaraddr += 2;
+ break;
+
+ case DICTENTRY_T:
+ case ROUTINE_T:
+ case OBJECTNUM_T:
+ grammaraddr += 3;
+ break;
+
+ case OPEN_BRACKET_T:
+ grammaraddr +=5;
+ break;
+ }
+}
+
+int Hugo::AnyObjWord(int wn) {
+ int i;
+
+ if (objword_cache[wn])
+ return objword_cache[wn];
+
+ for (i=0; i<objects; i++)
+ {
+ if (ObjWord(i, wd[wn]))
+ {
+ return (objword_cache[wn] = 1);
+ }
+ }
+
+ return (objword_cache[wn] = -1);
+}
+
+int Hugo::Available(int obj, char non_grammar) {
+ int temp_stack_depth;
+
+ if (findobjectaddr)
+ {
+ passlocal[0] = obj;
+
+ /* if anything or (Routine) grammar */
+ if ((Peek(grammaraddr)==ANYTHING_T
+ || (Peek(grammaraddr)==OPEN_BRACKET_T && Peek(grammaraddr+1)==ROUTINE_T))
+ && non_grammar==0)
+ {
+ passlocal[1] = 0;
+ }
+ else
+ {
+ if (domain > 0)
+ passlocal[1] = domain;
+ else if (speaking && non_grammar==0)
+ passlocal[1] = GrandParent(speaking);
+ /* domain of -1 is an explicit 'parent' */
+/*
+ else if (domain==-1)
+ passlocal[1] = parse_location;
+*/
+ else
+ passlocal[1] = parse_location;
+ }
+
+ ret = 0;
+
+ PassLocals(2);
+ temp_stack_depth = stack_depth;
+
+ SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+#if defined (DEBUGGER)
+ DebugRunRoutine((long)findobjectaddr*address_scale);
+#else
+ RunRoutine((long)findobjectaddr*address_scale);
+#endif
+ retflag = 0;
+ stack_depth = temp_stack_depth;
+ return ret;
+ }
+ else
+ return 1;
+}
+
+void Hugo::CallLibraryParse() {
+ if (parseaddr)
+ {
+#ifdef DEBUG_PARSER
+ Printout("CallLibraryParse()");
+#endif
+ parse_called_twice = false;
+
+ SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+ ret = 0;
+ PassLocals(0);
+#if defined (DEBUGGER)
+ DebugRunRoutine((long)parseaddr*address_scale);
+#else
+ RunRoutine((long)parseaddr*address_scale);
+#endif
+ retflag = 0;
+
+ /* Returning non-zero return calls the
+ engine's Parse routine again.
+ */
+ if (ret)
+ {
+ parse_called_twice = true;
+ Parse();
+ }
+#ifdef DEBUG_PARSER
+ if (ret)
+ Printout("CallLibraryParse() returned true");
+ else
+ Printout("CallLibraryParse() returned false");
+#endif
+ }
+}
+
+int Hugo::DomainObj(int obj) {
+ int yes = false;
+
+ if (obj != var[actor])
+ {
+ switch (domain)
+ {
+ case 0:
+ case -1:
+ {
+ if (Parent(obj)==parse_location)
+ yes = true;
+ else if ((parse_allflag) && GrandParent(obj)==parse_location)
+ yes = true;
+ else
+ {
+ if (Parent(obj)==parse_location && !InList(Parent(obj)))
+ yes = true;
+ }
+
+ if (Peek(grammaraddr)==MULTINOTHELD_T)
+ {
+ if (Parent(obj)==var[actor])
+ yes = false;
+ }
+ break;
+ }
+
+ default:
+ {
+ if (Parent(obj)==domain)
+ yes = true;
+ }
+ }
+ }
+
+ return yes;
+}
+
+unsigned int Hugo::FindWord(char *a) {
+ unsigned int ptr = 0;
+ int i, p, alen;
+
+ if (a[0]=='\0')
+ return 0;
+
+ alen = strlen(a);
+
+ defseg = dicttable;
+
+ for (i=1; i<=dictcount; i++)
+ {
+ if (alen==(p = Peek(ptr+2)) && (unsigned char)(MEM(dicttable*16L+ptr+3)-CHAR_TRANSLATION)==(unsigned char)a[0])
+ {
+ if (!strcmp(GetString(ptr + 2), a))
+ {
+ defseg = gameseg;
+ return ptr;
+ }
+ }
+
+ ptr += (p + 1);
+ }
+
+ /* As a last resort, see if the first 6 characters of the word (if it
+ has at least six characters) match a dictionary word:
+ */
+ if (alen >= 6)
+ {
+ unsigned int possible = 0;
+ int posscount = 0;
+
+ ptr = 0;
+
+ for (i=1; i<=dictcount; i++)
+ {
+ if (alen<=(p = Peek(ptr+2)) && (unsigned char)MEM(dicttable*16L+ptr+3)-CHAR_TRANSLATION==a[0])
+ {
+ if (!strncmp(GetString(ptr + 2), a, alen))
+ {
+ /* As long as the dictionary word
+ doesn't contain a space */
+ if (!strrchr(GetString(ptr+2), ' '))
+ {
+ possible = ptr;
+ posscount++;
+ }
+ }
+ }
+ ptr += (p + 1);
+ }
+
+ if (posscount==1)
+ return possible;
+ }
+
+ defseg = gameseg;
+
+ return UNKNOWN_WORD; /* not found */
+}
+
+int Hugo::InList(int obj) {
+ int i;
+
+ for (i=0; i<objcount; i++)
+ {
+ if (objlist[i]==obj)
+ return true;
+ }
+ return false;
+}
+
+void Hugo::KillWord(int a) {
+ int i;
+
+ if (a>words)
+ return;
+
+ for (i=a; i<words; i++)
+ word[i] = word[i+1];
+ word[words] = "";
+
+ RemoveWord(a);
+ words--;
+}
+
+int Hugo::MatchCommand() {
+ int i, j, flag, a, mw = 0, gotspeaker = 0;
+ int wordnum;
+ int numverbs = 0;
+ bool nextverb = false;
+ unsigned int ptr, verbptr, nextgrammar;
+ unsigned int obj, propaddr;
+
+#ifdef DEBUG_PARSER
+ Printout("Entering MatchCommand()");
+#endif
+
+ odomain = 0;
+
+ /* Reset these for command-matching */
+ if (!speaking)
+ {
+ var[actor] = var[player];
+ parse_location = var[location];
+ }
+ else
+ {
+ var[actor] = speaking;
+ parse_location = GrandParent(speaking);
+ }
+
+ if (!strcmp(word[1], "~oops"))
+ {
+ strcpy(parseerr, "");
+
+ /* "oops" on its own */
+ if (words==1 || !strcmp(oops, ""))
+ {
+ ParseError(16, 0); /* "You'll have to make a mistake..." */
+ return 0;
+ }
+
+ /* trying to correct more than one word */
+ if (words > 2 || oopscount)
+ {
+ ParseError(17, 0); /* "...one word at a time..." */
+ return 0;
+ }
+
+ /* trying to correct a correction */
+ if (!strcmp(Left(errbuf, 5), "~oops"))
+ {
+ ParseError(13, 0);
+ return 0;
+ }
+
+ /* Rebuild the corrected buffer */
+ oopscount = 1;
+ strcpy(line, word[2]);
+ for (i=1; i<=(int)strlen(errbuf); i++)
+ {
+ if (!strcmp(Mid(errbuf, i, strlen(oops)), oops))
+ break;
+ }
+
+ strcpy(buffer, errbuf);
+ buffer[i-1] = '\0';
+ strcat(buffer, line);
+
+ strcat(buffer, Right(errbuf, strlen(errbuf) - i - strlen(oops) + 1));
+
+ SeparateWords();
+ if (!Parse())
+ return 0;
+
+ CallLibraryParse();
+ }
+
+ if (word[1][0]=='.') KillWord(1);
+
+
+ /*
+ * STEP 1: Match verb
+ *
+ */
+
+ ptr = 64;
+
+MatchVerb:
+
+#ifdef DEBUG_PARSER
+ Printout("MatchCommand(): Step 1");
+#endif
+ if (words==0)
+ return 0;
+
+ defseg = gameseg;
+
+ grammaraddr = 0;
+ domain = 0;
+ obj_match_state = 0;
+ xverb = 0;
+ starts_with_verb = 0;
+ objcount = 0;
+ parse_allflag = false;
+ objstart = 0;
+ object_is_number = false;
+
+ for (i=1; i<MAXWORDS; i++)
+ objword_cache[i] = 0;
+
+ var[object] = 0;
+ var[xobject] = 0;
+ var[self] = 0;
+ var[verbroutine] = 0;
+
+ while ((a = Peek(ptr)) != 255)
+ {
+ defseg = gameseg;
+
+ /* verb or xverb header */
+ if (a==VERB_T || a==XVERB_T)
+ {
+ /* Skim through 1 or more verb words */
+ numverbs = Peek(ptr + 1);
+ verbptr = ptr + 2;
+ for (i=1; i<=numverbs; i++)
+ {
+ /* 0xffff signals something other than a
+ dictionary word--see BuildVerb() in hcbuild.c.
+ This will be the case, like, 1% of the time.
+ */
+
+ /* If it is a dictionary word... */
+ if (PeekWord(verbptr)!=0xffff)
+ {
+ /* If one of the verb words matches the first
+ word in the input line
+ */
+ if (wd[1]==PeekWord(verbptr))
+ {
+ grammaraddr = ptr;
+ goto GotVerb;
+ }
+
+ verbptr += 2;
+ }
+
+ /* ...otherwise assume it's an object (value) */
+ else
+ {
+ codeptr = verbptr + 1; /* skip 0xffff */
+
+ /* GetVal(), not GetValue(), since it's
+ always a simple value
+ */
+ obj = GetVal();
+
+ /* codeptr can't be >65535 on a 16-bit
+ compiler
+ */
+ verbptr = (unsigned int)codeptr;
+ propaddr = PropAddr(obj, noun, 0);
+ if (propaddr)
+ {
+ defseg = proptable;
+ a = Peek(propaddr+1); /* obj.#prop */
+ defseg = gameseg;
+
+ for (j=1; j<=a; j++)
+ {
+ if (wd[1]==(unsigned)GetProp(obj, noun, j, 0))
+ {
+ grammaraddr = ptr;
+ goto GotVerb;
+ }
+ }
+ }
+ }
+ }
+
+ /* Otherwise skip over this verb header */
+ ptr += 2 + numverbs * 2;
+ }
+
+ /* anything else */
+ else
+ ptr += Peek(ptr + 1) + 1;
+ }
+
+
+ /*
+ * STEP 2: Match object/character (if no verb match)
+ *
+ */
+#ifdef DEBUG_PARSER
+ Printout("MatchCommand(): Step 2");
+#endif
+ /* If we hit the end of the grammar without finding a verb match: */
+ if (Peek(ptr)==255)
+ {
+ /* If we already tried this once */
+ if (gotspeaker)
+ {
+ if (!starts_with_verb)
+ /* "Better start with a verb..." */
+ ParseError(2, 0);
+ else
+ /* "That doesn't make any sense..." */
+ ParseError(6, 0);
+ return 0;
+ }
+
+ /* See if the command begins with an object
+ (character) name:
+ */
+ flag = 0;
+ if (AnyObjWord(1)==1)
+ flag = 1;
+
+ /* No match, ergo an invalid command */
+ if (flag==0 && nextverb==true)
+ {
+ strcpy(parseerr, "");
+ ParseError(6, 0); /* "...doesn't make any sense..." */
+ return 0;
+ }
+
+ /* No provision made for addressing objects (characters) */
+ if (flag==0 || speaktoaddr==0)
+ {
+ strcpy(parseerr, "");
+ ParseError(2, 0); /* "Better start with a verb..." */
+ return 0;
+ }
+
+ /* Count how many object words there are */
+ for (i=2; i<=words; i++)
+ {
+ if (AnyObjWord(i)!=1)
+ break;
+ }
+
+ /* Try to match the first word to a valid object */
+ objfinish = i - 1;
+ obj_match_state = 5;
+ i = 1;
+ recursive_call = 0;
+
+ if (MatchObject(&i) != true)
+ return 0; /* unsuccessful */
+
+ speaking = pobj; /* successful */
+ gotspeaker = true;
+
+ /* So erase the object name from the start of the line */
+ for (i=1; i<=objfinish; i++)
+ KillWord(1);
+ if (word[1][0]=='~') KillWord(1);
+
+ /* If it's a name and that's all...*/
+ if (words==0)
+ return true;
+
+ /* ...or else proceed as usual */
+ ptr = 64;
+ goto MatchVerb;
+ }
+ else if (!gotspeaker)
+ speaking = 0;
+
+GotVerb:
+
+ if (!speaking)
+ {
+ var[actor] = var[player];
+ parse_location = var[location];
+ }
+ else
+ {
+ var[actor] = speaking;
+ parse_location = GrandParent(speaking);
+ }
+
+ obj_match_state = 0;
+ starts_with_verb = 1;
+ strcpy(parseerr, word[1]);
+
+ if (Peek(grammaraddr)==XVERB_T) xverb = true;
+ grammaraddr += 2 + numverbs * 2;
+
+ /*
+ * STEP 3: Match proper grammar structure (syntax)
+ *
+ */
+#ifdef DEBUG_PARSER
+ Printout("MatchCommand(): Step 3");
+#endif
+
+ /*
+ * (STEP 4: We'll be matching xobject, if any, before object)
+ *
+ */
+#ifdef DEBUG_PARSER
+ Printout("MatchCommand(): Step 4");
+#endif
+
+ /* Loop until end of grammar table, or next verb:
+ */
+ while (Peek(grammaraddr)!=255 &&
+ Peek(grammaraddr)!=VERB_T && Peek(grammaraddr)!=XVERB_T)
+ {
+ wordnum = 1;
+
+ nextgrammar = grammaraddr + Peek(grammaraddr + 1) + 1;
+
+ /* Loop until end of table or next verb: */
+ while (Peek(grammaraddr) != 255 && Peek(grammaraddr) != VERB_T && Peek(grammaraddr) != XVERB_T)
+ {
+ mw = MatchWord(&wordnum);
+
+ if (mw==1)
+ {
+ /* end of both input and grammar */
+ if (wd[wordnum]==0 && Peek(grammaraddr)==ROUTINE_T)
+ {
+ full_buffer = (char)wordnum;
+ break;
+ }
+
+ /* end of grammar, not input */
+ if (Peek(grammaraddr)==ROUTINE_T)
+ {
+ mw = false;
+ goto NextStructure;
+ }
+ }
+ else
+ {
+ /* If error already signalled */
+ if (mw==2)
+ return 0;
+
+ /* No match, so try next structure */
+ else
+ {
+NextStructure:
+ grammaraddr = nextgrammar;
+ var[object] = 0;
+ var[xobject] = 0;
+ var[verbroutine] = 0;
+ domain = 0;
+ odomain = 0;
+ obj_match_state = 0;
+ xverb = 0;
+ objcount = 0;
+ objstart = 0;
+ break;
+ }
+ }
+ }
+
+ /* Matched the complete syntax of a verb */
+ if (mw==1)
+ {
+ var[verbroutine] = PeekWord(grammaraddr + 1);
+ break;
+ }
+ }
+
+ if (mw != 1)
+ {
+ if (mw==0) /* mw = 2 if error already printed */
+ {
+ /* If there's more grammar to check... */
+ if (Peek(grammaraddr) != 255)
+ {
+ ptr = grammaraddr;
+ nextverb = true;
+ goto MatchVerb;
+ }
+
+ /* ...or if we reached the end without a sensible
+ syntax matched:
+ */
+ strcpy(parseerr, "");
+
+ /* "...doesn't make any sense..." */
+ ParseError(6, 0);
+ }
+ return 0;
+ }
+
+
+ /*
+ * STEP 5: Match remaining object(s), if any
+ *
+ */
+#ifdef DEBUG_PARSER
+ Printout("MatchCommand(): Step 5");
+#endif
+
+ /* If there are objects waiting to be loaded into objlist[] */
+ if (objstart)
+ {
+ ResetFindObject();
+
+ obj_match_state = 2;
+ recursive_call = false;
+ if (odomain) domain = odomain;
+
+ grammaraddr = objgrammar;
+ i = objstart;
+
+ /* If there was a problem matching them */
+ if (MatchObject(&i)==false)
+ return 0;
+ }
+
+ /* Got a successfully matched verb with all the requisite
+ parameters (if any).
+ */
+
+ if (words < wordnum)
+ remaining = 0;
+ else
+ remaining = (char)(words - wordnum);
+
+#ifdef DEBUG_PARSER
+ Printout("MatchCommand(): Leaving");
+#endif
+ return true;
+}
+
+bool Hugo::MatchObject(int *wordnum) {
+ char found_noun = false;
+ int i, j, k, m, flag;
+ int mobjs; unsigned int mobj[MAX_MOBJ];
+ bool allmatch;
+ int roomloc;
+ int bestobj = 0;
+ char bestavail = 0;
+
+ /* If this is a recursive call, we're not adding new objects */
+ if (!recursive_call) addflag = true;
+
+ stack_depth = 0;
+
+ pobj = 0; /* possible object */
+ objcount = 0; /* of objlist[] */
+ pobjcount = 0; /* of pobjlist[] */
+ mobjs = 0; /* # of previous words in phrase */
+ bestavail = 0; /* adjective or noun */
+
+#ifdef DEBUG_PARSER
+ Printout("MatchObject(): Entering");
+#endif
+ strcpy(parseerr, "");
+
+ do /* starting at word #a */
+ {
+ /* Check first to make sure it's not a housekeeping word
+ such as "~and" or "~all".
+ */
+ if (word[*wordnum][0]!='~' && word[*wordnum][0]!='\0')
+ {
+ if (parseerr[0]!='\0') strcat(parseerr, " ");
+ strcat(parseerr, word[*wordnum]);
+
+ flag = 0;
+ for (i=0; i<objects; i++)
+ {
+ if (wd[*wordnum]==0)
+ break;
+
+ /* Might be this object if wd[*wordnum] is an
+ adjective or noun of object i
+ */
+ m = ObjWord(i, wd[*wordnum]);
+
+ if (m)
+ {
+ flag = true;
+ allmatch = true;
+
+ /* check previously matched words */
+ for (j=1; j<=mobjs; j++)
+ {
+ if (!ObjWord(i, mobj[j])) /* || wd[*wordnum]==mobj[j]) */
+ allmatch = false;
+ }
+
+ /* matches all previous words */
+ if (allmatch==true)
+ {
+ AddPossibleObject(i, (char)m, wd[*wordnum]);
+ pobj = i;
+ if (bestavail==0 || bestavail>=(char)m)
+ {
+ if (!bestobj)
+ bestobj = i;
+ bestavail = (char)m;
+ }
+ }
+
+ /* doesn't match previous words */
+ else
+ SubtractPossibleObject(i);
+ }
+
+ /* definitely not this object */
+ else
+ SubtractPossibleObject(i);
+ }
+
+
+ /* If checking the start of an input line, i.e. for
+ a command addressed to an object (character):
+ */
+ if (obj_match_state==5 && !flag) goto Clarify;
+ }
+
+ else if (!strcmp(word[*wordnum], "~any"))
+ goto NextLoop;
+
+ /* "~and", "~all",... */
+ else
+ goto Clarify;
+
+ /* Didn't get any suspects */
+ if (pobjcount==0)
+ {
+ /* If "~and", "~all",... */
+ if (word[*wordnum][0]=='~')
+ {
+ /* If checking the xobject */
+ if (obj_match_state==1)
+ {
+ strcpy(parseerr, word[1]);
+ /* "...can't use multiple objects..."
+ (as indirect objects) */
+ ParseError(7, 0);
+ return false;
+ }
+ goto Clarify;
+ }
+
+ /* Got an unmatchable sequence of words */
+ else
+ {
+ if (obj_match_state==5)
+ {
+ if (!starts_with_verb)
+ /* "Better start with a verb..." */
+ ParseError(2, 0);
+ else
+ /* "That doesn't make any sense..." */
+ ParseError(6, 0);
+ }
+ else
+ /* "(no such thing)..." */
+ ParseError(5, 0);
+ return false;
+ }
+ }
+
+ if (word[*wordnum][0]!='~')
+ {
+ /* Go back for next word in this object phrase */
+
+ mobjs++;
+ if (mobjs==MAX_MOBJ)
+ {
+ /* "(no such thing)..." */
+ ParseError(5, 0);
+ return false;
+ }
+ mobj[mobjs] = wd[*wordnum];
+ }
+ else
+ {
+ /* Since hitting "~and" or "~all", we've obviously
+ finished an object phrase
+ */
+ (*wordnum)++;
+ goto Clarify;
+ }
+
+NextLoop:
+ (*wordnum)++; /* next word */
+ if ((*wordnum > words || word[*wordnum][0]=='\0')
+ || *wordnum > objfinish)
+ goto Clarify;
+ }
+ while (true); /* endless loop */
+
+
+Clarify:
+
+#ifdef DEBUG_PARSER
+ Printout("MatchObject(): Clarify");
+#endif
+ /* If "~and", "~all",... */
+ if (word[*wordnum][0]=='~')
+ {
+ /* If checking the xobject or addressing a command */
+ if (obj_match_state==1 || speaking)
+ {
+ strcpy(parseerr, word[1]);
+ /* "...can't use multiple objects..."
+ (as indirect objects) */
+ ParseError(7, 0);
+ return false;
+ }
+ }
+
+ if (!strcmp(word[*wordnum], "~all")) /* if "~all" is specified */
+ {
+ parse_allflag = true;
+
+ /* If one or more words were already matched, however... */
+
+ if (mobjs > 0)
+ {
+ ParseError(6, 0); /* "...doesn't make any sense..." */
+ return false;
+ }
+
+ if (!domain) /* no particular domain object specified */
+ roomloc = parse_location;
+ else
+ roomloc = domain;
+
+ AddAllObjects(roomloc);
+
+ (*wordnum)++;
+
+
+ /* Done processing the object phrase yet? */
+
+ /* only >GET ALL EXCEPT... if we're not done the object phrase */
+ if (*wordnum<=objfinish && strcmp(word[*wordnum], "~except"))
+ {
+ ParseError(6, 0); /* "Doesn't make any sense..." */
+ return false;
+ }
+
+ if ((*wordnum > words || word[*wordnum][0]=='\0')
+ || (obj_match_state != 1 && *wordnum >= objfinish))
+ {
+ if (!objcount && !speaking)
+ {
+ strcpy(parseerr, word[1]);
+ ParseError(9, 0); /* "Nothing to (verb)..." */
+ return false;
+ }
+ return true;
+ }
+
+ /* Go back for the next piece of the phrase */
+ pobjcount = 0;
+ (*wordnum)--;
+ goto NextLoop;
+ }
+
+
+ /* If we have a possible object or set of objects, go through the
+ disqualification process, either to sort out any confusion, or
+ even if there's only one possible object, to make sure it's
+ available
+ */
+ if (pobjcount >= 1)
+ {
+ bestavail = 0;
+
+ for (k=0; k<pobjcount; k++) /* disqualify if unavailable */
+ {
+ i = pobjlist[k].obj;
+
+ /* held or multiheld */
+ if ((domain) && domain==var[actor])
+ {
+ if (Parent(i) != var[actor])
+ {
+ SubtractPossibleObject(i);
+ k--;
+ }
+ else
+ pobj = i;
+ }
+
+ else if ((Peek(grammaraddr)==NOTHELD_T || Peek(grammaraddr)==MULTINOTHELD_T)
+ && Parent(i)==var[actor])
+ {
+ SubtractPossibleObject(i);
+ k--;
+
+ /* if this was the last suspect */
+ if (pobjcount<1) /* i.e., 0 */
+ {
+ ParseError(11, i); /* "You don't see that..." */
+ return false;
+ }
+ }
+
+ /* otherwise */
+ else if (Available(i, 0)==false) /* and obj_match_state!=5) */
+ {
+ SubtractPossibleObject(i);
+ k--;
+ }
+ else
+ pobj = i;
+
+ /* Try to determine the best available object */
+ if ((!bestavail) || (bestavail==(char)adjective && pobjlist[k].type==(char)noun))
+ {
+ m = domain;
+ /* Temporary parent domain */
+ domain = -1;
+ if (Available(i, 0))
+ {
+ bestavail = pobjlist[k].type;
+ bestobj = i;
+ }
+ domain = m;
+ }
+
+ /* Pick a default (poor) best match */
+ if (!bestobj)
+ bestobj = i;
+ }
+
+
+ /* Disqualify if an object is less exact--i.e., if the
+ word is a noun for one object but only an adjective for
+ another, disqualify the one for which it is an
+ adjective.
+ */
+ if (pobjcount > 1)
+ {
+ for (i=0; i<pobjcount; i++)
+ {
+ if (pobjlist[i].type==(char)noun)
+ found_noun = true;
+ }
+
+ if (found_noun)
+ {
+ for (i=0; i<pobjcount; i++)
+ {
+ /* Use ObjWord()'s test to see if this word
+ should get upgraded from an adjective to a noun
+ */
+ if (pobjlist[i].type==(char)adjective)
+ {
+ if (ObjWordType(pobjlist[i].obj, wd[*wordnum-1], noun))
+ pobjlist[i].type = (char)noun;
+ }
+
+ if (pobjlist[i].type==(char)adjective)
+ {
+ SubtractPossibleObject(pobjlist[i].obj);
+ i--;
+ }
+
+ if (pobjcount==1) break;
+ }
+ }
+ }
+
+
+ /* If we're dealing with an 'anything' object, prefer an
+ object that's currently available to one that isn't
+ */
+ if (pobjcount > 1 && Peek(grammaraddr)==ANYTHING_T)
+ {
+ for (i=0; i<pobjcount; i++)
+ {
+ if (Available(pobjlist[i].obj, true))
+ {
+ for (j=0; j<pobjcount; j++)
+ {
+ if (j!=i)
+ {
+ if (!Available(pobjlist[j].obj, true))
+ {
+ SubtractPossibleObject(pobjlist[j].obj);
+ j--; /* don't skip one */
+ if (pobjcount<=1) break;
+ }
+ }
+ }
+ }
+ if (pobjcount<=1) break;
+ }
+ }
+
+
+ /* Use whatever disambiguation the FindObject routine might provide */
+ if (pobjcount > 1 && findobjectaddr)
+ {
+ for (k=0; k<pobjcount; k++)
+ {
+ i = pobjlist[k].obj;
+ ret = 0;
+ passlocal[0] = i;
+ passlocal[1] = 0;
+ PassLocals(2);
+
+ SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+#if defined (DEBUGGER)
+ DebugRunRoutine((long)findobjectaddr*address_scale);
+#else
+ RunRoutine((long)findobjectaddr*address_scale);
+#endif
+ retflag = 0;
+
+ if (ret==0) /* returned false */
+ {
+ SubtractPossibleObject(i);
+ k--; /* so we don't skip one */
+ if (pobjcount<=1) break;
+ }
+ else
+ {
+ bestobj = i;
+ pobj = i;
+ }
+
+ if (pobjcount==1) break;
+ }
+ }
+
+
+ /* If this is a clarification-required call to MatchObject(),
+ return true if more than one possible object is found.
+ */
+ if (pobjcount > 1 && recursive_call)
+ return true;
+
+
+ /* On the other hand, if we've managed to disqualify
+ everything:
+ */
+ if (pobjcount==0 && !speaking)
+ {
+ if (obj_match_state==5)
+ {
+ if (!starts_with_verb)
+ /* "Better start with a verb..." */
+ ParseError(2, 0);
+ else
+ /* "That doesn't make any sense..." */
+ ParseError(6, 0);
+ }
+ else
+ {
+ if ((!domain) || domain != var[actor])
+ {
+ if (Peek(grammaraddr)==ANYTHING_T)
+ /* "...haven't seen..." */
+ ParseError(10, bestobj);
+ else if (domain)
+ /* "...don't see that there..." */
+ ParseError(14, bestobj);
+ else
+ /* "...don't see any..." */
+ ParseError(11, bestobj);
+ }
+ else
+ ParseError(15, bestobj); /* "...don't have any..." */
+ }
+ return false;
+ }
+ else if (pobjcount==0)
+ pobj = bestobj;
+
+
+ /* If, after all this, there still exists some confusion
+ about what object is meant:
+ */
+ if (pobjcount > 1)
+ {
+ int wtemp;
+ unsigned int wdtemp[MAXWORDS+1];
+ int tempobjlist[MAXWORDS+1], tempobjcount;
+ struct pobject_structure temppobjlist[MAXPOBJECTS];
+ int tempobjfinish;
+ int temppobjcount;
+ bool tempaddflag;
+
+ char tempxverb = xverb;
+ ParseError(8, 0); /* "Which...do you mean..." */
+ xverb = tempxverb;
+
+ for (i=1; i<=words; i++) /* save word arrays */
+ wdtemp[i] = wd[i];
+ wtemp = words;
+ tempobjfinish = objfinish;
+
+ for (i=0; i<=objcount; i++) /* save object arrays */
+ tempobjlist[i] = objlist[i];
+ for (i=0; i<=pobjcount; i++)
+ temppobjlist[i] = pobjlist[i];
+
+ tempobjcount = objcount;
+ temppobjcount = pobjcount;
+ tempaddflag = addflag;
+
+
+ /* Get a new input and properly parse it; after
+ all, it may be returned to MatchCommand() as
+ a potentially valid command.
+ */
+ GetCommand();
+ SeparateWords();
+ if (words==0)
+ {
+ ParseError(0, 1);
+ return false;
+ }
+ if (!Parse())
+ return false;
+
+ objfinish = words;
+
+ /* Do we not care? i.e. is "any", "either", etc. given? */
+ if (!strcmp(word[1], "~any") && words==1)
+ {
+ for (k=0; k<pobjcount; k++)
+ {
+ i = pobjlist[k].obj;
+
+ if (strcmp(Name(i), ""))
+ {
+ pobj = i;
+ sprintf(line, "(%s)", Name(i));
+ AP(line);
+ goto RestoreTempArrays;
+ }
+ }
+ }
+
+ /* Check to see if any object is disqualified by
+ any one of the words.
+ */
+ for (i=1; i<=words; i++)
+ {
+ if (word[i][0]!='~')
+ {
+ for (j=0; j<pobjcount; j++)
+ {
+ if (!ObjWord(pobjlist[j].obj, wd[i]))
+ {
+ SubtractPossibleObject(pobjlist[j].obj);
+ j--;
+ }
+ }
+ }
+ }
+ if (pobjcount==1)
+ {
+ pobj = pobjlist[0].obj;
+ full_buffer = false;
+ goto RestoreTempArrays;
+ }
+
+ /* Check to see if the first word is a noun
+ or adjective for any of the possible suspect objects.
+ */
+ flag = 0;
+ for (i=0; i<pobjcount; i++)
+ {
+ if (ObjWord(pobjlist[i].obj, wd[1]))
+ {
+ flag = 1;
+ break;
+ }
+ }
+ if (!strcmp(word[1], "~all"))
+ flag = 1;
+
+ /* If not, tell MatchCommand() that there's a
+ new command coming down the hopper.
+ */
+ if (flag==0)
+ {
+ full_buffer = true;
+ return false;
+ }
+
+ /* Here, tell MatchObject()--this function--
+ that this isn't a virgin call.
+ */
+ recursive_call = true;
+ addflag = true;
+ i = 1;
+ j = MatchObject(&i);
+ if (j==false) /* parsing error */
+ {
+ full_buffer = false;
+ return false;
+ }
+ else if (j==-1) /* multiple matches found */
+ {
+ /* See if a single entered word matches
+ exactly the name of only one object
+ */
+ if (words==1)
+ {
+ flag = 0;
+ for (j=0; j<temppobjcount; j++)
+ {
+ if (wd[1]==(unsigned int)GetProp(temppobjlist[j].obj, 0, 1, 0))
+ {
+ pobj = temppobjlist[j].obj;
+ flag++;
+ }
+ }
+ if (flag==1)
+ {
+ full_buffer = false;
+ goto RestoreTempArrays;
+ }
+ }
+
+ /* Now, even if we weren't able to find a
+ match, check to see if the last word
+ belongs to only one of the possible
+ suspects, especially if it's a noun.
+ */
+ flag = 0;
+ bestobj = 0;
+ i = words;
+ while (wd[i]==0) i--;
+ for (j=0; j<temppobjcount; j++)
+ {
+ if ((m = ObjWord(temppobjlist[j].obj, wd[i])) != 0)
+ {
+ if (!bestobj || m<=bestobj)
+ {
+ flag++;
+ bestobj = m;
+ pobj = temppobjlist[j].obj;
+ }
+ }
+ }
+ if (flag != 1)
+ {
+ /* "You'll have to be more specific..." */
+ ParseError(13, 0);
+ full_buffer = false;
+ return false;
+ }
+ full_buffer = false;
+ }
+
+RestoreTempArrays:
+ /* Rebuild <buffer> and word[] array */
+ strcpy(buffer, "");
+ for (i=1; i<=wtemp; i++)
+ {
+ strcat(buffer, GetWord(wdtemp[i]));
+ strcat(buffer, " ");
+ }
+ SeparateWords();
+
+ /* Restore wd[] array after SeparateWords() */
+ for (i=1; i<=wtemp; i++)
+ wd[i] = wdtemp[i];
+ words = wtemp;
+ objfinish = tempobjfinish;
+
+ /* Restore object lists */
+ for (i=0; i<temppobjcount; i++)
+ pobjlist[i] = temppobjlist[i];
+ pobjcount = temppobjcount;
+
+ /* Multiple disambig. results for a non-multi verb? */
+ i = Peek(grammaraddr);
+ if (objcount>1 && i!=MULTI_T && i!=MULTIHELD_T && i!=MULTINOTHELD_T)
+ {
+ strcpy(parseerr, word[1]);
+ /* "You can't...multiple objects." */
+ ParseError(3, 0);
+ return false;
+ }
+
+ /* Check objlist against original pobjlist */
+ for (i=0; i<objcount; i++)
+ {
+ flag = 0;
+ for (j=0; j<pobjcount; j++)
+ {
+ if (pobjlist[j].obj==objlist[i])
+ flag = true;
+ }
+ if (!flag)
+ {
+ /* "You'll have to be more specific..." */
+ ParseError(13, 0);
+ full_buffer = false;
+ return false;
+ }
+ }
+
+ /* Check pobjlist against disambiguation objlist */
+ for (i=0; i<pobjcount; i++)
+ {
+ flag = 0;
+ for (j=0; j<objcount; j++)
+ {
+ if (pobjlist[i].obj==objlist[j])
+ flag = true;
+ }
+ if (!flag)
+ {
+ SubtractPossibleObject(pobjlist[i].obj);
+ i--;
+ }
+ }
+
+ for (i=0; i<=tempobjcount; i++)
+ objlist[i] = tempobjlist[i];
+ objcount = (char)tempobjcount;
+ addflag = (char)tempaddflag;
+
+ if (word[*wordnum][0]=='~') (*wordnum)--;
+ }
+ }
+
+ /* Rule out "noun noun" and "noun adjective" combinations */
+
+ i = 0; /* count nouns */
+ k = 10; /* best (i.e., lowest) match */
+ for (j=1; j<=mobjs; j++)
+ {
+ if ((m = ObjWord(pobj, mobj[j]))==noun)
+ {
+ k = noun;
+ i++;
+ }
+ else if (k==noun) i = 2;
+ }
+
+ /* Can't use more than one noun for a given object */
+ if (i > 1)
+ {
+ /* "...no such thing" */
+ ParseError(5, 0);
+ return false;
+ }
+
+ /* Check finally to make sure it's valid */
+ ResetFindObject();
+ if (!ValidObj(pobj))
+ return false;
+
+ /* Finally--now add it or subtract it from the list, as
+ appropriate:
+ */
+ if (addflag==true)
+ {
+ if (pobjcount)
+ {
+ for (i=0; i<pobjcount; i++)
+ AddObj(pobjlist[i].obj);
+ }
+
+ /* backup */
+ else if (pobj != 0)
+ AddObj(pobj);
+ }
+ else
+ SubtractObj(pobj);
+
+ /* If the object wasn't where it was specifically claimed to be,
+ applies to second pass through object phrase(s) after xobject
+ is found (since <obj_match_state> is 2):
+ */
+ if (obj_match_state==2 && domain != 0 &&
+ Parent(pobj) != domain && pobj != 0 && !speaking)
+ {
+ if (domain==var[player])
+ ParseError(15, pobj); /* "You don't have any..." */
+ else
+ ParseError(14, pobj); /* "You don't see any...there..." */
+ return false;
+ }
+
+ if (!strcmp(word[*wordnum], "~except"))
+ {
+ if (obj_match_state==1)
+ {
+ ParseError(7, 0); /* can't have multiple xobjects */
+ return false;
+ }
+ addflag = false;
+ }
+
+ if ((strcmp(word[*wordnum], "~and") && strcmp(word[*wordnum], "~except"))
+ || obj_match_state==5)
+ {
+ /* At the end yet? */
+ if ((*wordnum > words || word[*wordnum][0]=='\0')
+ || *wordnum > objfinish)
+ {
+ if ((objcount > 0 && pobj != 0) || recursive_call || speaking)
+ return true;
+ else
+ {
+ /* No objects found */
+ strcpy(parseerr, word[1]);
+ ParseError(9, 0); /* "Nothing to (verb)..." */
+ return false;
+ }
+ }
+ }
+
+ /* Go back for the next object phrase */
+ pobjcount = 0;
+ mobjs = 0;
+ strcpy(parseerr, "");
+
+ goto NextLoop;
+}
+
+int Hugo::MatchWord(int *wordnum) {
+ char num[18];
+ int i, p, t, flag, finish;
+ unsigned int thissyntax, nextsyntax;
+
+ if (wd[*wordnum]==0)
+ return 0;
+
+ switch ((t = Peek(grammaraddr)))
+ {
+ /* the verb ("*") */
+ case ASTERISK_T:
+ (*wordnum)++;
+ AdvanceGrammar();
+ return 1;
+
+ /* a non-specific dictionary word */
+ case WORD_T:
+ if (obj_match_state==1)
+ var[xobject] = wd[*wordnum];
+ else
+ {
+ var[object] = wd[*wordnum];
+ obj_match_state = 1;
+ }
+ object_is_number = true;
+ (*wordnum)++;
+ AdvanceGrammar();
+ return 1;
+
+ /* a specific dictionary entry */
+ case DICTENTRY_T:
+CheckWord:
+ /* matches word */
+ if (wd[*wordnum]==PeekWord(grammaraddr + 1))
+ {
+ (*wordnum)++;
+ AdvanceGrammar();
+ while (Peek(grammaraddr)==9)
+ grammaraddr += 4;
+ return 1;
+ }
+ else
+ {
+ /* if next word is a "/" */
+ if (Peek(grammaraddr + 3)==FORWARD_SLASH_T)
+ {
+ AdvanceGrammar(); /* this word */
+ AdvanceGrammar(); /* "/" */
+ goto CheckWord;
+ }
+
+ return 0;
+ }
+
+ /* alternative dictionary words */
+ case FORWARD_SLASH_T:
+ grammaraddr++;
+ return 1;
+
+ /* a number */
+ case NUMBER_T:
+ if ((STARTS_AS_NUMBER(word[*wordnum])) &&
+ !strcmp(itoa(atoi(word[*wordnum]), num, 10), word[*wordnum]))
+ {
+ if (obj_match_state==1)
+ var[xobject] = atoi(word[*wordnum]);
+ else
+ {
+ var[object] = atoi(word[*wordnum]);
+ obj_match_state = 1;
+ }
+ object_is_number = true;
+ AdvanceGrammar();
+ (*wordnum)++;
+ return 1;
+ }
+ break;
+
+ /* a string enclosed in quotes */
+ case STRING_T:
+ if (parsestr[0]=='\"')
+ {
+ AdvanceGrammar();
+ (*wordnum)++;
+ return 1;
+ }
+ else
+ return 0;
+
+ default:
+ {
+
+ /* Some manifestation of an object (or objects) before domain is
+ found, since <obj_match_state> is initially set to 0:
+ */
+ if (obj_match_state==0)
+ {
+ if (Peek(grammaraddr)==HELD_T || Peek(grammaraddr)==MULTIHELD_T)
+ odomain = var[actor];
+
+ obj_match_state = 1; /* since next set of object words
+ must be the xobject */
+ objstart = *wordnum;
+ objgrammar = grammaraddr;
+
+ while (wd[*wordnum] != 0)
+ {
+ finish = *wordnum;
+
+ /* Check what's coming up in case it's a dictionary
+ word--which would override an object phrase.
+ */
+ thissyntax = grammaraddr;
+ AdvanceGrammar();
+ nextsyntax = grammaraddr;
+ grammaraddr = thissyntax;
+
+ /* dictionary word or string */
+CheckWordorString:
+ p = Peek(nextsyntax);
+ if (p==DICTENTRY_T || p==STRING_T)
+ {
+ if ((PeekWord(nextsyntax + 1)==wd[*wordnum]) ||
+ (p==STRING_T && wd[*wordnum]==UNKNOWN_WORD))
+ {
+ grammaraddr = nextsyntax;
+ if (*wordnum != objstart)
+ return 1;
+ else
+ return 0;
+ }
+ else if (Peek(nextsyntax + 3)==FORWARD_SLASH_T)
+ {
+ thissyntax = grammaraddr;
+ grammaraddr = nextsyntax + 3;
+ AdvanceGrammar();
+ nextsyntax = grammaraddr;
+ grammaraddr = thissyntax;
+
+ goto CheckWordorString;
+ }
+ }
+
+ /* or a number */
+ else if ((p==NUMBER_T && STARTS_AS_NUMBER(word[*wordnum])) &&
+ !strcmp(word[*wordnum], itoa(atoi(word[*wordnum]), num, 10)))
+ {
+ grammaraddr = nextsyntax;
+ if (*wordnum != objstart)
+ return 1;
+ else
+ return 0;
+ }
+
+ /* Pass over any object words--they'll be matched
+ specifically later in MatchCommand().
+ */
+ flag = 0;
+ if (AnyObjWord(*wordnum)==1)
+ {
+ (*wordnum)++;
+ flag = 1;
+ }
+
+ /* if "~and", "~all",... */
+ if (word[*wordnum][0]=='~')
+ {
+ int multicheck = Peek(grammaraddr);
+
+ (*wordnum)++;
+ flag = 1;
+
+ /* multi or multi(something) */
+ if (multicheck != MULTI_T &&
+ multicheck != MULTIHELD_T &&
+ multicheck != MULTINOTHELD_T)
+ {
+ strcpy(parseerr, word[1]);
+ /* "You can't...multiple objects." */
+ ParseError(3, 0);
+ return 2;
+ }
+ }
+
+ objfinish = finish;
+
+ if (flag==0)
+ return 0;
+ }
+
+ AdvanceGrammar();
+ return 1;
+ }
+
+ /* hitting xobject */
+
+ else if (obj_match_state==1)
+ {
+ int temp_objfinish = objfinish;
+
+ /* If we don't know the verbroutine, try to figure it out */
+ if (var[verbroutine]==0)
+ {
+ thissyntax = grammaraddr;
+ AdvanceGrammar();
+ nextsyntax = grammaraddr;
+ grammaraddr = thissyntax;
+ if (Peek(nextsyntax)==ROUTINE_T)
+ {
+ var[verbroutine] = PeekWord(nextsyntax+1);
+ }
+ }
+
+ p = Peek(grammaraddr);
+
+ /* If the xobject is specifically a parent of the
+ object(s) to be matched later:
+ */
+ if (p==PARENT_T) domain = -1;
+
+ /* Also deal with held xobjects */
+ t = domain;
+ if (p==HELD_T || p==MULTIHELD_T)
+ domain = var[actor];
+
+ /* Figure out where this xobject must end, as per grammar */
+ objfinish = -1;
+ if (*wordnum < words)
+ {
+ thissyntax = grammaraddr;
+ AdvanceGrammar();
+ nextsyntax = grammaraddr;
+ grammaraddr = thissyntax;
+
+CheckXobjectFinish:
+ p = Peek(nextsyntax);
+ for (i=*wordnum+1; i<=words; i++)
+ {
+ if (p==DICTENTRY_T || p==STRING_T)
+ {
+ if ((PeekWord(nextsyntax + 1)==wd[i]) ||
+ (p==STRING_T && wd[i]==UNKNOWN_WORD))
+ {
+ objfinish = i-1;
+ break;
+ }
+ }
+ else if (Peek(nextsyntax + 3)==FORWARD_SLASH_T)
+ {
+ thissyntax = grammaraddr;
+ grammaraddr = nextsyntax + 3;
+ AdvanceGrammar();
+ nextsyntax = grammaraddr;
+ grammaraddr = thissyntax;
+
+ goto CheckXobjectFinish;
+ }
+ else if ((p==NUMBER_T && STARTS_AS_NUMBER(word[*wordnum])) &&
+ !strcmp(word[*wordnum], itoa(atoi(word[*wordnum]), num, 10)))
+ {
+ objfinish = i;
+ break;
+ }
+ else
+ break;
+ }
+
+ }
+
+ if (objfinish==-1) objfinish = words;
+
+ /* Regardless, try to match the xobject */
+ recursive_call = false;
+ if (MatchObject(&(*wordnum))==0)
+ {
+ objfinish = temp_objfinish;
+
+ return 2;
+ }
+ else
+ {
+ objfinish = temp_objfinish;
+
+ domain = 0;
+ if (ValidObj(pobj)==false)
+ return 2;
+ domain = t;
+
+ if (objcount==1)
+ var[xobject] = objlist[0];
+ else
+ var[xobject] = pobj;
+ if (domain==-1) domain = var[xobject]; /* parent */
+ obj_match_state = 2;
+
+ AdvanceGrammar();
+
+ /* Can't have multiple xobjects */
+ if (objcount > 1)
+ {
+ ParseError(7, 0);
+ return 2;
+ }
+ objcount = 0;
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int Hugo::ObjWordType(int obj, unsigned int w, int type) {
+ int j, num;
+ unsigned int pa;
+
+ pa = PropAddr(obj, type, 0);
+ if (pa)
+ {
+ defseg = proptable;
+ num = Peek(pa + 1);
+
+ if (num==PROP_ROUTINE)
+ {
+ if ((unsigned int)GetProp(obj, type, 1, false)==w)
+ {
+ defseg = gameseg;
+ return true;
+ }
+ }
+ else
+ {
+ for (j=1; j<=num; j++)
+ {
+ if (PeekWord(pa + j * 2)==w)
+ {
+ defseg = gameseg;
+ return true;
+ }
+ }
+ }
+ }
+
+ defseg = gameseg;
+
+ return false;
+}
+
+int Hugo::ObjWord(int obj, unsigned int w) {
+ if ((obj_parselist) && !(obj_parselist[obj/8]&1<<(obj%8)))
+ return 0;
+
+ if (ObjWordType(obj, w, adjective))
+ return adjective;
+
+ if (ObjWordType(obj, w, noun))
+ return noun;
+
+ return 0;
+}
+
+int Hugo::Parse() {
+ char foundstring = 0; /* allow one unknown word/phrase */
+ int notfound_word = 0;
+ int i, j, k, m;
+ char num[33];
+ char tempword[81];
+ unsigned int period, comma;
+ unsigned int synptr;
+
+ period = FindWord(".");
+ comma = FindWord(",");
+
+ strcpy(parsestr, ""); /* for storing any unknown string */
+ parsed_number = 0; /* " " " parsed number */
+
+ for (i=1; i<=words; i++) /* find dictionary addresses */
+ {
+ if (word[i][0]=='\"' && foundstring==0)
+ {
+ strcpy(parsestr, word[i]);
+ foundstring = 1;
+ wd[i] = UNKNOWN_WORD;
+ }
+ else
+ {
+ wd[i] = FindWord(word[i]);
+
+ /* Numbers -32768 to 32767 are valid...*/
+ if (!strcmp(word[i], itoa(atoi(word[i]), num, 10)))
+ {
+#if !defined (MATH_16BIT)
+ if (atoi(word[i]) > 32767 || atoi(word[i]) < -32768)
+ goto NotinDictionary;
+#endif
+ parsed_number = atoi(word[i]);
+ if (parseerr[0]=='\0')
+ strcpy(parseerr, word[i]);
+ }
+
+ /* Otherwise it must be a dictionary entry */
+ else
+ {
+ /* If it's not in the dictionary */
+ if (wd[i]==UNKNOWN_WORD)
+ {
+NotinDictionary:
+ if (!notfound_word)
+ {
+ strcpy(parseerr, word[i]);
+ strcpy(oops, word[i]);
+
+ notfound_word = i;
+ }
+ }
+ }
+ }
+ }
+
+ /* Return here instead of immediately, so that we have a chance
+ to load the rest of the recognized words into the word array
+ */
+ if (notfound_word)
+ {
+ i = notfound_word;
+
+ /* "...can't use the word..." */
+ ParseError(1, 0);
+ strcpy(errbuf, "");
+ for (i=1; i<=words; i++)
+ {
+ strcat(errbuf, word[i]);
+ if (i != words) strcat(errbuf, " ");
+ }
+
+ return 0;
+ }
+
+ wd[words+1] = 0;
+ oopscount = 0;
+
+
+ /* Do synonyms, removals, compounds, punctuation */
+
+ for (i=1; i<=words; i++) /* Look through words... */
+ {
+ synptr = 2;
+
+ for (j=1; j<=syncount; j++) /* ...and alterations */
+ {
+ defseg = syntable;
+ if (wd[i]==PeekWord(synptr + 1))
+ {
+ switch (Peek(synptr))
+ {
+ case 0: /* synonym */
+ {
+ defseg = syntable;
+ wd[i] = PeekWord(synptr + 3);
+ m = strlen(GetWord(wd[i])) - strlen(word[i]);
+ if (m)
+ {
+ if (m + (int)strlen(buffer) > 81)
+ {strcpy(buffer, "");
+ words = 0;
+ ParseError(0, 0);
+ return 0;}
+
+ for (k=words; k>i; k--)
+ {
+ strcpy(tempword, word[k]);
+ word[k] += m;
+ strcpy(word[k], tempword);
+ }
+ }
+ strcpy(word[i], GetWord(wd[i]));
+ i--;
+ break;
+ }
+
+ case 1: /* removal */
+ {
+ KillWord(i);
+ i--;
+ break;
+ }
+
+ case 2: /* compound */
+ {
+ if (wd[i+1]==PeekWord(synptr+3))
+ {
+ strcat(word[i], word[i+1]);
+ wd[i] = FindWord(word[i]);
+ KillWord(i+1);
+ }
+ break;
+ }
+ }
+ goto NextSyn;
+ }
+NextSyn:
+ synptr += 5;
+ }
+
+ if (wd[i]==comma)
+ {
+ if (strcmp(word[i+1], "~and"))
+ {
+ word[i] = "~and";
+ wd[i] = FindWord("~and");
+ }
+ else
+ KillWord(i);
+ }
+
+ if (wd[i]==period)
+ {
+ wd[i] = 0;
+ word[i] = "";
+ }
+ }
+
+ defseg = gameseg;
+
+ if (strcmp(word[1], "~oops")) strcpy(oops, "");
+
+ if (words==0)
+ {
+ ParseError(0,0); /* What? */
+ return false;
+ }
+
+ return true;
+}
+
+void Hugo::ParseError(int e, int a) {
+ int i, k, count;
+
+ remaining = 0;
+ xverb = true;
+
+ if (e==5 && !strcmp(parseerr, "")) e = 6;
+
+ if (parseerroraddr)
+ {
+ ret = 0;
+ passlocal[0] = e;
+ passlocal[1] = a;
+ PassLocals(2);
+
+ SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+#if defined (DEBUGGER)
+ DebugRunRoutine((long)parseerroraddr*address_scale);
+#else
+ RunRoutine((long)parseerroraddr*address_scale);
+#endif
+ stack_depth = 0;
+ retflag = 0;
+ if (ret)
+ {
+ if (ret==2) reparse_everything = true;
+ return;
+ }
+ }
+
+ switch (e)
+ {
+ case 0:
+ AP("What?");
+ break;
+
+ case 1:
+ sprintf(line, "You can't use the word \"%s\".", parseerr);
+ AP(line);
+ break;
+
+ case 2:
+ AP("Better start with a verb.");
+ break;
+
+ case 3:
+ sprintf(line, "You can't %s multiple objects.", parseerr);
+ AP(line);
+ break;
+
+ case 4:
+ AP("Can't do that.");
+ break;
+
+ case 5:
+ sprintf(line, "You haven't seen any \"%s\", nor are you likely to in the near future even if such a thing exists.", parseerr);
+ AP(line);
+ break;
+
+ case 6:
+ AP("That doesn't make any sense.");
+ break;
+
+ case 7:
+ AP("You can't use multiple objects like that.");
+ break;
+
+ case 8:
+ {
+ sprintf(line, "Which %s do you mean, ", !parse_called_twice?parseerr:"exactly");
+ count = 1;
+ for (k=0; k<pobjcount; k++)
+ {
+ i = pobjlist[k].obj;
+
+ if (strcmp(Name(i), ""))
+ {
+ if (count==pobjcount)
+ {
+ if (count > 2) strcat(line, ",");
+ strcat(line, " or ");
+ }
+ else
+ {
+ if (count != 1)
+ strcat(line, ", ");
+ }
+ if (GetProp(i, article, 1, 0))
+ {
+ const char *w = GetWord(GetProp(i, article, 1, 0));
+ /* Don't use "a" or "an" in listing */
+ /*
+ if (!strcmp(w, "a") || !strcmp(w, "an"))
+ strcat(line, "the ");
+ else
+ sprintf(line+strlen(line), "%s ", w);
+ */
+ /* We'll just use "the" */
+ if (w) strcat(line, "the ");
+ }
+ strcat(line, Name(i));
+ count++;
+ }
+ }
+ strcat(line, "?");
+ AP(line);
+ break;
+ }
+
+ case 9:
+ sprintf(line, "Nothing to %s.", parseerr);
+ AP(line);
+ break;
+
+ case 10:
+ AP("You haven't seen anything like that.");
+ break;
+
+ case 11:
+ AP("You don't see that.");
+ break;
+
+ case 12:
+ sprintf(line, "You can't do that with the %s.", Name(a));
+ AP(line);
+ break;
+
+ case 13:
+ AP("You'll have to be a little more specific.");
+ break;
+
+ case 14:
+ AP("You don't see that there.");
+ break;
+
+ case 15:
+ AP("You don't have that.");
+ break;
+
+ case 16:
+ AP("You'll have to make a mistake first.");
+ break;
+
+ case 17:
+ AP("You can only correct one word at a time.");
+ break;
+ }
+}
+
+void Hugo::RemoveWord(int a) {
+ if (a > words)
+ return;
+
+ for (; a<words; a++)
+ {
+ wd[a] = wd[a + 1];
+ objword_cache[a] = objword_cache[a + 1];
+ }
+ wd[words] = 0;
+ objword_cache[words] = 0;
+}
+
+void Hugo::ResetFindObject() {
+ if (findobjectaddr)
+ {
+ SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+ PassLocals(0);
+#if defined (DEBUGGER)
+ DebugRunRoutine((long)findobjectaddr*address_scale);
+#else
+ RunRoutine((long)findobjectaddr*address_scale);
+#endif
+ retflag = 0;
+ }
+}
+
+void Hugo::SeparateWords() {
+ char inquote = 0;
+ char a[1025];
+ char b[2];
+ char w1[17], w2[17]; /* for time conversions */
+ char temp[17];
+ short n1, n2; /* must be 16 bits */
+ int bloc = 0; /* buffer location */
+ int i;
+
+
+ /* First filter the line of any user-specified punctuation */
+ do
+ {
+ i = strcspn(buffer, punc_string);
+ if (buffer[i]) buffer[i] = ' ';
+ } while (buffer[i]);
+
+
+ /* Begin the word-splitting proper: */
+
+ words = 1; /* Setup a blank string */
+
+ for (i=0; i<MAXWORDS+1; i++)
+ {
+ word[i] = "";
+ wd[i] = 0;
+ }
+ word[1] = buffer;
+
+ strcpy(a, buffer);
+ strcpy(buffer, "");
+
+ for (i=1; i<=(int)strlen(a); i++)
+ {
+ if (inquote!=1 && isascii(a[i-1]))
+ b[0] = (char)tolower(a[i-1]);
+ else b[0] = a[i-1];
+ b[1] = '\0';
+
+ if (b[0]=='\"' && inquote==1)
+ {
+ strcpy(buffer+bloc, b);
+ bloc++;
+ inquote++;
+ }
+
+ if (b[0]=='\"' || ((b[0]==' ' || b[0]=='!' || b[0]=='?') && inquote!=1))
+ {
+ if (word[words][0]!='\0')
+ {
+ bloc++;
+ if (++words > MAXWORDS) words = MAXWORDS;
+ word[words] = buffer + bloc;
+ strcpy(word[words], "");
+ }
+
+ if (b[0]=='\"' && inquote==0)
+ {
+ strcpy(buffer+bloc, b);
+ bloc++;
+ inquote = 1;
+ }
+ }
+ else
+ {
+ if ((b[0]=='.' || b[0]==',') && inquote!=1)
+ {
+ if (word[words][0]!='\0')
+ {
+ bloc++;
+ if (++words > MAXWORDS) words = MAXWORDS;
+ }
+ word[words] = buffer + bloc;
+ strcpy(word[words], b);
+ bloc += strlen(b) + 1;
+ if (++words > MAXWORDS) words = MAXWORDS;
+ word[words] = buffer + bloc;
+ strcpy(word[words], "");
+ }
+ else
+ {
+ strcpy(buffer+bloc, b);
+ bloc++;
+ }
+ }
+ }
+
+ if (!strcmp(word[words], "")) words--;
+
+ for (i=1; i<=words; i++)
+ {
+ /* Convert hours:minutes time to minutes only */
+ if (strcspn(word[i], ":")!=strlen(word[i]) && strlen(word[i])<=5)
+ {
+ strcpy(w1, Left(word[i], strcspn(word[i], ":")));
+ strcpy(w2, Right(word[i], strlen(word[i]) - strcspn(word[i], ":") - 1));
+ n1 = (short)atoi(w1);
+ n2 = (short)atoi(w2);
+
+ if (!strcmp(Left(w2, 1), "0"))
+ strcpy(w2, Right(w2, strlen(w2) - 1));
+
+ /* If this is indeed a hh:mm time, write it back
+ as the modified word, storing the original hh:mm
+ in parse$:
+ */
+ if (!strcmp(w1, itoa((int)n1, temp, 10)) && !strcmp(w2, itoa((int)n2, temp, 10)) && (n1 > 0 && n1 < 25) && (n2 >= 0 && n2 < 60))
+ {
+ strcpy(parseerr, word[i]);
+ itoa(n1 * 60 + n2, word[i], 10);
+ }
+ }
+ }
+}
+
+void Hugo::SubtractObj(int obj) {
+ int i, j;
+
+ for (i=0; i<objcount; i++)
+ {
+ if (objlist[i]==obj)
+ {
+ for (j=i; j<objcount; j++)
+ objlist[j] = objlist[j+1];
+ objcount--;
+ return;
+ }
+ }
+}
+
+void Hugo::SubtractPossibleObject(int obj) {
+ int i, j, last = 0;
+
+ for (i=0; i<pobjcount; i++)
+ {
+ if (pobjlist[i].obj==obj)
+ {
+ if (pobjlist[i].obj==pobj && last!=0) pobj = last;
+
+ for (j=i; j+1<pobjcount; j++)
+ {
+ pobjlist[j] = pobjlist[j+1];
+ }
+ pobjcount--;
+
+#ifdef DEBUG_PARSER
+{
+ char buf[100];
+ sprintf(buf, "SubtractPossibleObject(%d:\"%s\")", obj, Name(obj));
+ Printout(buf);
+}
+#endif
+ return;
+ }
+ else last = pobjlist[i].obj;
+ }
+}
+
+void Hugo::TryObj(int obj) {
+ unsigned int tempdomain;
+
+ if ((obj_parselist) && !(obj_parselist[obj/8]&1<<(obj%8)))
+ return;
+
+ if (DomainObj(obj))
+ {
+ tempdomain = domain;
+ domain = 0;
+
+ if (Available(obj, 0) && !InList(Parent(obj)))
+ {
+ AddObj(obj);
+ }
+ else
+ {
+ SubtractObj(obj);
+ }
+
+ domain = tempdomain;
+ }
+}
+
+int Hugo::ValidObj(int obj) {
+ int attr, nattr = 0;
+ unsigned int addr;
+
+ defseg = gameseg;
+
+ if (!Available(obj, 0) && !speaking &&
+ (Peek(grammaraddr)!=OPEN_BRACKET_T ||
+ Peek(grammaraddr+1)!=ROUTINE_T))
+ {
+ if (Peek(grammaraddr)==ANYTHING_T)
+ ParseError(10, obj); /* "...haven't seen..." */
+ else
+ ParseError(11, obj); /* "...don't see any..." */
+ return 0;
+ }
+
+ switch (Peek(grammaraddr))
+ {
+ case OPEN_BRACKET_T:
+ {
+ if (Peek(grammaraddr+1)==ROUTINE_T)
+ {
+ addr = PeekWord(grammaraddr+2);
+ ret = 0;
+ passlocal[0] = obj;
+ PassLocals(1);
+
+ SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+#if defined (DEBUGGER)
+ DebugRunRoutine((long)addr*address_scale);
+#else
+ RunRoutine((long)addr*address_scale);
+#endif
+ retflag = 0;
+
+ /* If the routine doesn't return true,
+ the object doesn't qualify.
+ */
+ if (!ret)
+ return (0);
+ }
+ else if (Peek(grammaraddr+1)==OBJECTNUM_T)
+ {
+ if (obj != (int)PeekWord(grammaraddr+2))
+ {
+ strcpy(parseerr, "");
+ if (GetProp(obj, article, 1, 0))
+ strcpy(parseerr, "the ");
+ strcat(parseerr, Name(obj));
+
+ /* "...can't do that with..." */
+ ParseError(12, obj);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ case ATTR_T:
+ case NOT_T:
+ {
+ if (Peek(grammaraddr)==NOT_T) nattr = 1;
+ attr = Peek(grammaraddr + 1 + nattr);
+
+ /* If the attribute match is not made,
+ the object doesn't qualify.
+ */
+ if (!TestAttribute(obj, attr, nattr))
+ {
+ strcpy(parseerr, "");
+ if (GetProp(obj, article, 1, 0))
+ strcpy(parseerr, "the ");
+ strcat(parseerr, Name(obj));
+
+ /* "...can't do that with..." */
+ ParseError(12, obj);
+ return 0;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index 60d27aebcf..f7d39a7068 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -53,7 +53,7 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
words(0), parsed_number(0), remaining(0), xverb(0), starts_with_verb(0),
grammaraddr(0), obj_parselist(nullptr), domain(0), odomain(0), objcount(0),
parse_allflag(false), pobjcount(0), pobj(0), obj_match_state(0), object_is_number(0),
- objgrammar(0), objstart(0), objfinish(0), addflag(0), speaking(0), oopscount(0),
+ objgrammar(0), objstart(0), objfinish(0), addflag(false), speaking(0), oopscount(0),
parse_called_twice(0), reparse_everything(0), full_buffer(false), recursive_call(false),
parse_location(0),
// herun
@@ -86,7 +86,7 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
Common::fill(&buffer[0], &buffer[MAXBUFFER + MAXWORDS], '\0');
Common::fill(&errbuf[0], &errbuf[MAXBUFFER + 1], 0);
Common::fill(&line[0], &line[1025], 0);
- Common::fill(&word[0], &word[MAXWORDS + 1], (const char *)nullptr);
+ Common::fill(&word[0], &word[MAXWORDS + 1], (char *)nullptr);
Common::fill(&wd[0], &wd[MAXWORDS + 1], 0);
Common::fill(&parseerr[0], &parseerr[MAXBUFFER + 1], '\0');
Common::fill(&parsestr[0], &parsestr[MAXBUFFER + 1], '\0');
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index 76983adc4f..aa4deee21a 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -144,7 +144,7 @@ private:
char line[1025]; ///< line buffer
int words; ///< parsed word count
- const char *word[MAXWORDS + 1]; ///< breakdown into words
+ char *word[MAXWORDS + 1]; ///< breakdown into words
unsigned int wd[MAXWORDS + 1]; ///< " " dict. entries
unsigned int parsed_number; ///< needed for numbers in input
@@ -168,7 +168,7 @@ private:
unsigned int objgrammar; ///< for 2nd pass
int objstart; ///< " " "
int objfinish; ///< " " "
- char addflag; ///< true if adding to objlist[]
+ bool addflag; ///< true if adding to objlist[]
int speaking; ///< if command is addressed to obj.
char oops[MAXBUFFER + 1]; ///< illegal word
@@ -496,7 +496,7 @@ private:
/**
* From the dictionary table.
*/
- const char *GetWord(unsigned int w);
+ char *GetWord(unsigned int w);
void HandleTailRecursion(long addr);
@@ -604,38 +604,6 @@ private:
* \defgroup heobject - Object/property/attribute management functions
* @{
*/
-
-#if defined (DEBUGGER)
- int CheckinRange(uint v1, uint v2, const char *v3) {
- // TODO: Where the heck is this actualy implemented in Gargoyle
- return 1;
- }
-
- /**
- * Shorthand since many of these object functions may call CheckinRange() if the debugger
- * is running and runtime_warnings is set.
- */
- int CheckObjectRange(int obj);
-
- void DebugRunRoutine(long addr) {}
-
- void RuntimeWarning(const char *msg) {}
-
- void DebugMessageBox(const char *title, const char *msg) {}
-
- bool IsBreakpoint(long loc) const { return false; }
-
- const char *RoutineName(long loc) { return "Routine"; }
-
- void AddStringtoCodeWindow(const char *str) {}
-
- void SwitchtoDebugger() {}
-
- void DebuggerFatal(DEBUGGER_ERROR err) { error("Debugger error"); }
-
- void RecoverLastGood() {}
-#endif
-
int Child(int obj);
int Children(int obj);
@@ -690,6 +658,170 @@ private:
/**@}*/
/**
+ * \defgroup heparse
+ * @{
+ */
+
+ void AddAllObjects(int loc);
+
+ /**
+ * Adds the object <obj> to objlist[], making all related adjustments.
+ */
+ void AddObj(int obj);
+
+ /**
+ * Adds <obj> as a contender to the possible object list, noting that it was referred
+ * to as either a noun or an adjective.
+ */
+ void AddPossibleObject(int obj, char type, unsigned int w);
+
+ /**
+ * Move the address in the grammar table past the current token.
+ */
+ void AdvanceGrammar();
+
+ /**
+ * For when it's only necessary to know if word[wn] is an object word for any object,
+ * not a particular object. Returns 1 for an object word or -1 for a non-object word.
+ */
+ int AnyObjWord(int wn);
+
+ /**
+ * The non_grammar argument is true when called from a non-grammar function such as RunEvents().
+ */
+ int Available(int obj, char non_grammar);
+
+ void CallLibraryParse();
+
+ /**
+ * Takes into account the preset domain for checking an object's presence;
+ * <domain> is 0, -1, or an object number..
+ */
+ int DomainObj(int obj);
+
+ /**
+ * Returns the dictionary address of <a>.
+ */
+ unsigned int FindWord(char *a);
+
+ /**
+ * Checks to see if <obj> is in objlist[].
+ */
+ int InList(int obj);
+
+ /**
+ * Deletes word[a].
+ */
+ void KillWord(int a);
+
+ /**
+ * Here, briefly, is how MatchCommand() works:
+ *
+ * 1. Match the verb.
+ *
+ * 2. If no match, check to see if the line begins with an object (character)
+ * and try to match it.
+ *
+ * 3. If found, try to match a syntax for that verb, including objects, dictionary words,
+ * numbers, attributes, and routines. If any objects are specified, skip over them for now,
+ * marking the start and finish. This is done mostly in MatchWord().
+ *
+ * 4. Match the xobject, if there is one--via MatchObject().
+ *
+ * 5. If all is well, return to match the objects that were previously skipped over,
+ * loading them into objlist[]. Once again, this is done by MatchObject().
+ *
+ * (The reason the objects are initially skipped is because it may be necessary to know
+ * where to look for them--this may require knowing what the xobject is, if the syntax
+ * is something like:
+ *
+ * "get" <object> "from" <xobject>)
+ *
+ * The variable <obj_match_state> is the indicator of what stage object-matching is at:
+ *
+ * obj_match_state = 0 - haven't matched anything yet
+ *
+ * obj_match_state = 1 - xobject has been matched
+ *
+ * obj_match_state = 2 - matching object(s), loading objlist[]
+ *
+ * obj_match_state = 5 - matching first word/name, i.e., "Bob, <do something>"
+ */
+ int MatchCommand();
+
+ /**
+ * The argument is the word number we're starting matching on.
+ *
+ * NOTE: recusive_call is set to 0 if this is the first call. MatchObject() sets it to 1
+ * when calling itself when asking for clarification as to which object is meant.
+ *
+ * Return true on a recursive call to allow parsing to continue.
+ */
+ bool MatchObject(int *wordnum);
+
+ int MatchWord(int *wordnum);
+
+ /**
+ * Returns true if the specified object has the specified word as an adjective or noun
+ * (as specified by type).
+ */
+ int ObjWordType(int obj, unsigned int w, int type);
+
+ /**
+ * Returns <adjective> if the word at dictionary address <w> is an adjective of <obj>,
+ * or <noun> if it is a noun.
+ */
+ int ObjWord(int obj, unsigned int w);
+
+ /**
+ * Turns word[] into dictionary addresses stored in wd[]. Takes care of fingering illegal
+ * (unknown) words and doing alterations such as compounds, removals, and synonyms.
+ */
+ int Parse();
+
+ void ParseError(int e, int a);
+
+ /**
+ * Deletes wd[a].
+ */
+ void RemoveWord(int a);
+
+ /**
+ * Call FindObject(0, 0) to reset library's disambiguation mechanism.
+ */
+ void ResetFindObject();
+
+ /**
+ * Splits <buffer> into the word[] array. Also does nifty things such as turning time
+ * values such as hh:mm into a single number (representing minutes from midnight).
+ */
+ void SeparateWords();
+
+ /**
+ * Removes object <obj> from objlist[], making all related adjustments.
+ */
+ void SubtractObj(int obj);
+
+ /**
+ * Removes <obj> as a possible contender for object disambiguation.
+ */
+ void SubtractPossibleObject(int obj);
+
+ /**
+ * Called by MatchObject() to see if <obj> is available, and add it to or subtract
+ * it from objlist[] accordingly.
+ */
+ void TryObj(int obj);
+
+ /**
+ * Checks first of all to see if an object is available, then checks if it meets
+ * all the qualifications demanded by the grammar syntax.
+ */
+ int ValidObj(int obj);
+
+ /**@}*/
+
+ /**
* \defgroup Miscellaneous
* @{
*/
@@ -735,6 +867,37 @@ private:
void *hugo_blockalloc(size_t num) { return malloc(num); }
void hugo_blockfree(void *block) { free(block); }
+
+#if defined (DEBUGGER)
+ int CheckinRange(uint v1, uint v2, const char *v3) {
+ // TODO: Where the heck is this actualy implemented in Gargoyle
+ return 1;
+ }
+
+ /**
+ * Shorthand since many of these object functions may call CheckinRange() if the debugger
+ * is running and runtime_warnings is set.
+ */
+ int CheckObjectRange(int obj);
+
+ void DebugRunRoutine(long addr) {}
+
+ void RuntimeWarning(const char *msg) {}
+
+ void DebugMessageBox(const char *title, const char *msg) {}
+
+ bool IsBreakpoint(long loc) const { return false; }
+
+ const char *RoutineName(long loc) { return "Routine"; }
+
+ void AddStringtoCodeWindow(const char *str) {}
+
+ void SwitchtoDebugger() {}
+
+ void DebuggerFatal(DEBUGGER_ERROR err) { error("Debugger error"); }
+
+ void RecoverLastGood() {}
+#endif
public:
/**
* Constructor
diff --git a/engines/glk/hugo/hugo_defines.h b/engines/glk/hugo/hugo_defines.h
index 5609601ca8..5d69c847c2 100644
--- a/engines/glk/hugo/hugo_defines.h
+++ b/engines/glk/hugo/hugo_defines.h
@@ -41,8 +41,10 @@ namespace Hugo {
#define MAX_DEBUG_LINE 256
#define MAX_OBJECT 999
#define MAX_PROPERTY 999
+#define MAX_MOBJ 16 /* maximum number of matchable object words */
#define MAXBUFFER 255
#define MAXUNDO 1024
+
#define CHARWIDTH 1
#define STAT_UNAVAILABLE (-1)
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 62237b6806..00f5e35d56 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -76,6 +76,7 @@ MODULE_OBJS := \
hugo/heglk.o \
hugo/hemisc.o \
hugo/heobject.o \
+ hugo/heparse.o \
hugo/htokens.o \
hugo/hugo.o \
hugo/stringfn.o \