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