//
// Copyright (c) Microsoft Corporation 1995
//
// codegen.c
//
// This file contains the code-generating functions.
//
// The "code" is actually just an intermediate representation.
// Currently this is an array of ASTs.
//
// History:
//  06-18-95 ScottH     Created
//


#include "proj.h"
#include "rcids.h"

RES     PRIVATE Stmt_Codegen(PSTMT this, PASTEXEC pastexec, PSYMTAB pst);


/*----------------------------------------------------------
Purpose: Generate code for the 'while' statement

Returns: RES_OK
         or some error result

Cond:    --
*/
RES PRIVATE WhileStmt_Codegen(
    PSTMT this,
    PASTEXEC pastexec,
    PSYMTAB pst)
    {
    RES res;
    LPSTR pszTop;
    LPSTR pszEnd;

    ASSERT(this);
    ASSERT(pastexec);
    ASSERT(AT_WHILE_STMT == Ast_GetType(this));

    pszTop = WhileStmt_GetTopLabel(this);
    pszEnd = WhileStmt_GetEndLabel(this);

    res = Astexec_InsertLabel(pastexec, pszTop, pst);
    if (RSUCCEEDED(res))
        {
        // add the 'while' statement for the test expression
        res = Astexec_Add(pastexec, this);
        if (RSUCCEEDED(res))
            {
            // add the statements in the statement block
            DWORD i;
            DWORD cstmts;
            HPA hpaStmts = WhileStmt_GetStmtBlock(this);

            res = RES_OK;

            cstmts = PAGetCount(hpaStmts);

            // Add each statement
            for (i = 0; i < cstmts; i++)
                {
                PSTMT pstmt = PAFastGetPtr(hpaStmts, i);

                res = Stmt_Codegen(pstmt, pastexec, pst);
                if (RFAILED(res))
                    break;
                }

            if (RSUCCEEDED(res))
                {
                // add the end label
                res = Astexec_InsertLabel(pastexec, pszEnd, pst);
                }
            }
        }

    return res;
    }


/*----------------------------------------------------------
Purpose: Generate code for the 'if' statement

Returns: RES_OK
         or some error result

Cond:    --
*/
RES PRIVATE IfStmt_Codegen(
    PSTMT this,
    PASTEXEC pastexec,
    PSYMTAB pst)
    {
    RES res;
    LPSTR pszElse;
    LPSTR pszEnd;

    ASSERT(this);
    ASSERT(pastexec);
    ASSERT(AT_IF_STMT == Ast_GetType(this));

    pszElse = IfStmt_GetElseLabel(this);
    pszEnd = IfStmt_GetEndLabel(this);

    // add the 'if' statement for the test expression
    res = Astexec_Add(pastexec, this);
    if (RSUCCEEDED(res))
        {
        // add the statements in the 'then' statement block
        DWORD i;
        DWORD cstmts;
        HPA hpaStmts = IfStmt_GetStmtBlock(this);

        res = RES_OK;

        cstmts = PAGetCount(hpaStmts);

        // Add each statement
        for (i = 0; i < cstmts; i++)
            {
            PSTMT pstmt = PAFastGetPtr(hpaStmts, i);

            res = Stmt_Codegen(pstmt, pastexec, pst);
            if (RFAILED(res))
                break;
            }

        if (RSUCCEEDED(res))
            {
            // add the else label
            res = Astexec_InsertLabel(pastexec, pszElse, pst);
            }
        }

    return res;
    }


/*----------------------------------------------------------
Purpose: Generate code for the label statement

Returns: RES_OK
         or some error result

Cond:    --
*/
RES PRIVATE LabelStmt_Codegen(
    PSTMT this,
    PASTEXEC pastexec,
    PSYMTAB pst)
    {
    LPSTR pszIdent;

    ASSERT(this);
    ASSERT(pastexec);
    ASSERT(AT_LABEL_STMT == Ast_GetType(this));

    pszIdent = LabelStmt_GetIdent(this);

    return Astexec_InsertLabel(pastexec, pszIdent, pst);
    }


/*----------------------------------------------------------
Purpose: Generate code for the 'set' statement

Returns: RES_OK
         or some error result

Cond:    --
*/
RES PRIVATE SetStmt_Codegen(
    PSTMT this,
    PASTEXEC pastexec,
    PSYMTAB pst)
    {
    RES res = RES_OK;

    ASSERT(this);
    ASSERT(pastexec);
    ASSERT(AT_SET_STMT == Ast_GetType(this));

    switch (SetStmt_GetType(this))
        {
    case ST_IPADDR:
    case ST_PORT:
    case ST_SCREEN:
        res = Astexec_Add(pastexec, this);
        break;

    default:
        ASSERT(0);
        res = RES_E_INVALIDPARAM;
        break;
        }

    return res;
    }


