/* $Header: d:/cvsroot/tads/tads3/TCT3INT.H,v 1.3 1999/07/11 00:46:57 MJRoberts Exp $ */

/* 
 *   Copyright (c) 1999, 2002 Michael J. Roberts.  All Rights Reserved.
 *   
 *   Please see the accompanying license file, LICENSE.TXT, for information
 *   on using and copying this software.  
 */
/*
Name
  tct3int.h - T3-specific intermediate parse node classes
Function
  
Notes
  
Modified
  05/12/99 MJRoberts  - Creation
*/

#ifndef TCT3INT_H
#define TCT3INT_H

/* include our T3-specific base classes */
#include "tct3base.h"

/* include the target-independent intermediate classes */
#include "tcpnint.h"

/* ------------------------------------------------------------------------ */
/*
 *   Unary Operator Expression node 
 */
class CTPNUnary: public CTPNUnaryBase
{
public:
    CTPNUnary(CTcPrsNode *subexpr)
        : CTPNUnaryBase(subexpr) { }

    /*
     *   Generate code for a unary operator with no side effects.  We'll
     *   generate code for the subexpression, then generate the given
     *   opcode.
     *   
     *   If 'discard' is true, we won't generate the opcode.  We know the
     *   opcode has no side effects, so if the result of the calculation
     *   isn't going to be used, there's no point in calculating it in the
     *   first place.  
     */
    void gen_unary(uchar opc, int discard, int for_condition);
};


/* ------------------------------------------------------------------------ */
/*
 *   Binary Operator Expression node 
 */
class CTPNBin: public CTPNBinBase
{
public:
    CTPNBin(CTcPrsNode *lhs, CTcPrsNode *rhs)
        : CTPNBinBase(lhs, rhs) { }

    /*
     *   Generate code for a binary operator with no side effects.  We'll
     *   generate code for the left operand, then for the right operand,
     *   then generate the opcode.
     *   
     *   If 'discard' is true, we won't generate the opcode.  We know that
     *   the opcode has no side effects, so if the result of the
     *   calculation isn't needed, we need not apply the opcode; we simply
     *   want to evaluate the subexpressions for any side effects they
     *   might have.  
     */
    void gen_binary(uchar opc, int discard, int for_condition);
};

/* ------------------------------------------------------------------------ */
/*
 *   generic statement 
 */
class CTPNStm: public CTPNStmBase
{
public:
    /* initialize at the tokenizer's current source file position */
    CTPNStm() : CTPNStmBase() { }

    /* initialize at the given source position */
    CTPNStm(class CTcTokFileDesc *file, long linenum)
        : CTPNStmBase(file, linenum) { }

    /*
     *   Generate code for a labeled 'continue'.  These are called when
     *   this statement is labeled, and the same label is used in a
     *   'continue' statement.
     *   
     *   Returns true if successful, false if not.  This should return
     *   false if the statement is not suitable for a 'continue'.  By
     *   default, we simply return false, since most statements cannot be
     *   used as the target of a 'continue'.  
     */
    virtual int gen_code_labeled_continue() { return FALSE; }
};

/* ------------------------------------------------------------------------ */
/*
 *   Enclosing Statement class: this is the base class for statements that
 *   have special needs for exiting due to break, continue, goto, and
 *   return.  Refer to tcpnint.h for details.  
 */
class CTPNStmEnclosing: public CTPNStm
{
public:
    CTPNStmEnclosing(CTPNStmEnclosing *enclosing)
    {
        /* remember the statement that encloses me */
        enclosing_ = enclosing;
    }

    /* get the enclosing statement */
    CTPNStmEnclosing *get_enclosing() const { return enclosing_; }

