As in Part 1 of the Hints for Control Structures, covering the If-Then-Elsif-Else-End, I suggested you take it in pieces.
We use the notation that {} denotes pseudo code and () enclose tuples. For a simple loop such as loop statement list end loop; We generate (LABEL, LoopStart) { code for statement list } (JUMP, LoopStart) For a While-loop such as while bexpr loop statement list end loop; We generate: (LABEL, LoopStart) { code for bexpr } (JUMP0, bexpr, Out) { code for statement list } (JUMP, LoopStart) (LABEL, Out)
The material in Section 8.4.5 is somewhat related, but it is more on the ForLoop.
Once you have the if-then-elsif-else-end if running, you should try the loop structure. I have combined the Semantic Processing (extensive) with the Code Generation suggestion in this one handout, since so much of the processing is in the creation of the Statement Structure and a great deal of similar code generation has been accomplished with the If-Then structures from Phase 5: Control Structures - Part 1.
Consider the sample:
loopit: while bexpr1 loop bunch of statements (i.e. a stmtlist) exit when bexpr2; (exit the "current loop" depending on bexpr2 being true/false) statements exit loopit; (just get out of the loop loopit) statements exit; (exit the "current loop") statements exit loopit when bexpr3; (exit "loopit" depending on bexpr3) statements end loop; (or end loop loopit; )Loops can be nested and can optionally have labeled names. You must be able to keep track of named loops - using the symbol table. So there will be a new kind of entry in the symbol table where we keep information about loop labels when they are encountered:
typedef struct attrec{ IDNAME id; /* user name */ int level; /* variable to hold procedure level */ int offset; /* Current variable's offset in memory */ TYPEKIND typeid; /* variables are declared with a type */ int typename; /* distinguish "integer" from user name */ int initialized; int used; int loopopen; /* for loops, keeping track of scope */ } ATTREC, *ATTRPTR;"initialized" is set true (1) when the loop name ("loopit" above) is first encountered.
You can keep track of the loop's scope number (the value of loop_depth, below) in the symbol table.
The grammar rules that accomplish this can be: loopstmt : id_colon_opt iter_opt basic_loop id_opt {makeloop();} id_colon_opt : identifier Colonsym {insertloopname();} | lambda {nullname();} iter_opt : While booleanexpr | lambda {nullexpr();} basic_loop : loop_head stmtlist End Loop {closeloop();} loop_head : Loop {openloop();} id_opt : identifier {checkname();} | lambda exitstmt : Exit name_opt when_opt {makeexit();} when_opt : When booleanexpr | lambda {nullexpr();} name_opt : identifier | lambda { nullname();}Don't forget to keep your lrdebug entries going. I am hoping you have seen how useful this mechanism is for tracking the translation of our project.
typedef enum { /* --- statements and statement lists --- */ AssignStmt, StdProcCall, /* read or write */ IfThenStmt, LoopStmt, ExitStmt } STMTTYPE;
typedef struct stmt{ /* statement description */ STMTTYPE type; union { struct { EXPRTREE bexpr; struct stmt *slist; struct stmt *elsiflist; struct stmt *elselist; }ifthen; /* ifthenstmt */ struct { ATTRPTR id; EXPRTREE bexpr; struct stmt *slist; }loop; /* loopstmt */ struct { ATTRPTR id; EXPRTREE bexpr; }exit; /* exitstmt */ struct { ADDRTREE lhs; EXPRTREE rhs; }assign; /* assignstmt */ struct { PROCEDURETYPE type; PARAMLIST params; }procall; /* stdproccall */ }se; struct stmt *next; /* next statement in list */ } STMTNODE, *STMTLIST, *STMTPTR; /* stmtlistnode */ /* dynamic memory allocation of statement list node */ #define stmt_alloc() (STMTLIST)(malloc(sizeof(STMTNODE)))
The scoping (nesting) of the loops is kept track of by a global
variable, initialized to 0 in sem.h
/* for loops and keeping track of their nesting */ static int loop_depth = 0;
Here's an idea of what the semantic actions accomplish:
/* --------------------------------------------------------------------- */ /* -------- OPENLOOP: INCREASE LOOP NAME SCOPE ------------------------ */ /* --------------------------------------------------------------------- */ openloop() { loop_depth++; } /* --------------------------------------------------------------------- */ /* -------- CLOSELOOP: MARK LOOP NAME SCOPE CLOSED --------------------- */ /* --------------------------------------------------------------------- */ /* PRODUCTIONS: * closeloop ::= Loop stmts End Loop : #closeloop * * semtop -> | slist | * --------- * | bexpr | * --------- * | name | expr may be nullexpr; name may be nullname * */
/* ------------------------------------------------------------------- */ /* -------- INSERTLOOPNAME: INSERT LOOP ID INTO SYMTAB -------------- */ /* ------------------------------------------------------------------- */ /* PRODUCTIONS: * id_colon_option ::= Id : #insertloopname */ /* SEMANTIC STACK MANIPULATION: * top ->| identry | >>> | attrec |<- top * ----------- ----------- * | ? | | ? | */ and /* --------------------------------------------------------------------- */ /* ------ NULLNAME: Push a null identifier onto semantic stack ------- */ /* --------------------------------------------------------------------- */ /* SEMANTIC STACK MANIPULATION: * | | | attrec |<- top * ----------- ------------ * top ->| ? | | ? | * */Are used to get a labeled loop or null-labeled loop name onto the Semstack.
The grammar will have caused a bexprentry to be on the semantic stack if the grammar rule when_opt : When booleanexpr was reduced otherwise there will be
/* --------------------------------------------------------------------- */ /* -------------- NULLEXPR: CREATE A NULL EXPR ------------------------- */ /* --------------------------------------------------------------------- */ /* PRODUCTIONS: * iter_opt ::= lambda #nullexpr * whenoption ::= lambda #nullexpr */
If the user has included a "loop name" as an optional id after the end loop, you check it:
/* -------------------------------------------------------------------------- */ /* ------------- CHECKNAME: CHECK LOOP NAME WITH ONE ON STACK -------------- */ /* -------------------------------------------------------------------------- */ /* PRODUCTIONS: * id_opt ::= Id #checkname */ /* SEMANTIC STACK MANIPULATION: * top ->| identry | >>> | stmtlist |<- top * ------------ ------------ * | stmtlist | |null/bexpr| * ------------ ------------ * |null/bexpr| | identry | * ------------ ------------ * | identry | | ? | * * POP 1 - once you verified that the optional name on the end loop is * the valid Open Loop * NOTE: the picture is not very clear here! */
/* -------------------------------------------------------------------------- */ /* ------- MAKELOOP : MAKE A LOOP NODE - use assign as model --------------- */ /* -------------------------------------------------------------------------- */ /* PRODUCTIONS: * loopstmt ::= id_colon_opt iter_opt basic_loop id_opt #makeloop */ /* SEMANTIC STACK MANIPULATION: * top ->|stlstentry| * ------------ * |bexprentry| * ------------ * | identry | >>> |stmtentry|<- top * ------------ ----------- * | ? | | ? | * * make the statement entry and POP 2 */
/* -------------------------------------------------------------------------- */ /* ------- MAKEEXIT : MAKE AN EXIT NODE - use assign as model -------------- */ /* -------------------------------------------------------------------------- */ /* PRODUCTIONS: * exitstmt ::= exit Id booleanexpr #makeexit */ /* SEMANTIC STACK MANIPULATION: * top ->|bexprentry| * ------------ * | identry | >>> |stmtentry|<- top * ------------ ----------- * | ? | | ? | */ bexprentry may be null; identry may be null.
bexprentry may be a null expression depending on whether the loop
is a while bexpr1 loop structure
or a simple loop structure.
- identry may be a nullname depending on whether the loop had a
label