DIRECTORY
  PrintingDefs USING [PrintComments, OutCode],
  PrintDclsDefs USING [PrintDeclarations],
  PrintExprDefs USING [IntExpToRelation, MakeExpression],
  PrintStmtsDefs USING [],
  SymbolTableDefs USING [EnterBlock, ExitBlock, PrintSymbolTable],
  TreesDefs USING [FreeTree, TreeNode],
  Storage USING [FreeString];

PrintStmtsImpl: PROGRAM IMPORTS PrintDclsDefs, PrintingDefs, Storage,
        TreesDefs, SymbolTableDefs, PrintExprDefs
    EXPORTS PrintStmtsDefs = {
  
  PrintStatement: PUBLIC PROCEDURE [node: LONG POINTER TO TreesDefs.TreeNode] = {
    
    temp: LONG STRING;
    
    
    PrintStatementRec: PUBLIC PROCEDURE [node: LONG POINTER TO TreesDefs.TreeNode,
	indent: CARDINAL, semiAtEnd, inSwitch, inContinue: BOOLEAN]
	RETURNS [wasGoto: BOOLEAN] = {
    
    wasGoto ← FALSE;
    DO
    IF node = NIL THEN RETURN;
    SELECT node.nodeType FROM
    
        ifStmt => {
	    PrintingDefs.OutCode["IF "L, indent];
	    PrintExprDefs.IntExpToRelation[@node.left];
	    temp ← PrintExprDefs.MakeExpression[node.left, FALSE];
	    PrintingDefs.OutCode[temp, 0];
	    Storage.FreeString[temp];
	    PrintingDefs.OutCode[" THEN\n"L, 0];
	    wasGoto ← PrintStatementRec[node: node.right.left,
	        indent: indent + 1, semiAtEnd: FALSE, inSwitch: inSwitch,
		inContinue: FALSE];
	    IF node.right.right # NIL THEN {
		PrintingDefs.OutCode["\n"L, 0];
		PrintingDefs.OutCode["ELSE\n"L, indent];
		wasGoto ← wasGoto OR PrintStatementRec[node: node.right.right,
		    indent: indent + 1, semiAtEnd: FALSE, inSwitch: inSwitch,
		    inContinue: FALSE]; };
	    GOTO Done; };
	    
        whileStmt => {
	    PrintingDefs.OutCode["WHILE "L, indent];
	    PrintExprDefs.IntExpToRelation[@node.left];
	    temp ← PrintExprDefs.MakeExpression[node.left, FALSE];
	    PrintingDefs.OutCode[temp, 0];
	    Storage.FreeString[temp];
	    PrintingDefs.OutCode[" DO\n"L, 0];
	    -- skip compound statement by first printing
	    -- its declarations then printing the stmtList
	    IF node.right.nodeType = compoundStmt THEN {
	        SymbolTableDefs.EnterBlock[];
	        PrintDclsDefs.PrintDeclarations[node.right.left,
				indent + 1, ";\n"L, TRUE];
                [] ← PrintStatementRec[node: node.right.right,
		    indent: indent + 1, semiAtEnd: TRUE, inSwitch: FALSE,
		    inContinue: FALSE];
		    SymbolTableDefs.ExitBlock[]; }
            ELSE [] ← PrintStatementRec[node: node.right,
	        indent: indent + 1, semiAtEnd: TRUE, inSwitch: FALSE,
		inContinue: FALSE];
	    PrintingDefs.OutCode["ENDLOOP"L, indent + 1];
	    GOTO Done; };
	    
        doWhileStmt => {
	    loopInBlock: BOOLEAN ← LoopInBlock[node.right];
	    
	    PrintingDefs.OutCode["DO"L, indent];
	    IF loopInBlock THEN PrintingDefs.OutCode[" BEGIN"L, 0];
	    PrintingDefs.OutCode["\n"L, 0];
	    -- skip compound statement by first printing
	    -- its declarations then printing the stmtList
	    IF node.right.nodeType = compoundStmt THEN {
	        SymbolTableDefs.EnterBlock[];
	        PrintDclsDefs.PrintDeclarations[node.right.left, indent + 1, ";\n"L, TRUE];
                [] ← PrintStatementRec[node: node.right.right,
		    indent: indent + 1, semiAtEnd: TRUE, inSwitch: FALSE,
		    inContinue: loopInBlock];
		    SymbolTableDefs.ExitBlock[]; }
            ELSE [] ← PrintStatementRec[node: node.right,
		        indent: indent + 1, semiAtEnd: TRUE, inSwitch: FALSE,
		        inContinue: loopInBlock];
	    IF loopInBlock THEN
                PrintingDefs.OutCode["EXITS Continue => NULL; END;\n"L, indent + 1];
	    PrintingDefs.OutCode["IF NOT "L, indent + 1];
	    PrintExprDefs.IntExpToRelation[@node.left];
	    temp ← PrintExprDefs.MakeExpression[node.left, TRUE];
	    PrintingDefs.OutCode[temp, 0];
	    Storage.FreeString[temp];
	    PrintingDefs.OutCode[" THEN EXIT;\n"L, 0];
	    PrintingDefs.OutCode["ENDLOOP"L, indent + 1];
	    GOTO Done; };
	    
        forStmt1 => {
	    loopInBlock: BOOLEAN ← LoopInBlock[node.left];
	    
	    IF node.right.right.left # NIL THEN {
	        PrintExpressionAsStatement[node.right.right.left, indent];
                PrintingDefs.OutCode[";\n"L, 0]; };
	    IF node.right.right.right # NIL THEN {
	        PrintingDefs.OutCode["WHILE "L, indent];
		PrintExprDefs.IntExpToRelation[@node.right.right.right];
		temp ← PrintExprDefs.MakeExpression[node.right.right.right, FALSE];
		PrintingDefs.OutCode[temp, 0];
	        Storage.FreeString[temp];
		PrintingDefs.OutCode[" "L, 0]; }
	    ELSE
	        PrintingDefs.OutCode[""L, indent];
	    PrintingDefs.OutCode["DO"L, 0];
	    IF loopInBlock THEN PrintingDefs.OutCode[" BEGIN"L, 0];
	    PrintingDefs.OutCode["\n"L, 0];
	    IF node.left.nodeType = compoundStmt THEN {
	        SymbolTableDefs.EnterBlock[];
	        PrintDclsDefs.PrintDeclarations[node.left.left, indent + 1, ";\n"L, TRUE];
                wasGoto ← PrintStatementRec[node: node.left.right,
		    indent: indent + 1, semiAtEnd: TRUE, inSwitch: FALSE,
		    inContinue: loopInBlock];
		    SymbolTableDefs.ExitBlock[]; }
            ELSE wasGoto ← PrintStatementRec[node: node.left,
		indent: indent + 1, semiAtEnd: TRUE, inSwitch: FALSE,
		 inContinue: loopInBlock];
	    IF loopInBlock THEN
                PrintingDefs.OutCode["EXITS Continue => NULL; END;\n"L, indent + 1];
	    IF node.right.left # NIL THEN {
		PrintExpressionAsStatement[node.right.left, indent + 1];
                PrintingDefs.OutCode[";\n"L, 0];  };
	    PrintingDefs.OutCode["ENDLOOP"L, indent + 1];
	    GOTO Done; };
	    
        switchStmt => {
	    n: LONG POINTER TO TreesDefs.TreeNode;
	    backUpTo: LONG POINTER TO TreesDefs.TreeNode;
	    default: LONG POINTER TO TreesDefs.TreeNode;
	    statement: LONG POINTER TO TreesDefs.TreeNode;
	    wasBreak: BOOLEAN;
	    
	    PrintingDefs.OutCode["SELECT "L, indent];
	    temp ← PrintExprDefs.MakeExpression[node.left, FALSE];
	    PrintingDefs.OutCode[temp, 0];
	    Storage.FreeString[temp];
	    PrintingDefs.OutCode[" FROM\n"L, 0];
            default ← NIL;
	    backUpTo ← NIL;
	    wasBreak ← TRUE;
	    wasGoto ← FALSE;
            n ← node.right;	-- get to compoundStmt
	    IF n.nodeType # compoundStmt THEN
	        PrintingDefs.OutCode[
		    "*** select error - no compound statement. ***"L, 0];
	    n ← n.right;	-- get to stmtList
	    DO
	        statement ← n.left;
		IF n.string # NIL AND n.string.length > 0 THEN
	            PrintingDefs.PrintComments[n.string, indent];
		IF statement # NIL THEN -- skip the select if statment = NIL
	        SELECT statement.nodeType FROM
		
	            caseLabel => {
		        IF wasBreak THEN {
			    IF backUpTo # NIL THEN {
			        n ← backUpTo;
				backUpTo ← NIL;
				LOOP; };
			    wasBreak ← FALSE;
			    temp ← PrintExprDefs.MakeExpression[statement.left, FALSE];
			    PrintingDefs.OutCode[temp, indent + 1];
	                    Storage.FreeString[temp];
			    WHILE n.right # NIL
			        AND n.right.left # NIL
			        AND n.right.left.nodeType = caseLabel
			    DO
			        n ← n.right;
			        PrintingDefs.OutCode[", "L, 0];
			        temp ← PrintExprDefs.MakeExpression[n.left.left, FALSE];
			        PrintingDefs.OutCode[temp, indent + 1];
	                        Storage.FreeString[temp];
			        ENDLOOP;
			    PrintingDefs.OutCode[" => {\n"L, 0]; }
			ELSE {
			    IF backUpTo = NIL THEN backUpTo ← n; }; };
			
	            breakStmt => {
		        wasBreak ← TRUE;
		        IF wasGoto THEN {
			    wasGoto ← FALSE;
			    PrintingDefs.OutCode["EXITS outSwitch=>NULL};"L, indent + 2]; }
			ELSE PrintingDefs.OutCode["};"L, indent + 2];
			PrintingDefs.OutCode["\n"L, 0];};
		    
	            loopStmt, gotoStmt, returnStmt => {
		        [] ← PrintStatementRec[node: statement, indent: indent + 2,
			    semiAtEnd: TRUE, inSwitch: TRUE, inContinue: FALSE];
		        wasBreak ← TRUE;
		        IF wasGoto THEN {
			    wasGoto ← FALSE;
			    PrintingDefs.OutCode["EXITS outSwitch=>NULL};"L, indent + 2]; }
			ELSE PrintingDefs.OutCode["};"L, indent + 2];
			PrintingDefs.OutCode["\n"L, 0];};
		    
	            defaultLabel => {
		        default ← n;  -- remember where it is
		        -- skip to next case if there was a break
		        IF wasBreak THEN {
			    WHILE n.right # NIL AND n.right.left.nodeType # caseLabel DO
			        n ← n.right;  ENDLOOP; };
			IF n.right = NIL THEN wasBreak ← TRUE; };
			
	        ENDCASE => {wasGoto ← wasGoto OR PrintStatementRec[node: statement,
		                indent: indent + 2, semiAtEnd: TRUE, inSwitch: TRUE,
		    inContinue: FALSE]; };
			
	        n ← n.right;
	        IF n = NIL THEN {
		    IF NOT wasBreak THEN {
		        IF wasGoto THEN {
			    wasGoto ← FALSE;
			    PrintingDefs.OutCode["EXITS outSwitch=>NULL}"L, indent + 2]; }
			ELSE PrintingDefs.OutCode["};"L, indent + 2];
			PrintingDefs.OutCode["\n"L, 0];
			wasBreak ← TRUE; };
		    IF backUpTo # NIL THEN {
		        n ← backUpTo;
			backUpTo ← NIL }
		    ELSE EXIT; };
	    ENDLOOP;
            PrintingDefs.OutCode["ENDCASE"L, indent + 1];
	    IF default # NIL THEN {
	        PrintingDefs.OutCode[" => {\n"L, 0];
		wasGoto ← FALSE;
		DO
		    default ← default.right; -- next statment
		    IF default = NIL THEN EXIT;
		    SELECT default.left.nodeType FROM
		        breakStmt => EXIT;
			caseLabel => NULL;
		    ENDCASE => wasGoto ← wasGoto OR
		    		PrintStatementRec[node: default.left,
		    		indent: indent + 2, semiAtEnd: TRUE, inSwitch: TRUE,
		    inContinue: FALSE];
		ENDLOOP;
		IF wasGoto THEN {
		    wasGoto ← FALSE;
		    PrintingDefs.OutCode["EXITS outSwitch=>NULL}"L, indent + 2]; }
		ELSE PrintingDefs.OutCode["}"L, indent + 2];
		};
	    GOTO Done; };
	    
        breakStmt => {
	    IF inSwitch THEN {
	        PrintingDefs.OutCode["GOTO outSwitch"L, indent];
		wasGoto ← TRUE; }
	    ELSE PrintingDefs.OutCode["EXIT"L, indent];
	    GOTO Done; };
	    
        loopStmt => {
	    IF inContinue THEN PrintingDefs.OutCode["GOTO Continue"L, indent]
	    ELSE PrintingDefs.OutCode["LOOP"L, indent];
	    GOTO Done; };
	    
        returnStmt => {
	    PrintingDefs.OutCode["RETURN"L, indent];
	    IF node.left # NIL THEN {
	        PrintingDefs.OutCode[" ["L, 0];
		temp ← PrintExprDefs.MakeExpression[node.left, FALSE];
		PrintingDefs.OutCode[temp, 0];
	        Storage.FreeString[temp];
		PrintingDefs.OutCode["]"L, 0]; };
	    GOTO Done; };
	    
        gotoStmt => {
	    PrintingDefs.OutCode["GOTO "L, indent];
	    PrintingDefs.OutCode[node.string, 0];
	    GOTO Done; };
	    
        emptyStmt => {
	    PrintingDefs.OutCode["NULL"L, indent];
	    GOTO Done; };
	    
        compoundStmt => {
	    PrintingDefs.OutCode["{\n"L, indent];
	    SymbolTableDefs.EnterBlock[];
	    PrintDclsDefs.PrintDeclarations[node.left, indent, ";\n"L, TRUE];
	    wasGoto ← PrintStatementRec[node: node.right, indent: indent,
	        semiAtEnd: TRUE, inSwitch: inSwitch, inContinue: FALSE];
	    SymbolTableDefs.PrintSymbolTable[];
	    SymbolTableDefs.ExitBlock[];
	    PrintingDefs.OutCode["}"L, indent];
	    GOTO Done; };
	    
        expressionStmt => {
	    PrintExpressionAsStatement[node.left, indent];
	    GOTO Done; };
	    
        labelLabel => {
	    PrintingDefs.OutCode["label ← "L, indent];
	    PrintingDefs.OutCode[node.string, 0];
	    GOTO Done; };
	    
        stmtList => {
	    IF node.string # NIL AND node.string.length > 0 THEN
	        PrintingDefs.PrintComments[node.string, indent];
	    wasGoto ← PrintStatementRec[node: node.left,indent: indent,
	        semiAtEnd: TRUE, inSwitch: inSwitch,inContinue: FALSE];
	    node ← node.right; };
	    
    ENDCASE => {
            PrintingDefs.OutCode["*** unknown node type ***"L, 0];
	    IF node.string # NIL THEN {
	        PrintingDefs.OutCode["   string: "L, 0];
	        PrintingDefs.OutCode[node.string, 0]; };
	    PrintingDefs.OutCode[";\n"L, 0];
	    GOTO Done; };
    REPEAT
    	Done => {IF semiAtEnd THEN PrintingDefs.OutCode[";\n"L, 0]; };
    ENDLOOP;
    };
    
    -- begin of main PrintStatement code --
    [] ← PrintStatementRec[node: node, indent: 0,
        semiAtEnd: TRUE, inSwitch: FALSE, inContinue: FALSE];
    TreesDefs.FreeTree[node];
    };
    
    
  LoopInBlock: PROCEDURE [node: LONG POINTER TO TreesDefs.TreeNode]
        RETURNS [BOOLEAN] = {
  
    LoopInBlockRec: PROCEDURE [node: LONG POINTER TO TreesDefs.TreeNode]
            RETURNS [BOOLEAN] = {
    
        IF node = NIL THEN RETURN [FALSE];
	
	SELECT node.nodeType FROM
	    loopStmt => RETURN [TRUE];
	    whileStmt => RETURN [FALSE];
            doWhileStmt => RETURN [FALSE];
	    forStmt1 => RETURN [FALSE];
	    ENDCASE => RETURN [LoopInBlockRec[node.left]
	                       OR LoopInBlockRec[node.right]];
        };
    
    RETURN [LoopInBlockRec[node]];
    };
    
    
  PrintExpressionAsStatement: PROCEDURE [node: LONG POINTER TO TreesDefs.TreeNode, indent: CARDINAL] = {
  
    temp:LONG STRING ← PrintExprDefs.MakeExpression[node, FALSE];
    
    PrintingDefs.OutCode[""L, indent];	-- print indent only
    -- If this is an assignment expression used solely for the assignment
    -- side effect then use the non-value-producing form of the
    -- assignment procedure.
    SELECT node.operationType FROM
        multiplyAssign,
        divideAssign,
        modulusAssign,
        plusAssign,
        minusAssign,
        shiftLeftAssign,
        shiftRightAssign,
        bitAndAssign,
        bitOrAssign,
        bitXorAssign,
        postIncrement,
        postDecrement => PrintingDefs.OutCode["[]←"L, 0];
	ENDCASE => NULL;
    PrintingDefs.OutCode[temp, 0];
    Storage.FreeString[temp];
    };
    
}.