    /* 
     *   Generate code for a 'break' to a given label, or an unlabeled
     *   'break' if lbl is null.  Returns true if the break was fully
     *   processed, false if not.  If any work is necessary to unwind the
     *   exception stack to break out of this block, that should be done
     *   here.  If this node doesn't actually accomplish the break, it
     *   should recursively invoke the next enclosing statement's routine
     *   to do the work.
     *   
     *   By default, we'll simply invoke the enclosing handler, if there
     *   is one, or return failure (i.e., false) if not.  
     */
    virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len)
    {
        if (enclosing_)
            return enclosing_->gen_code_break(lbl, lbl_len);
        else
            return 0;
    }

    /*
     *   Generate code for a 'continue' to a given label, or an unlabeled
     *   'continue' if lbl is null.  Same rules as gen_code_break().  
     *   
     *   By default, we'll simply invoke the enclosing handler, if there
     *   is one, or return failure (i.e., false) if not.  
     */
    virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len)
    {
        if (enclosing_)
            return enclosing_->gen_code_continue(lbl, lbl_len);
        else
            return 0;
    }

    /*
     *   Generate the code necessary to unwind the stack for returning
     *   through this enclosing statement.  This should generate any code
     *   necessary (such as a call to a 'finally' block, for a 'try'
     *   statement), then call the next enclosing statement to do the same
     *   thing.  By default, this does nothing except invoke the enclosing
     *   statement, since most enclosing statements don't need to generate
     *   any code to handle a 'return' through them.  
     */
    virtual void gen_code_unwind_for_return()
    {
        /* if there's an enclosing statement, ask it to unwind itself */
        if (enclosing_ != 0)
            enclosing_->gen_code_unwind_for_return();
    }

    /*
     *   Determine if we'll generate any code to unwind the stack for
     *   returning through this enclosing statement.  This should return
     *   true if any code will be generated by a call to
     *   gen_code_unwind_for_return().  By default, we'll return whatever
     *   our enclosing statement does.  
     */
    virtual int will_gen_code_unwind_for_return() const
    {
        /* 
         *   return what the enclosing statement does, or false if we're
         *   the outermost statement 
         */
        return (enclosing_ != 0
                ? enclosing_->will_gen_code_unwind_for_return()
                : FALSE);
    }

    /*
     *   Generate the code necessary to unwind the stack for executing a
     *   'goto' to the given label statement.  We first determine if the
     *   target label is contained within this statement; if it is,
     *   there's nothing we need to do, because we're transferring control
     *   within the same enclosing statement.
     *   
     *   If control is being transferred outside of this statement (i.e.,
     *   the label is not contained within this statement), we must
     *   generate the necessary code to leave the block; for example, a
     *   'try' block must generate a call to its 'finally' block.  We must
     *   then invoke this same function on our enclosing statement to let
     *   it do the same work.
     */
    void gen_code_unwind_for_goto(class CTPNStmGoto *goto_stm,
                                  class CTPNStmLabel *target);

    /* 
     *   Generate code for transferring control out of this statement.  We
     *   invoke this from gen_code_unwind_for_goto() when we determine
     *   that the 'goto' target is not enclosed within this statement.  By
     *   default we do nothing; statements such as 'try' that must do work
     *   on transferring control must override this to generate the
     *   appropriate code. 
     */
    virtual void gen_code_for_transfer_out() { }

    /*
     *   Check to see if we are allowed to transfer in to this block via
     *   'goto' statements.  If so, simply return TRUE.  If we don't allow
     *   this type of transfer, we should log an appropriate error, then
     *   return FALSE.  Note that the function should both log an error
     *   and return FALSE if the transfer isn't allowed; the return value
     *   allows the caller to ensure that only one error is displayed for
     *   this type of problem even if the target label is nested within
     *   several levels of blocks that don't allow transfers in.
     *   
     *   By default, we'll simply return TRUE, since most block types
     *   allow this type of transfer.  
     */
    virtual int check_enter_by_goto(class CTPNStmGoto * /*goto_stm*/,
                                    class CTPNStmLabel * /*target*/)
        { return TRUE; }

protected:
    /* 
     *   generate code for a break - this can be used as a service routine
     *   by loop/switch statements 
     */
    int gen_code_break_loop(CTcCodeLabel *code_label,
                            const textchar_t *lbl, size_t lbl_len);
    
    /* 
     *   generate code for a continue - this can be used as a service
     *   routine by loop/switch statements 
     */
    int gen_code_continue_loop(CTcCodeLabel *code_label,
                               const textchar_t *lbl, size_t lbl_len);
    
  
    /* the block enclosing this block */
    CTPNStmEnclosing *enclosing_;
};

#endif /* TCT3INT_H */