/*----------------------------------------------------------
Purpose: Generate code for a statement

Returns: RES_OK
         or some error result

Cond:    --
*/
RES PRIVATE Stmt_Codegen(
    PSTMT this,
    PASTEXEC pastexec,
    PSYMTAB pst)
    {
    RES res;

    ASSERT(this);
    ASSERT(pastexec);

    switch (Ast_GetType(this))
        {
    case AT_ENTER_STMT:
    case AT_LEAVE_STMT:
    case AT_HALT_STMT:
    case AT_TRANSMIT_STMT:
    case AT_WAITFOR_STMT:
    case AT_DELAY_STMT:
    case AT_GOTO_STMT:
    case AT_ASSIGN_STMT:
        res = Astexec_Add(pastexec, this);
        break;

    case AT_WHILE_STMT:
        res = WhileStmt_Codegen(this, pastexec, pst);
        break;

    case AT_IF_STMT:
        res = IfStmt_Codegen(this, pastexec, pst);
        break;

    case AT_SET_STMT:
        res = SetStmt_Codegen(this, pastexec, pst);
        break;

    case AT_LABEL_STMT:
        res = LabelStmt_Codegen(this, pastexec, pst);
        break;

    default:
        ASSERT(0);
        res = RES_E_INVALIDPARAM;
        break;
        }

    return res;
    }


/*----------------------------------------------------------
Purpose: Generate code for a procedure declaration.

Returns: RES_OK
         or some error result

Cond:    --
*/
RES PRIVATE ProcDecl_Codegen(
    PPROCDECL this,
    PASTEXEC pastexec)
    {
    RES res = RES_OK;
    DWORD i;
    DWORD cstmts;

    ASSERT(this);
    ASSERT(pastexec);

    cstmts = PAGetCount(this->hpaStmts);

    // Generate for each statement
    for (i = 0; i < cstmts; i++)
        {
        PSTMT pstmt = PAFastGetPtr(this->hpaStmts, i);

        res = Stmt_Codegen(pstmt, pastexec, this->pst);
        if (RFAILED(res))
            break;
        }

    return res;
    }


/*----------------------------------------------------------
Purpose: Find the proc decl that has the given identifier.

Returns: TRUE (if found)

Cond:    --
*/
BOOL PRIVATE FindProc(
    PMODULEDECL pmd,
    LPCSTR pszIdent,
    PPROCDECL * ppprocdecl)
    {
    DWORD i;
    DWORD cprocs = PAGetCount(pmd->hpaProcs);

    *ppprocdecl = NULL;

    for (i = 0; i < cprocs; i++)
        {
        PPROCDECL pprocdecl = PAFastGetPtr(pmd->hpaProcs, i);

        if (IsSzEqualC(ProcDecl_GetIdent(pprocdecl), pszIdent))
            {
            *ppprocdecl = pprocdecl;
            break;
            }
        }

    return NULL != *ppprocdecl;
    }


/*----------------------------------------------------------
Purpose: Generate code for the module declaration.

Returns: RES_OK
         or some error result

Cond:    --
*/
RES PUBLIC ModuleDecl_Codegen(
    PMODULEDECL this,
    PASTEXEC pastexec)
    {
    RES res = RES_OK;
    DWORD i;
    DWORD cprocs;
    PPROCDECL ppdMain;

    ASSERT(this);
    ASSERT(pastexec);

    TRACE_MSG(TF_GENERAL, "Generating code...");

    cprocs = PAGetCount(this->hpaProcs);

    // Generate code for the main proc first.
    if (FindProc(this, "main", &ppdMain))
        {
        res = ProcDecl_Codegen(ppdMain, pastexec);
        if (RSUCCEEDED(res))
            {
            // Generate code for the rest of the procs
            for (i = 0; i < cprocs; i++)
                {
                PPROCDECL pprocdecl = PAFastGetPtr(this->hpaProcs, i);

                if (pprocdecl != ppdMain)
                    {
                    res = ProcDecl_Codegen(pprocdecl, pastexec);
                    if (RFAILED(res))
                        break;
                    }
                }
            }
        }
    else
        {
        // Typechecking should have guaranteed that the main
        // proc was here
        ASSERT(0);
        res = RES_E_FAIL;
        }

    return res;
    }