1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "glk/hugo/hugo.h"
24 
25 namespace Glk {
26 namespace Hugo {
27 
EvalExpr(int p)28 int Hugo::EvalExpr(int p) {
29 	int n1, n2;
30 	int oper;
31 	short result = 0;		/* must be 16 bits */
32 
33 	/* for precedence stacking/unstacking */
34 	int next_prec, this_prec, temp_lp;
35 
36 	if (!evalcount) return 0;       /* no expression */
37 
38 	do
39 	{
40 		if (eval[p]==1)
41 		{
42 			if (eval[p+1]==OPEN_BRACKET_T ||
43 				eval[p+1]==OPEN_SQUARE_T)
44 			{
45 				eval[p] = 0;
46 				eval[p+1] = EvalExpr(p+2);
47 				TrimExpr(p+2);
48 			}
49 			else if (eval[p+1]==MINUS_T)
50 			{
51 				TrimExpr(p);
52 				eval[p+1] = -eval[p+1];
53 			}
54 		}
55 
56 		if (evalcount<=p+2)
57 		{
58 			result = eval[p+1];
59 			TrimExpr(p);
60 			eval[p] = 0;
61 			eval[p+1] = result;
62 			goto ReturnResult;
63 		}
64 
65 		n1 = eval[p+1];
66 		oper = eval[p+3];
67 
68 		/* At this point, <n1> holds the first value, and <oper> holds
69 		   the token number of the operator.
70 		*/
71 		if (eval[p+4]==1 && (eval[p+5]==OPEN_BRACKET_T
72 			|| eval[p+5]==OPEN_SQUARE_T))
73 		{
74 			eval[p+4] = 0;
75 			eval[p+5] = EvalExpr(p+6);
76 			TrimExpr(p+6);
77 		}
78 
79 		n2 = eval[p+5];
80 
81 		if (evalcount > p+7)
82 		{
83 			if (eval[p+3]==CLOSE_BRACKET_T && eval[p+2]==1)
84 			{
85 				TrimExpr(p+2);
86 				return eval[p+1];
87 			}
88 
89 			/* eval[p+7] holds the next operator, i.e., the "*"
90 			   in:  "x + y * z"
91 
92 			   This way, we can check if the upcoming operator
93 			   takes precedence over the current one.
94 			*/
95 			if ((next_prec = Precedence(eval[p+7]))
96 				< (this_prec = Precedence(oper)))
97 			{
98 				if (next_prec >= last_precedence)
99 				{
100 #if defined (DEBUG_PRECEDENCE)
101 sprintf(line, "Not preferring %s to %s because of previous level %d", token[eval[p+7]], token[oper], last_precedence);
102 Printout(line);
103 #endif
104 					goto ReturnResult;
105 				}
106 
107 #if defined (DEBUG_PRECEDENCE)
108 sprintf(line, "Preferring %s to %s", token[eval[p+7]], token[oper]);
109 Printout(line);
110 #endif
111 
112 				temp_lp = last_precedence;
113 				last_precedence = this_prec;
114 				n2 = EvalExpr(p+4);
115 				last_precedence = temp_lp;
116 			}
117 		}
118 		else if (Precedence(oper)>=last_precedence)
119 		{
120 			goto ReturnResult;
121 		}
122 
123 #if defined (DEBUG_PRECEDENCE)
124 sprintf(line, "Solving %d %s %d", n1, token[oper], n2);
125 Printout(line);
126 #endif
127 
128 		switch (oper)
129 		{
130 			case DECIMAL_T:
131 			{
132 				result = GetProp(n1, n2, 1, 0);
133 				break;
134 			}
135 
136 			case EQUALS_T:
137 			{
138 				result = (n1==n2);
139 				break;
140 			}
141 			case MINUS_T:
142 			{
143 				result = n1 - n2;
144 				break;
145 			}
146 			case PLUS_T:
147 			{
148 				result = n1 + n2;
149 				break;
150 			}
151 			case ASTERISK_T:
152 			{
153 				result = n1 * n2;
154 				break;
155 			}
156 			case FORWARD_SLASH_T:
157 			{
158 				if (n2==0)
159 #if defined (DEBUGGER)
160 				{
161 					RuntimeWarning("Division by zero:  invalid result");
162 					result = 0;
163 				}
164 #else
165 					FatalError(DIVIDE_E);
166 #endif
167 				result = n1 / n2;
168 				break;
169 			}
170 			case PIPE_T:
171 			{
172 				result = n1 | n2;
173 				break;
174 			}
175 			case GREATER_EQUAL_T:
176 			{
177 				result = (n1>=n2);
178 				break;
179 			}
180 			case LESS_EQUAL_T:
181 			{
182 				result = (n1<=n2);
183 				break;
184 			}
185 			case NOT_EQUAL_T:
186 			{
187 				result = (n1!=n2);
188 				break;
189 			}
190 			case AMPERSAND_T:
191 			{
192 				result = n1 & n2;
193 				break;
194 			}
195 			case GREATER_T:
196 			{
197 				result = (n1 > n2);
198 				break;
199 			}
200 			case LESS_T:
201 			{
202 				result = (n1 < n2);
203 				break;
204 			}
205 			case AND_T:
206 			{
207 				result = (n1 && n2);
208 				break;
209 			}
210 			case OR_T:
211 			{
212 				result = (n1 || n2);
213 				break;
214 			}
215 
216 			default:
217 			{
218 				result = n1;
219 			}
220 		}
221 
222 #if defined (DEBUGGER)
223 		if ((debug_eval) && debug_eval_error) return 0;
224 #endif
225 
226 		TrimExpr(p+4);          /* second value */
227 		TrimExpr(p+2);          /* operator */
228 
229 		eval[p] = 0;
230 		eval[p+1] = result;
231 
232 
233 	/* Keep looping while there are expression elements, or until there
234 	   is a ")", "]", or end of line
235 	*/
236 	} while ((evalcount>p+2) && !(eval[p+2]==1 &&
237 		(eval[p+3]==CLOSE_BRACKET_T || eval[p+3]==CLOSE_SQUARE_T ||
238 		eval[p+3]==255)));
239 
240 	result = eval[p+1];
241 
242 	TrimExpr(p);                    /* first value */
243 
244 ReturnResult:
245 
246 #if defined (DEBUG_EXPR_EVAL)
247 	if (p==0 && exprt)
248 	{
249 		sprintf(line, " = %d", result);
250 		AP(line);
251 	}
252 #endif
253 	return result;
254 }
255 
GetVal()256 int Hugo::GetVal() {
257 	char a = 0;
258 	char tempinexpr, tempgetaddress, tempinobj;
259 	int i, j;
260 	int tempret;
261 
262 	unsigned short routineaddr, arrayaddr;	/* must be 16 bits */
263 	short val = 0;
264 
265 	char inctype = 0;
266 	int preincdec;                  /* pre-increment/decrement */
267 
268 	defseg = gameseg;
269 
270 	tempret = ret;
271 	tempinexpr = inexpr;
272 	inexpr = 0;
273 
274 	preincdec = incdec;
275 	incdec = 0;
276 
277 	switch (MEM(codeptr))
278 	{
279 		case AMPERSAND_T:       /* an address */
280 			{codeptr++;
281 			getaddress = true;
282 			val = GetValue();
283 			getaddress = false;
284 			break;}
285 
286 		case ROUTINE_T:
287 		case CALL_T:
288 		{
289 			if (MEM(codeptr)==ROUTINE_T)
290 			{
291 				if (tail_recursion==0 && MEM(codeptr-1)==RETURN_T)
292 				{
293 					/* We may be able to tail-recurse this return
294 					   statement if it's simply 'return Routine(...)'
295 					*/
296 					tail_recursion = TAIL_RECURSION_ROUTINE;
297 				}
298 
299 				routineaddr = PeekWord(++codeptr);
300 				codeptr += 2;
301 
302 				if (getaddress)
303 					{val = routineaddr;
304 					getaddress = false;
305 					break;}
306 			}
307 			else
308 			{
309 				codeptr++;
310 				routineaddr = GetValue();
311 			}
312 
313 #if defined (DEBUGGER)
314 			if (debug_eval)
315 			{
316 				debug_eval_error = true;
317 				val = 0;
318 				break;
319 			}
320 #endif
321 			val = CallRoutine(routineaddr);
322 
323 			break;
324 		}
325 
326 		case OPEN_BRACKET_T:
327 		{
328 			codeptr++;
329 			inexpr = 1;
330 			tempgetaddress = getaddress;
331 			getaddress = false;
332 			SetupExpr();
333 			inexpr = 0;
334 			val = EvalExpr(0);
335 			getaddress = tempgetaddress;
336 			break;
337 		}
338 
339 		case MINUS_T:
340 		{
341 			codeptr++;
342 			j = inexpr;	/* don't reuse tempinexpr */
343 			inexpr = 1;
344 			val = -GetValue();
345 			inexpr = (char)j;
346 			break;
347 		}
348 
349 		case VALUE_T:                   /* integer 0 - 65535 */
350 		case OBJECTNUM_T:
351 		case DICTENTRY_T:
352 		{
353 			val = PeekWord(++codeptr);
354 			codeptr += 2;
355 			break;
356 		}
357 
358 		case ATTR_T:
359 		case PROP_T:
360 		{
361 			val = MEM(++codeptr);
362 			codeptr++;
363 			break;
364 		}
365 
366 		case VAR_T:                     /* variable */
367 		{
368 			val = var[(i=MEM(++codeptr))];
369 
370 			if (game_version >= 22)
371 			{
372 				/* Pre-v2.4 included linelength and pagelength as
373 				   global variables after objectcount
374 				*/
375 				if (i <= ((game_version>=24)?objectcount:objectcount+2))
376 				{
377 					if (i==wordcount) val = words;
378 					else if (i==objectcount) val = objects;
379 
380 					/* i.e., pre-v2.4 only */
381 					else if (i==objectcount+1)
382 					{
383 #if defined (ACTUAL_LINELENGTH)
384 						val = ACTUAL_LINELENGTH();
385 #else
386 						val = SCREENWIDTH/charwidth;
387 #endif
388 					}
389 					else if (i==objectcount+2)
390 						val = SCREENHEIGHT/lineheight;
391 				}
392 			}
393 			codeptr++;
394 
395 			if (!inobj) inctype = IsIncrement(codeptr);
396 
397 			/* don't operate on, e.g., ++variable.property as
398 			   (++variable).property
399 			*/
400 			if ((incdec || preincdec) && MEM(codeptr)!=DECIMAL_T)
401 			{
402 				if (i < MAXGLOBALS) SaveUndo(VAR_T, i, val, 0, 0);
403 
404 				if (inctype) val = Increment(val, inctype);
405 
406 				/* still a post-increment hanging around */
407 				var[i] = (val+=preincdec) + incdec;
408 
409 				incdec = preincdec = 0;
410 			}
411 
412 			break;
413 		}
414 
415 		case TRUE_T:
416 			val = 1;
417 			codeptr++;
418 			break;
419 
420 		case FALSE_T:
421 			val = 0;
422 			codeptr++;
423 			break;
424 
425 		case TILDE_T:
426 			codeptr++;
427 			val = ~GetValue();
428 			break;
429 
430 		case NOT_T:
431 			codeptr++;
432 			val = !GetValue();
433 			break;
434 
435 		case ARRAYDATA_T:
436 		case ARRAY_T:
437 		{
438 			unsigned int element;
439 
440 			if (MEM(codeptr)==ARRAY_T)
441 			{
442 				codeptr++;
443 				arrayaddr = GetValue();
444 			}
445 			else
446 			{
447 				arrayaddr = PeekWord(++codeptr);
448 				codeptr += 2;
449 			}
450 
451 			if (MEM(codeptr)!=OPEN_SQUARE_T)
452 				{val = arrayaddr;
453 				break;}
454 
455 			if (game_version>=22)
456 			{
457 				/* convert to word value */
458 				arrayaddr*=2;
459 
460 				if (game_version>=23)
461 					/* space for array length */
462 					a = 2;
463 			}
464 
465 			/* check if this is array[] (i.e., array length) */
466 			if (MEM(++codeptr)==CLOSE_SQUARE_T)
467 			{
468 				defseg = arraytable;
469 				val = PeekWord(arrayaddr);
470 				codeptr++;
471 				break;
472 			}
473 
474 			tempinobj = inobj;
475 			inobj = 0;
476 			j = GetValue();
477 			inobj = tempinobj;
478 
479 			/* The array element we're after: */
480 			element = arrayaddr+a + j*2;
481 
482 			defseg = arraytable;
483 #if defined (DEBUGGER)
484 			CheckinRange(element, debug_workspace, "array data");
485 #endif
486 			/* Check to make sure we've got a sane element number */
487 			if ((element>0) && (element < (unsigned int)(dicttable-arraytable)*16))
488 				val = PeekWord(element);
489 			else
490 				val = 0;
491 			codeptr++;
492 
493 			if (!inobj) inctype = IsIncrement(codeptr);
494 
495 			/* Don't operate on the array on:
496 
497 				 ++a[n].property
498 			*/
499 			if ((incdec || preincdec) && MEM(codeptr)!=DECIMAL_T)
500 			{
501 				/* Same sanity check for element number */
502 				if ((element>0) && (element < (unsigned)(dicttable-arraytable)*16))
503 				{
504 					if (inctype) val = Increment(val, inctype);
505 
506 					/* still a post-increment hanging around */
507 					SaveUndo(ARRAYDATA_T, arrayaddr+a, j, val, 0);
508 
509 					PokeWord(element, (val+=preincdec) + incdec);
510 
511 					incdec = preincdec = 0;
512 				}
513 			}
514 
515 			break;
516 		}
517 
518 		case RANDOM_T:
519 		{
520 			codeptr += 2;           /* skip the "(" */
521 			val = GetValue();
522 			if (val!=0)
523 #if !defined (RANDOM)
524 				val = (hugo_rand() % val)+1;
525 #else
526 				val = (RANDOM() % val)+1;
527 #endif
528 			if (MEM(codeptr)==2) codeptr++;
529 			break;
530 		}
531 
532 		case WORD_T:
533 		{
534 			codeptr += 2;           /* skip the "[" */
535 
536 			if (MEM(codeptr)==CLOSE_SQUARE_T)	/* words[] */
537 			{
538 				val = words;
539 				break;
540 			}
541 
542 			val = wd[GetValue()];
543 			if (MEM(codeptr)==CLOSE_SQUARE_T) codeptr++;
544 			break;
545 		}
546 
547 		case CHILDREN_T:
548 		{
549 			codeptr += 2;        /* skip the "(" */
550 			val = GetValue();
551 			if (MEM(codeptr)==CLOSE_BRACKET_T) codeptr++;
552 			val = Children(val);
553 			break;
554 		}
555 
556 		case PARENT_T:
557 		case SIBLING_T:
558 		case CHILD_T:
559 		case YOUNGEST_T:
560 		case ELDEST_T:
561 		case YOUNGER_T:
562 		case ELDER_T:
563 		{
564 			i = MEM(codeptr);
565 			codeptr += 2;         /* skip the "(" */
566 			val = GetValue();
567 			if (MEM(codeptr)==CLOSE_BRACKET_T) codeptr++;
568 
569 			switch (i)
570 			{
571 				case PARENT_T:
572 					val = Parent(val);
573 					break;
574 
575 				case SIBLING_T:
576 				case YOUNGER_T:
577 					val = Sibling(val);
578 					break;
579 
580 				case CHILD_T:
581 				case ELDEST_T:
582 					val = Child(val);
583 					break;
584 
585 				case YOUNGEST_T:
586 					val = Youngest(val);
587 					break;
588 
589 				case ELDER_T:
590 					val = Elder(val);
591 					break;
592 			}
593 			break;
594 		}
595 
596 		case SAVE_T:
597 			val = RunSave();
598 			codeptr++;
599 			break;
600 
601 		case RESTORE_T:
602 			val = RunRestore();
603 			codeptr++;
604 			break;
605 
606 		case SCRIPTON_T:
607 		case SCRIPTOFF_T:
608 			val = RunScriptSet();
609 			codeptr++;
610 			break;
611 
612 		case RESTART_T:
613 			val = RunRestart();
614 			codeptr++;
615 			break;
616 
617 		case STRING_T:
618 			val = RunString();
619 			break;
620 
621 		case UNDO_T:
622 			val = Undo();
623 			codeptr++;
624 			break;
625 
626 		case DICT_T:
627 			val = Dict();
628 			if (MEM(codeptr)==CLOSE_BRACKET_T) codeptr++;
629 			break;
630 
631 		case RECORDON_T:
632 		case RECORDOFF_T:
633 		case PLAYBACK_T:
634 			val = RecordCommands();
635 			codeptr++;
636 			break;
637 
638 		case READVAL_T:
639 		{
640 			val = 0;
641 			if (ioblock)
642 			{
643 #ifdef TODO
644 				int low, high;
645 
646 				if ((ioblock==1)
647 					|| (low = hugo_fgetc(io))==EOF
648 					|| (high = hugo_fgetc(io))==EOF)
649 				{
650 					ioerror = true;
651 					retflag = true;
652 				}
653 				else val = low + high*256;
654 #else
655 				error("TODO: file io");
656 #endif
657 			}
658 			codeptr++;
659 			break;
660 		}
661 
662 		case PARSE_T:
663 		{
664 			val = (short)PARSE_STRING_VAL;
665 			codeptr++;
666 			break;
667 		}
668 
669 		case SERIAL_T:
670 		{
671 			val = (short)SERIAL_STRING_VAL;
672 			codeptr++;
673 			break;
674 		}
675 
676 		case SYSTEM_T:
677 		{
678 			val = RunSystem();
679 			codeptr++;
680 			break;
681 		}
682 
683 		default:
684 		{
685 #if defined (DEBUGGER)
686 			if (debug_eval)
687 				debug_eval_error = true;
688 			else
689 #endif
690 
691 			FatalError(EXPECT_VAL_E);
692 
693 #if defined (DEBUGGER)
694 			runtime_error = true;
695 			codeptr++;
696 			return 0;
697 #endif
698 		}
699 	}
700 	defseg = gameseg;
701 	ret = tempret;
702 	inexpr = tempinexpr;
703 
704 	incdec = preincdec;
705 
706 	return val;
707 }
708 
GetValue()709 int Hugo::GetValue() {
710 	char noself = 0;
711 	int p, n;
712 	char inctype; int preincdec;
713 	int nattr = 0, attr;
714 	unsigned int pa, val;
715 	long tempptr;
716 	short g;			/* must be 16 bits */
717 	int potential_tail_recursion = 0;
718 
719 	/* Check to see if this may be a valid tail-recursion */
720 	if (tail_recursion==0 && MEM(codeptr-1)==RETURN_T)
721 	{
722 		/* We may be able to tail-recurse this return statement if
723 		   it's simply 'return object.property[.property...]'
724 		*/
725 		potential_tail_recursion = TAIL_RECURSION_PROPERTY;
726 	}
727 
728 	IsIncrement(codeptr);           /* check for ++, -- */
729 
730 	tempptr = codeptr;
731 	g = GetVal();
732 
733 	preincdec = incdec;
734 	incdec = 0;
735 
736 	if (inobj==0)
737 	{
738 	  switch (MEM(codeptr))
739 	  {
740 		case DECIMAL_T:                         /* object.property */
741 		{
742 DetermineProperty:
743 			if (MEM(++codeptr)==DECIMAL_T)  /* object..property */
744 			{
745 				noself = true;
746 				codeptr++;
747 			}
748 
749 			if (MEM(codeptr)==POUND_T)      /* object.#property */
750 			{
751 				codeptr++;
752 				inobj = true;
753 				p = GetValue();
754 				inobj = false;
755 				pa = PropAddr(g, p, 0);
756 				if (pa)
757 				{
758 					defseg = proptable;
759 					g = Peek(pa + 1);
760 					if (g==PROP_ROUTINE) g = 1;
761 					defseg = gameseg;
762 				}
763 				else
764 					g = 0;
765 			}
766 			else
767 			{
768 				inobj = true;
769 				p = GetValue();
770 				inobj = false;
771 
772 				if (MEM(codeptr) != POUND_T)
773 					n = 1;
774 
775 				else		/* object.property #x */
776 				{
777 					codeptr++;
778 
779 					/* Not GetValue(), since that might
780 					   botch "obj.property #n is attr"
781 					*/
782 					n = GetVal();
783 				}
784 
785 				/* We checked this at the start of the function, but
786 				   GetValue() for the property would've cleared it
787 				*/
788 				tail_recursion = potential_tail_recursion;
789 
790 				val = GetProp(g, p, n, noself);
791 
792 				inctype = IsIncrement(codeptr);
793 
794 				/* Increment/decrement an object.property, although
795 				   only if this is the last property in, e.g.,
796 				   object.property.property...
797 				*/
798 				if ((incdec || preincdec) && MEM(codeptr)!=DECIMAL_T)
799 				{
800 					SaveUndo(PROP_T, g, p, n, val);
801 
802 					if (inctype) val = Increment(val, inctype);
803 
804 					/* Still a post-increment hanging around */
805 					pa = PropAddr(g, p, 0);
806 					defseg = proptable;
807 
808 					/* Only change it if not a routine */
809 					if (Peek(pa+1)!=PROP_ROUTINE)
810 						PokeWord(pa+n*2, (val+=preincdec)+incdec);
811 
812 					defseg = gameseg;
813 
814 					incdec = preincdec = 0;
815 				}
816 				g = val;
817 			}
818 			if (MEM(codeptr)==IS_T) goto CheckAttribute;
819 
820 			break;
821 		}
822 
823 		case IS_T:
824 		{
825 CheckAttribute:
826 			if (!inobj)
827 			{
828 				codeptr++;
829 				if (MEM(codeptr)==NOT_T)
830 				{
831 					nattr = 1;
832 					codeptr++;
833 				}
834 				attr = GetValue();
835 #if defined (DEBUGGER)
836 				CheckinRange((unsigned)attr, (unsigned)attributes, "attribute");
837 #endif
838 				g = TestAttribute(g, attr, nattr);
839 
840 				break;
841 			}
842 		}
843 	  }
844 
845 	  switch (MEM(codeptr))
846 	  {
847 		/* This comes here (again) in order to process
848 		   object.property1.property2...
849 		*/
850 		case DECIMAL_T:  goto DetermineProperty;
851 
852 		case NOT_T:
853 			if (!inobj)
854 				{nattr = 1;
855 				codeptr++;}
856 			// fall through
857 
858 		case IN_T:
859 		{
860 			if (!inobj)
861 			{
862 				codeptr++;
863 				p = GetValue();          /* testing parent */
864 				g = (p==Parent(g));
865 				if (nattr)
866 					g = !g;
867 			}
868 		}
869 	  }
870 	}                                       /* end of "if (inobj==0)" */
871 
872 	n = MEM(codeptr);
873 
874 	/* See if we have an implicit expression that needs to be
875 	   taken as a single value, i.e., "n + 1" where we've just
876 	   read n
877 	*/
878 	if (((n>=MINUS_T && n<=PIPE_T) || n==AMPERSAND_T) &&
879 		((!inexpr)) && !inobj)
880 /*
881 #if !defined (DEBUGGER)
882 		((!inexpr)) && !inobj)
883 #else
884 		((!inexpr)) && !inobj && !debug_eval)
885 #endif
886 */
887 	{
888 		inexpr = 2;
889 		codeptr = tempptr;
890 		SetupExpr();
891 		g = EvalExpr(0);
892 		inexpr = 0;
893 	}
894 
895 	/* Not a tail-recursive 'return object.property' */
896 	if (tail_recursion_addr==0)
897 		tail_recursion = 0;
898 
899 	return g;
900 }
901 
Increment(int a,char inctype)902 int Hugo::Increment(int a, char inctype) {
903 	short v;			/* must be 16 bits */
904 
905 	v = a;
906 
907 	switch (inctype)
908 	{
909 		case MINUS_T:           {v -= incdec; break;}
910 		case PLUS_T:            {v += incdec; break;}
911 		case ASTERISK_T:        {v *= incdec; break;}
912 		case AMPERSAND_T:       {v &= incdec; break;}
913 		case PIPE_T:            {v |= incdec; break;}
914 		case FORWARD_SLASH_T:
915 		{
916 #if defined (DEBUGGER)
917 			if (incdec==0)
918 			{
919 				RuntimeWarning("Division by zero:  invalid result");
920 				v = 0;
921 			}
922 			else
923 #endif
924 				v /= incdec;
925 			break;
926 		}
927 	}
928 
929 	if (inctype!=1) incdec = 0;
930 
931 	return v;
932 }
933 
IsIncrement(long addr)934 char Hugo::IsIncrement(long addr) {
935 	unsigned char a, t = 0;
936 
937 	incdec = 0;
938 
939 	switch (a = MEM(addr))
940 	{
941 		case MINUS_T:
942 		case PLUS_T:
943 		case ASTERISK_T:
944 		case FORWARD_SLASH_T:
945 		case AMPERSAND_T:
946 		case PIPE_T:
947 		{
948 			/* ++, -- */
949 			if ((a==MINUS_T || a==PLUS_T) && MEM(addr+1)==a)
950 			{
951 				codeptr = addr + 2;
952 				if (a==PLUS_T) incdec = 1;
953 				else incdec = -1;
954 				t = 1;
955 				break;
956 			}
957 
958 			/* +=, -=, etc. */
959 			else if (MEM(addr+1)==EQUALS_T)
960 			{
961 				codeptr = addr + 2;
962 				incdec = GetValue();
963 				t = a;
964 			}
965 		}
966 	}
967 
968 #if defined (DEBUGGER)
969 	if (t && debug_eval)
970 	{
971 		debug_eval_error = true;
972 		sprintf(debug_line, "'%s%s' illegal in watch/assignment", token[a], token[MEM(addr+1)]);
973 		DebugMessageBox("Expression Error", debug_line);
974 		t = 0;
975 	}
976 #endif
977 	return t;
978 }
979 
Precedence(int t)980 int Hugo::Precedence(int t) {
981 	switch (t)
982 	{
983 		case DECIMAL_T:
984 			return 1;
985 
986 		case ASTERISK_T:
987 		case FORWARD_SLASH_T:
988 			return 2;
989 
990 		case MINUS_T:
991 		case PLUS_T:
992 			return 3;
993 
994 		case PIPE_T:
995 		case TILDE_T:
996 		case AMPERSAND_T:
997 			return 4;
998 
999 		case EQUALS_T:
1000 		case GREATER_EQUAL_T:
1001 		case LESS_EQUAL_T:
1002 		case NOT_EQUAL_T:
1003 		case GREATER_T:
1004 		case LESS_T:
1005 			return 5;
1006 
1007 		default:
1008 			return 6;
1009 	}
1010 }
1011 
1012 #if defined (DEBUG_EXPR_EVAL)
1013 /* PRINTEXPR
1014 
1015 Prints the current expression during expression tracing.
1016 */
PrintExpr(void)1017 void PrintExpr(void)
1018 {
1019 	char e[261];
1020 	int i, bracket = 0;
1021 
1022 	if (!evalcount) return;
1023 
1024 	strcpy(e, "( ");
1025 	for (i=0; i<=evalcount; i+=2)
1026 	{
1027 		switch (eval[i])
1028 		{
1029 			case 0:
1030 			{
1031 				sprintf(line, "%d ", eval[i + 1]);
1032 				strcat(e, line);
1033 				break;
1034 			}
1035 			case 1:
1036 			{
1037 				if (eval[i+1]==OPEN_BRACKET_T) bracket++;
1038 				if (eval[i+1]==CLOSE_BRACKET_T)
1039 					{bracket--;
1040 					if (bracket<0) goto ExitPrintExpr;}
1041 
1042 				if (token[eval[i+1]][0]=='~')
1043 					strcat(e, "\\");
1044 				if (eval[i+1] != 255)
1045 					{sprintf(line, "%s ", token[eval[i+1]]);
1046 					strcat(e, line);}
1047 				break;
1048 			}
1049 		}
1050 	}
1051 
1052 ExitPrintExpr:
1053 	strcat(e, ")\\;");
1054 
1055 	AP(e);
1056 }
1057 #endif
1058 
SetupExpr()1059 void Hugo::SetupExpr() {
1060 	char justgotvalue = 1;
1061 	int j, t, bracket = 0;
1062 	int tempret;
1063 	int tempeval[MAX_EVAL_ELEMENTS];
1064 	int tempevalcount;
1065 
1066 	last_precedence = 10;
1067 
1068 	tempret = ret;
1069 	tempevalcount = 0;
1070 
1071 	inobj = false;
1072 	if (!inexpr) inexpr = 1;
1073 
1074 	do
1075 	{
1076 		justgotvalue++;
1077 
1078 		switch (t = MEM(codeptr))
1079 		{
1080 			/* Various indications that we've hit the
1081 			   end of the expression:
1082 			*/
1083 			case EOL_T:
1084 				arrexpr = false;
1085 				// fall through
1086 			case COMMA_T:
1087 				multiprop = false;
1088 				// fall through
1089 			case SEMICOLON_T:
1090 			case CLOSE_SQUARE_T:
1091 			case JUMP_T:
1092 			{
1093 				if (t==EOL_T || t==COMMA_T || t==JUMP_T)
1094 					codeptr++;
1095 LeaveSetupExpr:
1096 				for (j=0; j<tempevalcount; j++)
1097 					eval[j] = tempeval[j];
1098 				evalcount = tempevalcount;
1099 
1100 				eval[evalcount] = 1;
1101 				eval[evalcount + 1] = 255;
1102 
1103 #if defined (DEBUG_EXPR_EVAL)
1104 				if (exprt) PrintExpr();
1105 #endif
1106 
1107 				ret = tempret;
1108 				return;
1109 			}
1110 
1111 
1112 			/* Otherwise we have a value: */
1113 
1114 			case OPEN_BRACKET_T:
1115 
1116 			case MINUS_T:
1117 			case PLUS_T:
1118 			case TILDE_T:
1119 			case AMPERSAND_T:
1120 			case NOT_T:
1121 
1122 			case PARENT_T:
1123 			case SIBLING_T:
1124 			case CHILD_T:
1125 			case YOUNGEST_T:
1126 			case ELDEST_T:
1127 			case YOUNGER_T:
1128 			case ELDER_T:
1129 			case CHILDREN_T:
1130 			case RANDOM_T:
1131 			case SYSTEM_T:
1132 
1133 			case PROP_T:
1134 			case ATTR_T:
1135 			case VAR_T:
1136 			case DICTENTRY_T:
1137 			case ROUTINE_T:
1138 			case OBJECTNUM_T:
1139 			case VALUE_T:
1140 
1141 			case ARRAYDATA_T:
1142 			case ARRAY_T:
1143 			case WORD_T:
1144 
1145 			case CALL_T:
1146 
1147 			case SAVE_T:
1148 			case RESTORE_T:
1149 			case SCRIPTON_T:
1150 			case SCRIPTOFF_T:
1151 			case RESTART_T:
1152 			case UNDO_T:
1153 			case READVAL_T:
1154 
1155 			case STRING_T:
1156 			case DICT_T:
1157 
1158 			case PARSE_T:
1159 			case SERIAL_T:
1160 			{
1161 				if ((t==AMPERSAND_T || t==MINUS_T ||
1162 					t==PLUS_T) && justgotvalue==1)
1163 					goto SomeSymbolorToken;
1164 
1165 				tempeval[tempevalcount] = 0;
1166 				tempeval[tempevalcount + 1] = GetValue();
1167 
1168 #if defined (DEBUGGER)
1169 				if ((debug_eval) && debug_eval_error)
1170 					return;
1171 #endif
1172 
1173 				tempevalcount += 2;
1174 				if (tempevalcount > MAX_EVAL_ELEMENTS-2)
1175 					FatalError(OVERFLOW_E);
1176 
1177 				justgotvalue = 0;
1178 
1179 				break;
1180 			}
1181 
1182 			/* Logical constants */
1183 			case TRUE_T:
1184 			case FALSE_T:
1185 			{
1186 				tempeval[tempevalcount] = 0;
1187 				if (Peek(codeptr)==TRUE_T)
1188 					tempeval[tempevalcount + 1] = 1;
1189 				else
1190 					tempeval[tempevalcount + 1] = 0;
1191 
1192 				codeptr++;
1193 
1194 				tempevalcount += 2;
1195 				if (tempevalcount > MAX_EVAL_ELEMENTS-2)
1196 					FatalError(OVERFLOW_E);
1197 
1198 				break;
1199 			}
1200 
1201 			/* Some symbol or token */
1202 			default:
1203 			{
1204 SomeSymbolorToken:
1205 				tempeval[tempevalcount] = 1;
1206 				tempeval[tempevalcount + 1] = MEM(codeptr++);
1207 
1208 				tempevalcount += 2;
1209 				if (tempevalcount > MAX_EVAL_ELEMENTS-2)
1210 					FatalError(OVERFLOW_E);
1211 
1212 				switch (MEM(codeptr-1))
1213 				{
1214 					case OPEN_BRACKET_T:
1215 						{bracket++;
1216 						break;}
1217 					case CLOSE_BRACKET_T:
1218 						{bracket--;
1219 						justgotvalue = 0;
1220 						if (inexpr==2)
1221 							codeptr--;}
1222 				}
1223 				if (bracket < 0) goto LeaveSetupExpr;
1224 
1225 				break;
1226 			}
1227 		}
1228 	}
1229 	while (true);                      /* endless loop */
1230 }
1231 
TrimExpr(int ptr)1232 void Hugo::TrimExpr(int ptr) {
1233 	int i;
1234 
1235 	for (i=ptr; i<=evalcount; i+=2)
1236 	{
1237 		eval[i] = eval[i+2];
1238 		eval[i+1] = eval[i+3];
1239 	}
1240 	evalcount -= 2;
1241 }
1242 
1243 } // End of namespace Hugo
1244 } // End of namespace Glk
1245