/* control.c : Compile control structures like while, for next, etc. * Return: class */ /* Copyright 1990, 1991, 1992 Craig Durland * Distributed under the terms of the GNU General Public License. * Distributed "as is", without warranties of any kind, but comments, * suggestions and bug reports are welcome. */ #include #include "mc.h" #include "mm.h" #include "opcode.h" extern char token[]; extern int breaklabel, contlabel, /* in mc.c */ btv; extern unsigned int class; /* in mc.c */ /* (while (test)(body)) * Compiles down to: * 1: if (test) is FALSE goto 2 ; ignore if (test) ==TRUE * (body) * goto 1 * 2: */ comp_while() { int l1, ldone, savebl = breaklabel, savecl = contlabel,z; l1 = contlabel = genlabel(); ldone = breaklabel = genlabel(); stufflabel(l1); lookahead(); /* check for TRUE or FALSE */ if (class == BOOLEAN) { if (btv == FALSE) /* (while FALSE (body) */ { groan("while loop never executed."); gojmp(JMP,ldone); } get_token(); } else /* compile (test) */ { compile(); type_check(BOOLEAN,0); gojmp(JMPFALSE,ldone); } compile(); z = class; /* compile (body), save result */ gojmp(JMP,l1); /* jump back to (test) */ stufflabel(ldone); /* next address after (body) */ breaklabel = savebl; contlabel = savecl; /* If we did a (push-arg) and we are compiling args for a fcn * call, don't push RV. * Else we don't know what RV is because of possible * (break) */ return (z == PUSHEDARGS) ? PUSHEDARGS : UNKNOWN; } /* (for (init)(test)(inc)(body)) : a glorified while * Compiles down to: * (init) * 1: if (test) is FALSE goto 3 * (goto 2) * (inc) * 2: (body) * (goto 1) * 3: */ comp_for() { int l1, l2, savebl = breaklabel, savecl = contlabel, z; l1 = genlabel(); contlabel = genlabel(); l2 = genlabel(); breaklabel = genlabel(); compile(); /* (init) */ stufflabel(l1); /* (test) */ compile(); type_check(BOOLEAN,0); gojmp(JMPFALSE,breaklabel); gojmp(JMP,l2); /* jmp around (inc) */ stufflabel(contlabel); compile(); gojmp(JMP,l1); /* (inc) */ stufflabel(l2); compile(); z = class; gojmp(JMP,contlabel); /* (body) */ stufflabel(breaklabel); breaklabel = savebl; contlabel = savecl; return (z == PUSHEDARGS) ? PUSHEDARGS : UNKNOWN; /* same as (while) */ } /* (if (test) (t) (f)) * Compiles down to: * if (test) is FALSE goto 1 * (t) * goto 2 ; not used if no else clause * 1: (f) ; not used if no else clause * 2: ; not used if no else clause * Note: * (if () ...) means use RV for the test. */ comp_if(lastclass) unsigned int lastclass; { int l1, l2, z; l1 = genlabel(); compile(); if (class == EMPTY) class = lastclass; type_check(BOOLEAN,0); gojmp(JMPFALSE,l1); compile(); z = class; lookahead(); /* check for else block */ if (class == DELIMITER && *token == ')') stufflabel(l1); else { l2 = genlabel(); gojmp(JMP,l2); stufflabel(l1); compile(); if (z != class) z = UNKNOWN; stufflabel(l2); } return z; } /* (cond (test1) (body1) (test2) (body2) ...) * A glorified if then else if then else if then else ... * Compiles down to: * if (test1) is FALSE goto 2 * (body1) * goto done * 2: if (test2) is FALSE goto 3 * (body2) * goto done * 3: * done: */ comp_cond() { int l1, ldone; ldone = genlabel(); do { l1 = genlabel(); compile(); type_check(BOOLEAN,0); gojmp(JMPFALSE,l1); compile(); gojmp(JMP,ldone); stufflabel(l1); } while (gaze_ahead(BOOLEAN,0)); stufflabel(ldone); return UNKNOWN; } /* (switch val val1 (body1) val2 (body2) ... [default (dbody)]) * A simplified if then else if then else if then else * Compiles down to: * push val * if val1 != val goto 2 * body1 * goto done * 2: if val2 != val goto 3 * body2 * goto done * 3: ... * (dbody) ; the default case * done: pop val */ comp_switch() { int l1, ldone, z; ldone = genlabel(); compile(); z = class; checkit("switch",STRING,NUMBER,BOOLEAN,0); pushpush(); do { lookahead(); if (class == TOKEN && strcmp(token,"default") == 0) { get_token(); compile(); break; } else { l1 = genlabel(); genop(DUP); compile(); if (z != UNKNOWN) type_check(z,0); /* yukk!!! */ genop(CMP); gojmp(JMPFALSE,l1); compile(); gojmp(JMP,ldone); stufflabel(l1); } } while (gaze_ahead(TOKEN,STRING,NUMBER,BOOLEAN,0)); stufflabel(ldone); genop(POP); return UNKNOWN; }