aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/hugo
diff options
context:
space:
mode:
authorPaul Gilbert2019-05-11 13:19:52 +1000
committerPaul Gilbert2019-05-11 13:19:52 +1000
commitabb7b22b2e4a9bd24058e0221c85680aed71d03f (patch)
tree188b5067bfda32ced4088ec370628524320827ed /engines/glk/hugo
parent84c47349a9f06057640ddc35c015fdd97b30d839 (diff)
downloadscummvm-rg350-abb7b22b2e4a9bd24058e0221c85680aed71d03f.tar.gz
scummvm-rg350-abb7b22b2e4a9bd24058e0221c85680aed71d03f.tar.bz2
scummvm-rg350-abb7b22b2e4a9bd24058e0221c85680aed71d03f.zip
GLK: HUGO: Add heexpr
Diffstat (limited to 'engines/glk/hugo')
-rw-r--r--engines/glk/hugo/heexpr.cpp1240
-rw-r--r--engines/glk/hugo/hemisc.cpp2
-rw-r--r--engines/glk/hugo/hugo.cpp6
-rw-r--r--engines/glk/hugo/hugo.h74
4 files changed, 1317 insertions, 5 deletions
diff --git a/engines/glk/hugo/heexpr.cpp b/engines/glk/hugo/heexpr.cpp
new file mode 100644
index 0000000000..b0865e4e24
--- /dev/null
+++ b/engines/glk/hugo/heexpr.cpp
@@ -0,0 +1,1240 @@
+/* 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 {
+
+int Hugo::EvalExpr(int p) {
+ int n1, n2;
+ int oper;
+ short result = 0; /* must be 16 bits */
+
+ /* for precedence stacking/unstacking */
+ int next_prec, this_prec, temp_lp;
+
+ if (!evalcount) return 0; /* no expression */
+
+ do
+ {
+ if (eval[p]==1)
+ {
+ if (eval[p+1]==OPEN_BRACKET_T ||
+ eval[p+1]==OPEN_SQUARE_T)
+ {
+ eval[p] = 0;
+ eval[p+1] = EvalExpr(p+2);
+ TrimExpr(p+2);
+ }
+ else if (eval[p+1]==MINUS_T)
+ {
+ TrimExpr(p);
+ eval[p+1] = -eval[p+1];
+ }
+ }
+
+ if (evalcount<=p+2)
+ {
+ result = eval[p+1];
+ TrimExpr(p);
+ eval[p] = 0;
+ eval[p+1] = result;
+ goto ReturnResult;
+ }
+
+ n1 = eval[p+1];
+ oper = eval[p+3];
+
+ /* At this point, <n1> holds the first value, and <oper> holds
+ the token number of the operator.
+ */
+ if (eval[p+4]==1 && (eval[p+5]==OPEN_BRACKET_T
+ || eval[p+5]==OPEN_SQUARE_T))
+ {
+ eval[p+4] = 0;
+ eval[p+5] = EvalExpr(p+6);
+ TrimExpr(p+6);
+ }
+
+ n2 = eval[p+5];
+
+ if (evalcount > p+7)
+ {
+ if (eval[p+3]==CLOSE_BRACKET_T && eval[p+2]==1)
+ {
+ TrimExpr(p+2);
+ return eval[p+1];
+ }
+
+ /* eval[p+7] holds the next operator, i.e., the "*"
+ in: "x + y * z"
+
+ This way, we can check if the upcoming operator
+ takes precedence over the current one.
+ */
+ if ((next_prec = Precedence(eval[p+7]))
+ < (this_prec = Precedence(oper)))
+ {
+ if (next_prec >= last_precedence)
+ {
+#if defined (DEBUG_PRECEDENCE)
+sprintf(line, "Not preferring %s to %s because of previous level %d", token[eval[p+7]], token[oper], last_precedence);
+Printout(line);
+#endif
+ goto ReturnResult;
+ }
+
+#if defined (DEBUG_PRECEDENCE)
+sprintf(line, "Preferring %s to %s", token[eval[p+7]], token[oper]);
+Printout(line);
+#endif
+
+ temp_lp = last_precedence;
+ last_precedence = this_prec;
+ n2 = EvalExpr(p+4);
+ last_precedence = temp_lp;
+ }
+ }
+ else if (Precedence(oper)>=last_precedence)
+ {
+ goto ReturnResult;
+ }
+
+#if defined (DEBUG_PRECEDENCE)
+sprintf(line, "Solving %d %s %d", n1, token[oper], n2);
+Printout(line);
+#endif
+
+ switch (oper)
+ {
+ case DECIMAL_T:
+ {
+ result = GetProp(n1, n2, 1, 0);
+ break;
+ }
+
+ case EQUALS_T:
+ {
+ result = (n1==n2);
+ break;
+ }
+ case MINUS_T:
+ {
+ result = n1 - n2;
+ break;
+ }
+ case PLUS_T:
+ {
+ result = n1 + n2;
+ break;
+ }
+ case ASTERISK_T:
+ {
+ result = n1 * n2;
+ break;
+ }
+ case FORWARD_SLASH_T:
+ {
+ if (n2==0)
+#if defined (DEBUGGER)
+ {
+ RuntimeWarning("Division by zero: invalid result");
+ result = 0;
+ }
+#else
+ FatalError(DIVIDE_E);
+#endif
+ result = n1 / n2;
+ break;
+ }
+ case PIPE_T:
+ {
+ result = n1 | n2;
+ break;
+ }
+ case GREATER_EQUAL_T:
+ {
+ result = (n1>=n2);
+ break;
+ }
+ case LESS_EQUAL_T:
+ {
+ result = (n1<=n2);
+ break;
+ }
+ case NOT_EQUAL_T:
+ {
+ result = (n1!=n2);
+ break;
+ }
+ case AMPERSAND_T:
+ {
+ result = n1 & n2;
+ break;
+ }
+ case GREATER_T:
+ {
+ result = (n1 > n2);
+ break;
+ }
+ case LESS_T:
+ {
+ result = (n1 < n2);
+ break;
+ }
+ case AND_T:
+ {
+ result = (n1 && n2);
+ break;
+ }
+ case OR_T:
+ {
+ result = (n1 || n2);
+ break;
+ }
+
+ default:
+ {
+ result = n1;
+ }
+ }
+
+#if defined (DEBUGGER)
+ if ((debug_eval) && debug_eval_error) return 0;
+#endif
+
+ TrimExpr(p+4); /* second value */
+ TrimExpr(p+2); /* operator */
+
+ eval[p] = 0;
+ eval[p+1] = result;
+
+
+ /* Keep looping while there are expression elements, or until there
+ is a ")", "]", or end of line
+ */
+ } while ((evalcount>p+2) && !(eval[p+2]==1 &&
+ (eval[p+3]==CLOSE_BRACKET_T || eval[p+3]==CLOSE_SQUARE_T ||
+ eval[p+3]==255)));
+
+ result = eval[p+1];
+
+ TrimExpr(p); /* first value */
+
+ReturnResult:
+
+#if defined (DEBUG_EXPR_EVAL)
+ if (p==0 && exprt)
+ {
+ sprintf(line, " = %d", result);
+ AP(line);
+ }
+#endif
+ return result;
+}
+
+int Hugo::GetVal() {
+ char a = 0;
+ char tempinexpr, tempgetaddress, tempinobj;
+ int i, j;
+ int tempret;
+
+ unsigned short routineaddr, arrayaddr; /* must be 16 bits */
+ short val = 0;
+
+ char inctype = 0;
+ int preincdec; /* pre-increment/decrement */
+
+ defseg = gameseg;
+
+ tempret = ret;
+ tempinexpr = inexpr;
+ inexpr = 0;
+
+ preincdec = incdec;
+ incdec = 0;
+
+ switch (MEM(codeptr))
+ {
+ case AMPERSAND_T: /* an address */
+ {codeptr++;
+ getaddress = true;
+ val = GetValue();
+ getaddress = false;
+ break;}
+
+ case ROUTINE_T:
+ case CALL_T:
+ {
+ if (MEM(codeptr)==ROUTINE_T)
+ {
+ if (tail_recursion==0 && MEM(codeptr-1)==RETURN_T)
+ {
+ /* We may be able to tail-recurse this return
+ statement if it's simply 'return Routine(...)'
+ */
+ tail_recursion = TAIL_RECURSION_ROUTINE;
+ }
+
+ routineaddr = PeekWord(++codeptr);
+ codeptr += 2;
+
+ if (getaddress)
+ {val = routineaddr;
+ getaddress = false;
+ break;}
+ }
+ else
+ {
+ codeptr++;
+ routineaddr = GetValue();
+ }
+
+#if defined (DEBUGGER)
+ if (debug_eval)
+ {
+ debug_eval_error = true;
+ val = 0;
+ break;
+ }
+#endif
+ val = CallRoutine(routineaddr);
+
+ break;
+ }
+
+ case OPEN_BRACKET_T:
+ {
+ codeptr++;
+ inexpr = 1;
+ tempgetaddress = getaddress;
+ getaddress = false;
+ SetupExpr();
+ inexpr = 0;
+ val = EvalExpr(0);
+ getaddress = tempgetaddress;
+ break;
+ }
+
+ case MINUS_T:
+ {
+ codeptr++;
+ j = inexpr; /* don't reuse tempinexpr */
+ inexpr = 1;
+ val = -GetValue();
+ inexpr = (char)j;
+ break;
+ }
+
+ case VALUE_T: /* integer 0 - 65535 */
+ case OBJECTNUM_T:
+ case DICTENTRY_T:
+ {
+ val = PeekWord(++codeptr);
+ codeptr += 2;
+ break;
+ }
+
+ case ATTR_T:
+ case PROP_T:
+ {
+ val = MEM(++codeptr);
+ codeptr++;
+ break;
+ }
+
+ case VAR_T: /* variable */
+ {
+ val = var[(i=MEM(++codeptr))];
+
+ if (game_version >= 22)
+ {
+ /* Pre-v2.4 included linelength and pagelength as
+ global variables after objectcount
+ */
+ if (i <= ((game_version>=24)?objectcount:objectcount+2))
+ {
+ if (i==wordcount) val = words;
+ else if (i==objectcount) val = objects;
+
+ /* i.e., pre-v2.4 only */
+ else if (i==objectcount+1)
+ {
+#if defined (ACTUAL_LINELENGTH)
+ val = ACTUAL_LINELENGTH();
+#else
+ val = SCREENWIDTH/charwidth;
+#endif
+ }
+ else if (i==objectcount+2)
+ val = SCREENHEIGHT/lineheight;
+ }
+ }
+ codeptr++;
+
+ if (!inobj) inctype = IsIncrement(codeptr);
+
+ /* don't operate on, e.g., ++variable.property as
+ (++variable).property
+ */
+ if ((incdec || preincdec) && MEM(codeptr)!=DECIMAL_T)
+ {
+ if (i < MAXGLOBALS) SaveUndo(VAR_T, i, val, 0, 0);
+
+ if (inctype) val = Increment(val, inctype);
+
+ /* still a post-increment hanging around */
+ var[i] = (val+=preincdec) + incdec;
+
+ incdec = preincdec = 0;
+ }
+
+ break;
+ }
+
+ case TRUE_T:
+ val = 1;
+ codeptr++;
+ break;
+
+ case FALSE_T:
+ val = 0;
+ codeptr++;
+ break;
+
+ case TILDE_T:
+ codeptr++;
+ val = ~GetValue();
+ break;
+
+ case NOT_T:
+ codeptr++;
+ val = !GetValue();
+ break;
+
+ case ARRAYDATA_T:
+ case ARRAY_T:
+ {
+ unsigned int element;
+
+ if (MEM(codeptr)==ARRAY_T)
+ {
+ codeptr++;
+ arrayaddr = GetValue();
+ }
+ else
+ {
+ arrayaddr = PeekWord(++codeptr);
+ codeptr += 2;
+ }
+
+ if (MEM(codeptr)!=OPEN_SQUARE_T)
+ {val = arrayaddr;
+ break;}
+
+ if (game_version>=22)
+ {
+ /* convert to word value */
+ arrayaddr*=2;
+
+ if (game_version>=23)
+ /* space for array length */
+ a = 2;
+ }
+
+ /* check if this is array[] (i.e., array length) */
+ if (MEM(++codeptr)==CLOSE_SQUARE_T)
+ {
+ defseg = arraytable;
+ val = PeekWord(arrayaddr);
+ codeptr++;
+ break;
+ }
+
+ tempinobj = inobj;
+ inobj = 0;
+ j = GetValue();
+ inobj = tempinobj;
+
+ /* The array element we're after: */
+ element = arrayaddr+a + j*2;
+
+ defseg = arraytable;
+#if defined (DEBUGGER)
+ CheckinRange(element, debug_workspace, "array data");
+#endif
+ /* Check to make sure we've got a sane element number */
+ if ((element>0) && (element < (unsigned int)(dicttable-arraytable)*16))
+ val = PeekWord(element);
+ else
+ val = 0;
+ codeptr++;
+
+ if (!inobj) inctype = IsIncrement(codeptr);
+
+ /* Don't operate on the array on:
+
+ ++a[n].property
+ */
+ if ((incdec || preincdec) && MEM(codeptr)!=DECIMAL_T)
+ {
+ /* Same sanity check for element number */
+ if ((element>0) && (element < (unsigned)(dicttable-arraytable)*16))
+ {
+ if (inctype) val = Increment(val, inctype);
+
+ /* still a post-increment hanging around */
+ SaveUndo(ARRAYDATA_T, arrayaddr+a, j, val, 0);
+
+ PokeWord(element, (val+=preincdec) + incdec);
+
+ incdec = preincdec = 0;
+ }
+ }
+
+ break;
+ }
+
+ case RANDOM_T:
+ {
+ codeptr += 2; /* skip the "(" */
+ val = GetValue();
+ if (val!=0)
+#if !defined (RANDOM)
+ val = (hugo_rand() % val)+1;
+#else
+ val = (RANDOM() % val)+1;
+#endif
+ if (MEM(codeptr)==2) codeptr++;
+ break;
+ }
+
+ case WORD_T:
+ {
+ codeptr += 2; /* skip the "[" */
+
+ if (MEM(codeptr)==CLOSE_SQUARE_T) /* words[] */
+ {
+ val = words;
+ break;
+ }
+
+ val = wd[GetValue()];
+ if (MEM(codeptr)==CLOSE_SQUARE_T) codeptr++;
+ break;
+ }
+
+ case CHILDREN_T:
+ {
+ codeptr += 2; /* skip the "(" */
+ val = GetValue();
+ if (MEM(codeptr)==CLOSE_BRACKET_T) codeptr++;
+ val = Children(val);
+ break;
+ }
+
+ case PARENT_T:
+ case SIBLING_T:
+ case CHILD_T:
+ case YOUNGEST_T:
+ case ELDEST_T:
+ case YOUNGER_T:
+ case ELDER_T:
+ {
+ i = MEM(codeptr);
+ codeptr += 2; /* skip the "(" */
+ val = GetValue();
+ if (MEM(codeptr)==CLOSE_BRACKET_T) codeptr++;
+
+ switch (i)
+ {
+ case PARENT_T:
+ val = Parent(val);
+ break;
+
+ case SIBLING_T:
+ case YOUNGER_T:
+ val = Sibling(val);
+ break;
+
+ case CHILD_T:
+ case ELDEST_T:
+ val = Child(val);
+ break;
+
+ case YOUNGEST_T:
+ val = Youngest(val);
+ break;
+
+ case ELDER_T:
+ val = Elder(val);
+ break;
+ }
+ break;
+ }
+
+ case SAVE_T:
+ val = RunSave();
+ codeptr++;
+ break;
+
+ case RESTORE_T:
+ val = RunRestore();
+ codeptr++;
+ break;
+
+ case SCRIPTON_T:
+ case SCRIPTOFF_T:
+ val = RunScriptSet();
+ codeptr++;
+ break;
+
+ case RESTART_T:
+ val = RunRestart();
+ codeptr++;
+ break;
+
+ case STRING_T:
+ val = RunString();
+ break;
+
+ case UNDO_T:
+ val = Undo();
+ codeptr++;
+ break;
+
+ case DICT_T:
+ val = Dict();
+ if (MEM(codeptr)==CLOSE_BRACKET_T) codeptr++;
+ break;
+
+ case RECORDON_T:
+ case RECORDOFF_T:
+ case PLAYBACK_T:
+ val = RecordCommands();
+ codeptr++;
+ break;
+
+ case READVAL_T:
+ {
+ val = 0;
+ if (ioblock)
+ {
+#ifdef TODO
+ int low, high;
+
+ if ((ioblock==1)
+ || (low = hugo_fgetc(io))==EOF
+ || (high = hugo_fgetc(io))==EOF)
+ {
+ ioerror = true;
+ retflag = true;
+ }
+ else val = low + high*256;
+#else
+ error("TODO: file io");
+#endif
+ }
+ codeptr++;
+ break;
+ }
+
+ case PARSE_T:
+ {
+ val = (short)PARSE_STRING_VAL;
+ codeptr++;
+ break;
+ }
+
+ case SERIAL_T:
+ {
+ val = (short)SERIAL_STRING_VAL;
+ codeptr++;
+ break;
+ }
+
+ case SYSTEM_T:
+ {
+ val = RunSystem();
+ codeptr++;
+ break;
+ }
+
+ default:
+ {
+#if defined (DEBUGGER)
+ if (debug_eval)
+ debug_eval_error = true;
+ else
+#endif
+
+ FatalError(EXPECT_VAL_E);
+
+#if defined (DEBUGGER)
+ runtime_error = true;
+ codeptr++;
+ return 0;
+#endif
+ }
+ }
+ defseg = gameseg;
+ ret = tempret;
+ inexpr = tempinexpr;
+
+ incdec = preincdec;
+
+ return val;
+}
+
+int Hugo::GetValue() {
+ char noself = 0;
+ int p, n;
+ char inctype; int preincdec;
+ int nattr = 0, attr;
+ unsigned int pa, val;
+ long tempptr;
+ short g; /* must be 16 bits */
+ int potential_tail_recursion = 0;
+
+ /* Check to see if this may be a valid tail-recursion */
+ if (tail_recursion==0 && MEM(codeptr-1)==RETURN_T)
+ {
+ /* We may be able to tail-recurse this return statement if
+ it's simply 'return object.property[.property...]'
+ */
+ potential_tail_recursion = TAIL_RECURSION_PROPERTY;
+ }
+
+ IsIncrement(codeptr); /* check for ++, -- */
+
+ tempptr = codeptr;
+ g = GetVal();
+
+ preincdec = incdec;
+ incdec = 0;
+
+ if (inobj==0)
+ {
+ switch (MEM(codeptr))
+ {
+ case DECIMAL_T: /* object.property */
+ {
+DetermineProperty:
+ if (MEM(++codeptr)==DECIMAL_T) /* object..property */
+ {
+ noself = true;
+ codeptr++;
+ }
+
+ if (MEM(codeptr)==POUND_T) /* object.#property */
+ {
+ codeptr++;
+ inobj = true;
+ p = GetValue();
+ inobj = false;
+ pa = PropAddr(g, p, 0);
+ if (pa)
+ {
+ defseg = proptable;
+ g = Peek(pa + 1);
+ if (g==PROP_ROUTINE) g = 1;
+ defseg = gameseg;
+ }
+ else
+ g = 0;
+ }
+ else
+ {
+ inobj = true;
+ p = GetValue();
+ inobj = false;
+
+ if (MEM(codeptr) != POUND_T)
+ n = 1;
+
+ else /* object.property #x */
+ {
+ codeptr++;
+
+ /* Not GetValue(), since that might
+ botch "obj.property #n is attr"
+ */
+ n = GetVal();
+ }
+
+ /* We checked this at the start of the function, but
+ GetValue() for the property would've cleared it
+ */
+ tail_recursion = potential_tail_recursion;
+
+ val = GetProp(g, p, n, noself);
+
+ inctype = IsIncrement(codeptr);
+
+ /* Increment/decrement an object.property, although
+ only if this is the last property in, e.g.,
+ object.property.property...
+ */
+ if ((incdec || preincdec) && MEM(codeptr)!=DECIMAL_T)
+ {
+ SaveUndo(PROP_T, g, p, n, val);
+
+ if (inctype) val = Increment(val, inctype);
+
+ /* Still a post-increment hanging around */
+ pa = PropAddr(g, p, 0);
+ defseg = proptable;
+
+ /* Only change it if not a routine */
+ if (Peek(pa+1)!=PROP_ROUTINE)
+ PokeWord(pa+n*2, (val+=preincdec)+incdec);
+
+ defseg = gameseg;
+
+ incdec = preincdec = 0;
+ }
+ g = val;
+ }
+ if (MEM(codeptr)==IS_T) goto CheckAttribute;
+
+ break;
+ }
+
+ case IS_T:
+ {
+CheckAttribute:
+ if (!inobj)
+ {
+ codeptr++;
+ if (MEM(codeptr)==NOT_T)
+ {
+ nattr = 1;
+ codeptr++;
+ }
+ attr = GetValue();
+#if defined (DEBUGGER)
+ CheckinRange((unsigned)attr, (unsigned)attributes, "attribute");
+#endif
+ g = TestAttribute(g, attr, nattr);
+
+ break;
+ }
+ }
+ }
+
+ switch (MEM(codeptr))
+ {
+ /* This comes here (again) in order to process
+ object.property1.property2...
+ */
+ case DECIMAL_T: goto DetermineProperty;
+
+ case NOT_T:
+ if (!inobj)
+ {nattr = 1;
+ codeptr++;}
+ case IN_T:
+ {
+ if (!inobj)
+ {
+ codeptr++;
+ p = GetValue(); /* testing parent */
+ g = (p==Parent(g));
+ if (nattr)
+ g = !g;
+ }
+ }
+ }
+ } /* end of "if (inobj==0)" */
+
+ n = MEM(codeptr);
+
+ /* See if we have an implicit expression that needs to be
+ taken as a single value, i.e., "n + 1" where we've just
+ read n
+ */
+ if (((n>=MINUS_T && n<=PIPE_T) || n==AMPERSAND_T) &&
+ ((!inexpr)) && !inobj)
+/*
+#if !defined (DEBUGGER)
+ ((!inexpr)) && !inobj)
+#else
+ ((!inexpr)) && !inobj && !debug_eval)
+#endif
+*/
+ {
+ inexpr = 2;
+ codeptr = tempptr;
+ SetupExpr();
+ g = EvalExpr(0);
+ inexpr = 0;
+ }
+
+ /* Not a tail-recursive 'return object.property' */
+ if (tail_recursion_addr==0)
+ tail_recursion = 0;
+
+ return g;
+}
+
+int Hugo::Increment(int a, char inctype) {
+ short v; /* must be 16 bits */
+
+ v = a;
+
+ switch (inctype)
+ {
+ case MINUS_T: {v -= incdec; break;}
+ case PLUS_T: {v += incdec; break;}
+ case ASTERISK_T: {v *= incdec; break;}
+ case AMPERSAND_T: {v &= incdec; break;}
+ case PIPE_T: {v |= incdec; break;}
+ case FORWARD_SLASH_T:
+ {
+#if defined (DEBUGGER)
+ if (incdec==0)
+ {
+ RuntimeWarning("Division by zero: invalid result");
+ v = 0;
+ }
+ else
+#endif
+ v /= incdec;
+ break;
+ }
+ }
+
+ if (inctype!=1) incdec = 0;
+
+ return v;
+}
+
+char Hugo::IsIncrement(long addr) {
+ unsigned char a, t = 0;
+
+ incdec = 0;
+
+ switch (a = MEM(addr))
+ {
+ case MINUS_T:
+ case PLUS_T:
+ case ASTERISK_T:
+ case FORWARD_SLASH_T:
+ case AMPERSAND_T:
+ case PIPE_T:
+ {
+ /* ++, -- */
+ if ((a==MINUS_T || a==PLUS_T) && MEM(addr+1)==a)
+ {
+ codeptr = addr + 2;
+ if (a==PLUS_T) incdec = 1;
+ else incdec = -1;
+ t = 1;
+ break;
+ }
+
+ /* +=, -=, etc. */
+ else if (MEM(addr+1)==EQUALS_T)
+ {
+ codeptr = addr + 2;
+ incdec = GetValue();
+ t = a;
+ }
+ }
+ }
+
+#if defined (DEBUGGER)
+ if (t && debug_eval)
+ {
+ debug_eval_error = true;
+ sprintf(debug_line, "'%s%s' illegal in watch/assignment", token[a], token[MEM(addr+1)]);
+ DebugMessageBox("Expression Error", debug_line);
+ t = 0;
+ }
+#endif
+ return t;
+}
+
+int Hugo::Precedence(int t) {
+ switch (t)
+ {
+ case DECIMAL_T:
+ return 1;
+
+ case ASTERISK_T:
+ case FORWARD_SLASH_T:
+ return 2;
+
+ case MINUS_T:
+ case PLUS_T:
+ return 3;
+
+ case PIPE_T:
+ case TILDE_T:
+ case AMPERSAND_T:
+ return 4;
+
+ case EQUALS_T:
+ case GREATER_EQUAL_T:
+ case LESS_EQUAL_T:
+ case NOT_EQUAL_T:
+ case GREATER_T:
+ case LESS_T:
+ return 5;
+
+ default:
+ return 6;
+ }
+}
+
+#if defined (DEBUG_EXPR_EVAL)
+/* PRINTEXPR
+
+Prints the current expression during expression tracing.
+*/
+void PrintExpr(void)
+{
+ char e[261];
+ int i, bracket = 0;
+
+ if (!evalcount) return;
+
+ strcpy(e, "( ");
+ for (i=0; i<=evalcount; i+=2)
+ {
+ switch (eval[i])
+ {
+ case 0:
+ {
+ sprintf(line, "%d ", eval[i + 1]);
+ strcat(e, line);
+ break;
+ }
+ case 1:
+ {
+ if (eval[i+1]==OPEN_BRACKET_T) bracket++;
+ if (eval[i+1]==CLOSE_BRACKET_T)
+ {bracket--;
+ if (bracket<0) goto ExitPrintExpr;}
+
+ if (token[eval[i+1]][0]=='~')
+ strcat(e, "\\");
+ if (eval[i+1] != 255)
+ {sprintf(line, "%s ", token[eval[i+1]]);
+ strcat(e, line);}
+ break;
+ }
+ }
+ }
+
+ExitPrintExpr:
+ strcat(e, ")\\;");
+
+ AP(e);
+}
+#endif
+
+void Hugo::SetupExpr() {
+ char justgotvalue = 1;
+ int j, t, bracket = 0;
+ int tempret;
+ int tempeval[MAX_EVAL_ELEMENTS];
+ int tempevalcount;
+
+ last_precedence = 10;
+
+ tempret = ret;
+ tempevalcount = 0;
+
+ inobj = false;
+ if (!inexpr) inexpr = 1;
+
+ do
+ {
+ justgotvalue++;
+
+ switch (t = MEM(codeptr))
+ {
+ /* Various indications that we've hit the
+ end of the expression:
+ */
+ case EOL_T:
+ arrexpr = false;
+ case COMMA_T:
+ multiprop = false;
+ case SEMICOLON_T:
+ case CLOSE_SQUARE_T:
+ case JUMP_T:
+ {
+ if (t==EOL_T || t==COMMA_T || t==JUMP_T)
+ codeptr++;
+LeaveSetupExpr:
+ for (j=0; j<tempevalcount; j++)
+ eval[j] = tempeval[j];
+ evalcount = tempevalcount;
+
+ eval[evalcount] = 1;
+ eval[evalcount + 1] = 255;
+
+#if defined (DEBUG_EXPR_EVAL)
+ if (exprt) PrintExpr();
+#endif
+
+ ret = tempret;
+ return;
+ }
+
+
+ /* Otherwise we have a value: */
+
+ case OPEN_BRACKET_T:
+
+ case MINUS_T:
+ case PLUS_T:
+ case TILDE_T:
+ case AMPERSAND_T:
+ case NOT_T:
+
+ case PARENT_T:
+ case SIBLING_T:
+ case CHILD_T:
+ case YOUNGEST_T:
+ case ELDEST_T:
+ case YOUNGER_T:
+ case ELDER_T:
+ case CHILDREN_T:
+ case RANDOM_T:
+ case SYSTEM_T:
+
+ case PROP_T:
+ case ATTR_T:
+ case VAR_T:
+ case DICTENTRY_T:
+ case ROUTINE_T:
+ case OBJECTNUM_T:
+ case VALUE_T:
+
+ case ARRAYDATA_T:
+ case ARRAY_T:
+ case WORD_T:
+
+ case CALL_T:
+
+ case SAVE_T:
+ case RESTORE_T:
+ case SCRIPTON_T:
+ case SCRIPTOFF_T:
+ case RESTART_T:
+ case UNDO_T:
+ case READVAL_T:
+
+ case STRING_T:
+ case DICT_T:
+
+ case PARSE_T:
+ case SERIAL_T:
+ {
+ if ((t==AMPERSAND_T || t==MINUS_T ||
+ t==PLUS_T) && justgotvalue==1)
+ goto SomeSymbolorToken;
+
+ tempeval[tempevalcount] = 0;
+ tempeval[tempevalcount + 1] = GetValue();
+
+#if defined (DEBUGGER)
+ if ((debug_eval) && debug_eval_error)
+ return;
+#endif
+
+ tempevalcount += 2;
+ if (tempevalcount > MAX_EVAL_ELEMENTS-2)
+ FatalError(OVERFLOW_E);
+
+ justgotvalue = 0;
+
+ break;
+ }
+
+ /* Logical constants */
+ case TRUE_T:
+ case FALSE_T:
+ {
+ tempeval[tempevalcount] = 0;
+ if (Peek(codeptr)==TRUE_T)
+ tempeval[tempevalcount + 1] = 1;
+ else
+ tempeval[tempevalcount + 1] = 0;
+
+ codeptr++;
+
+ tempevalcount += 2;
+ if (tempevalcount > MAX_EVAL_ELEMENTS-2)
+ FatalError(OVERFLOW_E);
+
+ break;
+ }
+
+ /* Some symbol or token */
+ default:
+ {
+SomeSymbolorToken:
+ tempeval[tempevalcount] = 1;
+ tempeval[tempevalcount + 1] = MEM(codeptr++);
+
+ tempevalcount += 2;
+ if (tempevalcount > MAX_EVAL_ELEMENTS-2)
+ FatalError(OVERFLOW_E);
+
+ switch (MEM(codeptr-1))
+ {
+ case OPEN_BRACKET_T:
+ {bracket++;
+ break;}
+ case CLOSE_BRACKET_T:
+ {bracket--;
+ justgotvalue = 0;
+ if (inexpr==2)
+ codeptr--;}
+ }
+ if (bracket < 0) goto LeaveSetupExpr;
+
+ break;
+ }
+ }
+ }
+ while (true); /* endless loop */
+}
+
+void Hugo::TrimExpr(int ptr) {
+ int i;
+
+ for (i=ptr; i<=evalcount; i+=2)
+ {
+ eval[i] = eval[i+2];
+ eval[i+1] = eval[i+3];
+ }
+ evalcount -= 2;
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/hemisc.cpp b/engines/glk/hugo/hemisc.cpp
index 696b7a2666..9e560570d4 100644
--- a/engines/glk/hugo/hemisc.cpp
+++ b/engines/glk/hugo/hemisc.cpp
@@ -978,7 +978,7 @@ char *Hugo::GetText(long textaddr) {
}
const char *Hugo::GetWord(unsigned int w) {
- static char *b;
+ static const char *b;
unsigned short a;
a = w;
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index dccf978882..60d27aebcf 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -68,7 +68,7 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
debugger_finish(false), debugger_run(false), debugger_interrupt(false),
debugger_skip(false), runtime_error(false), currentroutine(false),
complex_prop_breakpoint(false), trace_complex_prop_routine(false), routines(0),
- properties(0), current_locals(0), this_codeptr(0)
+ properties(0), current_locals(0), this_codeptr(0), debug_workspace(0), attributes(0)
#endif
{
// heexpr
@@ -103,8 +103,8 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
#ifdef DEBUGGER
debug_line[0] = '\0';
- Common::fill(&objectname[0], &objectname[MAX_OBJECT], nullptr);
- Common::fill(&propertyname[0], &propertyname[MAX_PROPERTY], nullptr);
+ Common::fill(&objectname[0], &objectname[MAX_OBJECT], (char *)nullptr);
+ Common::fill(&propertyname[0], &propertyname[MAX_PROPERTY], (char *)nullptr);
Common::fill(&codeline[0][0], &codeline[9][100], 0);
Common::fill(&localname[0][0], &localname[9][100], 0);
#endif
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index a1e2c29494..76983adc4f 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -237,9 +237,72 @@ private:
char localname[9][100];
int current_locals;
long this_codeptr;
+ int debug_workspace;
+ int attributes;
#endif
private:
/**
+ * \defgroup heexpr
+ * @{
+ */
+
+ /**
+ * The new-and-improved expression evaluator. Evaluates the current expression
+ * (or sub-expression therein) beginning at eval[p].
+ */
+ int EvalExpr(int p);
+
+ /**
+ * Called by GetValue(); does the actual dirty work of returning a value from a
+ * simple data type.
+ */
+ int GetVal();
+
+ /**
+ * Does any reckoning for more sophisticated constructions.
+ */
+ int GetValue();
+
+ /**
+ * Actually performs the increment given below by IsIncrement.
+ */
+ int Increment(int a, char inctype);
+
+ /**
+ * If an increment/decrement is next up (i.e. ++, --, or +=, *=, etc.),
+ * then sets incdec equal to the increment/decrement and repositions codeptr.
+ * Returns the token number of the operation, if any.
+ */
+ char IsIncrement(long addr);
+
+ /**
+ * Returns the precedence ranking of the operator represented by token[t].
+ * The lower the return value, the higher the rank in terms of processing order.
+ */
+ int Precedence(int t);
+
+ /**
+ * Reads the current expression from the current code position into eval[],
+ * using the following key:
+ *
+ * if eval[n] is 0, eval[n+1] is a value
+ * if eval[n] is 1, eval[n+1] is a token
+ *
+ * <inexpr> is used in various routines to keep track of whether or not we're currently
+ * reading an expression. If <inexpr> is 1, we're in an expression; if 2, we may have
+ * to step back one code position if encountering a closing parentheses.
+ */
+ void SetupExpr();
+
+ /**
+ * Cuts off straggling components of eval[] after an expression or sub-expression
+ * has been successfully evaluated.
+ */
+ void TrimExpr(int ptr);
+
+ /**@}*/
+
+ /**
* \defgroup heglk
* @{
*/
@@ -660,6 +723,10 @@ private:
return s->read(ptr, size * count);
}
+ uint hugo_rand() {
+ return _random.getRandomNumber(0xffffff);
+ }
+
/**@}*/
private:
/**
@@ -695,7 +762,6 @@ public:
virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
// TODO: Stubs to be Properly implemented
- int GetValue() { return 0; }
void PlayGame() {}
void hugo_closefiles() {}
void RunRoutine(long v) {}
@@ -704,6 +770,12 @@ public:
void hugo_stopmusic() {}
int hugo_hasgraphics() { return 0; }
int hugo_writetoscript(const char *s) { return 0; }
+ short RunSave() { return 0; }
+ short RunRestore() { return 0; }
+ short RunScriptSet() { return 0; }
+ short RunRestart() { return 0; }
+ short RunString() { return 0; }
+ short RunSystem() { return 0; }
};
} // End of namespace Hugo