/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Copyright (c) 1989 Microsoft Corporation

 Module Name:
	
	exprpr.cxx

 Abstract:

	expression evaluator print routines implementation.

 Notes:


 History:

 	VibhasC		Aug-05-1993		Created

 ----------------------------------------------------------------------------*/

/****************************************************************************
 *	include files
 ***************************************************************************/
#include "nulldefs.h"

extern "C"
	{
	#include <stdio.h>
	#include <assert.h>
	#include <string.h>
	}

#include "expr.hxx"
#include "nodeskl.hxx"

/****************************************************************************
 *	extern definitions
 ***************************************************************************/

extern char * OperatorToString( OPERATOR Op );

/***************************************************************************/

void
expr_node::PrintWithPrefix(
    ISTREAM *   pStream,
    char *      pPrefix )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

    This routine prints an expression adding a prefix to each of the varaibles
    within the expression.

 Arguments:

 	pStream		- A pointer to the stream to output to.
    pPrefix     - the prefix to be prepended to each variable
	
 Return Value:
	
 Notes:

----------------------------------------------------------------------------*/
{
    ITERATOR  VarList;
    expr_variable * pVarNode;

    short VarCount = MakeListOfVars( VarList );
    if ( VarCount )
        {

        VarList.Init();
        while ( ITERATOR_GETNEXT( VarList, pVarNode ) )
            pVarNode->SetPrefix( pPrefix );
        }

    Print( pStream );

    if ( VarCount )
        {
        VarList.Init();
        while ( ITERATOR_GETNEXT( VarList, pVarNode ) )
            pVarNode->SetPrefix( NULL );
        }
}



/*
//  table of precedences ( lower number => lower precedence )
 200	[] () . -> ()++ ()--
 190	++() --() sizeof() & *(deref) +() -() ~() !()
 180	(cast)
 170	* / %
 160	+ -
 150	<< >>
 140	< > <= >=
 130	== !=
 120	& (bitwise)
 110	^
 100	|
 90		&&
 80		||
 70		?:
 60		= *= /= %= += -= <<= >>= &= |= ^=
 50		, (seqential eval)
 0		all other operators (should be none)
 */

const short Prec[] =
	{
	0	// OP_ILLEGAL = OP_START

	// OP_UNARY_START

	// OP_UNARY_ARITHMETIC_START	= OP_UNARY_START
	,190	// OP_UNARY_PLUS 				= OP_UNARY_ARITHMETIC_START
	,190	// OP_UNARY_MINUS
	// OP_UNARY_ARITHMETIC_END

	// OP_UNARY_LOGICAL_START		= OP_UNARY_ARITHMETIC_END
	,190	// OP_UNARY_NOT				= OP_UNARY_LOGICAL_START
	,190	// OP_UNARY_COMPLEMENT
	// OP_UNARY_LOGICAL_END

	,190	// OP_UNARY_INDIRECTION		= OP_UNARY_LOGICAL_END
	,180	// OP_UNARY_CAST
	,190	// OP_UNARY_AND
	,190	// OP_UNARY_SIZEOF
	,190	// OP_PRE_INCR
	,190	// OP_PRE_DECR
	,200	// OP_POST_INCR
	,200	// OP_POST_DECR

	// OP_UNARY_END

	// OP_BINARY_START			= OP_UNARY_END

	// OP_BINARY_ARITHMETIC_START	= OP_BINARY_START
	,160	// OP_PLUS					= OP_BINARY_ARITHMETIC_START
	,160	// OP_MINUS
	,170	// OP_STAR
	,170	// OP_SLASH
	,170	// OP_MOD
	// OP_BINARY_ARITHMETIC_END

	// OP_BINARY_SHIFT_START		= OP_BINARY_ARITHMETIC_END
	,150	// OP_LEFT_SHIFT				= OP_BINARY_SHIFT_START
	,150	// OP_RIGHT_SHIFT
	// OP_BINARY_SHIFT_END

	// OP_BINARY_RELATIONAL_START	= OP_BINARY_SHIFT_END
	,140	// OP_LESS					= OP_BINARY_RELATIONAL_START
	,140	// OP_LESS_EQUAL
	,140	// OP_GREATER_EQUAL
	,140	// OP_GREATER
	,130	// OP_EQUAL
	,130	// OP_NOT_EQUAL
	// OP_BINARY_RELATIONAL_END

	// OP_BINARY_BITWISE_START	= OP_BINARY_RELATIONAL_END
	,120	// OP_AND						= OP_BINARY_BITWISE_START
	,100	// OP_OR
	,110	// OP_XOR
	// OP_BINARY_BITWISE_END

	// OP_BINARY_LOGICAL_START	= OP_BINARY_BITWISE_END
	,90		// OP_LOGICAL_AND				= OP_BINARY_LOGICAL_START
	,80		// OP_LOGICAL_OR
	// OP_BINARY_LOGICAL_END

	// OP_BINARY_TERNARY_START	= OP_BINARY_LOGICAL_END
	,70		// OP_QM						= OP_BINARY_TERNARY_START
	,70		// OP_COLON
	// OP_BINARY_TERNARY_END

	// OP_BINARY_END				= OP_BINARY_TERNARY_END

	,0		// OP_INTERNAL_START			= OP_BINARY_END
	,200	// OP_FUNCTION
	,0		// OP_PARAM

	,200	// OP_POINTSTO
	,200	// OP_DOT
	,200	// OP_INDEX
	,50		// OP_COMMA
	,0		// OP_STMT
	,60		// OP_ASSIGN
	
	,0		// OP_END
	};


/*
//  table of associativity ( -1 => L to R, 1 => R to L )
 -1	[] () . -> ()++ ()--
 1	++() --() sizeof() & *(deref) +() -() ~() !()
 1	(cast)
 -1	* / %
 -1	+ -
 -1	<< >>
 -1	< > <= >=
 -1	== !=
 -1	& (bitwise)
 -1	^
 -1	|
 -1		&&
 -1		||
 1		?:
 1		= *= /= %= += -= <<= >>= &= |= ^=
 -1		, (seqential eval)
 0		all other operators (should be none)
 */

const short AssocTbl[] =
	{
	0	// OP_ILLEGAL = OP_START

	// OP_UNARY_START

	// OP_UNARY_ARITHMETIC_START	= OP_UNARY_START
	,-1	// OP_UNARY_PLUS 				= OP_UNARY_ARITHMETIC_START
	,-1	// OP_UNARY_MINUS
	// OP_UNARY_ARITHMETIC_END

	// OP_UNARY_LOGICAL_START		= OP_UNARY_ARITHMETIC_END
	,1	// OP_UNARY_NOT				= OP_UNARY_LOGICAL_START
	,1	// OP_UNARY_COMPLEMENT
	// OP_UNARY_LOGICAL_END

	,1	// OP_UNARY_INDIRECTION		= OP_UNARY_LOGICAL_END
	,1	// OP_UNARY_CAST
	,1	// OP_UNARY_AND
	,1	// OP_UNARY_SIZEOF
	,1	// OP_PRE_INCR
	,1	// OP_PRE_DECR
	,-1	// OP_POST_INCR
	,-1	// OP_POST_DECR

	// OP_UNARY_END

	// OP_BINARY_START			= OP_UNARY_END

	// OP_BINARY_ARITHMETIC_START	= OP_BINARY_START
	,-1	// OP_PLUS					= OP_BINARY_ARITHMETIC_START
	,-1	// OP_MINUS
	,-1	// OP_STAR
	,-1	// OP_SLASH
	,-1	// OP_MOD
	// OP_BINARY_ARITHMETIC_END

	// OP_BINARY_SHIFT_START		= OP_BINARY_ARITHMETIC_END
	,-1	// OP_LEFT_SHIFT				= OP_BINARY_SHIFT_START
	,-1	// OP_RIGHT_SHIFT
	// OP_BINARY_SHIFT_END

	// OP_BINARY_RELATIONAL_START	= OP_BINARY_SHIFT_END
	,-1	// OP_LESS					= OP_BINARY_RELATIONAL_START
	,-1	// OP_LESS_EQUAL
	,-1	// OP_GREATER_EQUAL
	,-1	// OP_GREATER
	,-1	// OP_EQUAL
	,-1	// OP_NOT_EQUAL
	// OP_BINARY_RELATIONAL_END

	// OP_BINARY_BITWISE_START	= OP_BINARY_RELATIONAL_END
	,-1	// OP_AND						= OP_BINARY_BITWISE_START
	,-1	// OP_OR
	,-1	// OP_XOR
	// OP_BINARY_BITWISE_END

	// OP_BINARY_LOGICAL_START	= OP_BINARY_BITWISE_END
	,-1		// OP_LOGICAL_AND				= OP_BINARY_LOGICAL_START
	,-1		// OP_LOGICAL_OR
	// OP_BINARY_LOGICAL_END

	// OP_BINARY_TERNARY_START	= OP_BINARY_LOGICAL_END
	,1		// OP_QM						= OP_BINARY_TERNARY_START
	,1		// OP_COLON
	// OP_BINARY_TERNARY_END

	// OP_BINARY_END				= OP_BINARY_TERNARY_END

	,0		// OP_INTERNAL_START			= OP_BINARY_END
	,0		// OP_FUNCTION
	,0		// OP_PARAM

	,-1	// OP_POINTSTO
	,-1	// OP_DOT
	,-1	// OP_INDEX
	,-1		// OP_COMMA
	,0		// OP_STMT
	,1		// OP_ASSIGN
	
	,0		// OP_END
	};

void
expr_operator::PrintSubExpr(
	expr_node	* pExpr,
	ISTREAM	*	pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Print a subexpression, optionally adding parens.

 Arguments:

	pExpr		- the expression to print
 	pStream		- A pointer to the stream to output to.
	
 Return Value:
	
 Notes:

----------------------------------------------------------------------------*/
{
	short	PrecMe		= Prec[ GetOperator() ];
	short	PrecChild;
	BOOL	fAddParens	= FALSE;
	
	if ( pExpr->IsOperator() )
		{
		PrecChild = Prec[ pExpr->GetOperator() ];
		// account for associativity
		if ( pExpr != GetLeft() )
			PrecChild += AssocTbl[ pExpr->GetOperator() ];

		fAddParens = PrecChild < PrecMe;
		}
		
	if ( fAddParens )
		pStream->Write('(');

	pExpr->Print( pStream );

	if ( fAddParens )
		pStream->Write(')');

}


void
expr_variable::Print(
	ISTREAM	*	pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Print a variable name expression.

 Arguments:

 	pStream		- A pointer to the stream to output to.
	
 Return Value:
	
 Notes:

----------------------------------------------------------------------------*/
{
    if ( GetPrefix() )
    	pStream->Write( GetPrefix() );
	pStream->Write( GetName() );
}

char	*	ConstFormats[]	=
	{
	"\"%s\"",		// string
	"L\"%Ls\"",		// wstring
	"'%c'",			// char
	"L'%Lc'",		// wchar

	"%d",			// numeric
	"%uU",			// numeric unsigned
	"%ldL",			// numeric long
	"%luUL",		// numeric unsigned long

	"%#x",			// hex
	"%#xU",			// hex unsigned
	"%#lxL",		// hex long
	"%#lxUL",		// hex unsigned long

	"%#o",			// octal
	"%#oU",			// octal unsigned 
	"%#loL",		// octal long
	"%#loUL",		// octal unsigned long

	"%s",			// BOOL ( not used )

	"%gF",			// float
	"%lg",			// double
	};

void
expr_constant::Print(
	ISTREAM	*	pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

	Emit a constant expression to the stream.

 Arguments:
	
	pStream	- A pointer to the stream.

 Return Value:
	
 Notes:

----------------------------------------------------------------------------*/
{
	char Array[ 256 ];

	if (Format != VALUE_TYPE_BOOL)
		sprintf( Array, ConstFormats[ Format] , Value.L );
	else
		sprintf( Array, "%s", (Value.L) ? "TRUE" : "FALSE" );

	pStream->Write( (char *)Array );
}

void
expr_op_unary::Print(
	ISTREAM	*	pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

	Emit a unary expression to the stream.

 Arguments:
	
	pStream	- A pointer to the stream.

 Return Value:
	
 Notes:

----------------------------------------------------------------------------*/
{
	OPERATOR	Op	= GetOperator();
	char		ch;

	switch( Op )
		{
		case OP_UNARY_PLUS:			ch = '+'; break;
		case OP_UNARY_MINUS:		ch = '-'; break;
		case OP_UNARY_NOT:			ch = '!'; break;
		case OP_UNARY_COMPLEMENT:	ch = '~'; break;
		case OP_UNARY_INDIRECTION:	ch = '*'; break;
		case OP_UNARY_AND:			ch = '&'; break;
		default:					ch = 'X'; break;
		}

	pStream->Write( ch );
	PrintSubExpr( GetLeft(), pStream );
}

void
expr_cast::Print(
	ISTREAM * pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Emit a cast expression.

 Arguments:
 	
 	pStream	- A pointer to the stream.
	
 Return Value:

 	None.
	
 Notes:

 	Not implemented for now.

----------------------------------------------------------------------------*/
{
	GetType()->PrintType( (PRT_CAST),
					   pStream,
					   (node_skl *)0
					);
	PrintSubExpr( GetLeft(), pStream );

}

void
expr_sizeof::Print(
	ISTREAM * pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Emit a sizeof expression.

 Arguments:
 	
 	pStream	- A pointer to the stream.
	
 Return Value:

 	None.
	
 Notes:

 	Not implemented for now.

----------------------------------------------------------------------------*/
{
	pStream->Write( "sizeof" );
	pType->PrintType( (PRT_CAST),
					   pStream,
					   (node_skl *)0
					);
}
void
expr_pre_incr::Print(
	ISTREAM * pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Emit a pre-incr expression.

 Arguments:
 	
 	pStream	- A pointer to the stream.
	
 Return Value:

 	None.
	
 Notes:

 	Not implemented for now.

----------------------------------------------------------------------------*/
{
	pStream->Write("++");
	PrintSubExpr( GetLeft(), pStream );
}

void
expr_pre_decr::Print(
	ISTREAM * pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Emit a pre-decrement expression.

 Arguments:
 	
 	pStream	- A pointer to the stream.
	
 Return Value:

 	None.
	
 Notes:

 	Not implemented for now.

----------------------------------------------------------------------------*/
{
	pStream->Write("--");
	PrintSubExpr( GetLeft(), pStream );
}

void
expr_post_incr::Print(
	ISTREAM * pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Emit a post-increment expression.

 Arguments:
 	
 	pStream	- A pointer to the stream.
	
 Return Value:

 	None.
	
 Notes:

 	Not implemented for now.

----------------------------------------------------------------------------*/
{
	PrintSubExpr( GetLeft(), pStream );
	pStream->Write("++");
}

void
expr_post_decr::Print(
	ISTREAM * pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Emit a post-decrement expression.

 Arguments:
 	
 	pStream	- A pointer to the stream.
	
 Return Value:

 	None.
	
 Notes:

 	Not implemented for now.

----------------------------------------------------------------------------*/
{
	PrintSubExpr( GetLeft(), pStream );
	pStream->Write("--");
}

void
expr_op_binary::Print(
	ISTREAM * pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Emit a binary arithmetic expression.

 Arguments:
 	
 	pStream	- A pointer to the stream.
	
 Return Value:

 	None.
	
 Notes:

 	Not implemented for now.

----------------------------------------------------------------------------*/
{
	PrintSubExpr( GetLeft(), pStream );

	pStream->Write( OperatorToString( GetOperator() ) );

	PrintSubExpr( GetRight(), pStream );

}
void
expr_op_binary::PrintCall(
	ISTREAM	*	pStream,
	short		LeftMargin,
	BOOL		fInProc )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

	Emit expression as part of a procedure call.

 Arguments:

 	pStream	- A pointer to the stream.
 	LeftMargin	- The start margin for the procedure call.
 	fInProc	- Is it in a proc context already ?
	
 Return Value:
	
 	None.

 Notes:

	The left margin is 0 if the call is the only thing on the line. If the
	call is in an assignment, the start of param indent must take that into
	account.
----------------------------------------------------------------------------*/
{
	expr_node	*	pN;
	if( pN = GetLeft() )
		pN->PrintCall( pStream, LeftMargin, fInProc );

	pStream->Write( OperatorToString( GetOperator() ) );

	if( pN = GetRight() )
		pN->PrintCall( pStream, LeftMargin, fInProc );
}
void
expr_assign::PrintCall(
	ISTREAM	*	pStream,
	short		LeftMargin,
	BOOL		fInProc )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

	Emit expression as part of a procedure call.

 Arguments:

 	pStream	- A pointer to the stream.
 	LeftMargin	- The start margin for the procedure call.
 	fInProc	- Is it in a proc context already ?
	
 Return Value:
	
 	None.

 Notes:

	The left margin is 0 if the call is the only thing on the line. If the
	call is in an assignment, the start of param indent must take that into
	account.
----------------------------------------------------------------------------*/
{
	expr_node	*	pN;
	if( pN = GetLeft() )
		pN->PrintCall( pStream, LeftMargin, fInProc );

	pStream->Write( " = " );

	if( pN = GetRight() )
		pN->PrintCall( pStream, LeftMargin, fInProc );
}

void
expr_index::Print(
	ISTREAM	*	pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

	Emit expression for the array index operator.

 Arguments:

 	pStream	- A pointer to the stream.
	
 Return Value:
	
 	None.

 Notes:

----------------------------------------------------------------------------*/
{

	PrintSubExpr( GetLeft(), pStream );
	pStream->Write( '[' );
	GetRight()->Print( pStream );
	pStream->Write( ']' );
}
void
expr_index::PrintCall(
	ISTREAM	*	pStream,
	short		LeftMargin,
	BOOL		fInProc )
	{
	GetLeft()->PrintCall( pStream, LeftMargin, fInProc );
	pStream->Write( '[' );
	GetRight()->PrintCall( pStream, LeftMargin, fInProc );
	pStream->Write( ']' );
	}

void
expr_proc_call::PrintCall(
	ISTREAM	*	pStream,
	short		LeftMargin,
	BOOL		fInProc )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

	Emit expression for a procedure call.

 Arguments:

 	pStream	- A pointer to the stream.
 	LeftMargin	- The start margin for the procedure call.
 	fInProc	- Is it in a proc context already ?
	
 Return Value:
	
 	None.

 Notes:

	The left margin is 0 if the call is the only thing on the line. If the
	call is in an assignment, the start of param indent must take that into
	account.
----------------------------------------------------------------------------*/
{
	PNAME	pName = GetName();
	unsigned short CurPref	= pStream->GetPreferredIndent();
	unsigned short CurIndent= pStream->GetIndent();

	//
	// Print fancy only if more than 2 params.
	//

	if( GetNoOfParams() < 3 )
		{
		Print( pStream );
		if( !fInProc )
			pStream->Write(';');
		return;
		}

	pStream->Write( GetName() );

	pStream->Write( '(' );
	pStream->SetPreferredIndent(
						 LeftMargin+CurPref+strlen((const char *)pName ) - CurIndent
							   );
	pStream->IndentInc();
	pStream->NewLine();
	if( GetFirstParam())
		{
		GetFirstParam()->PrintCall( pStream,
								    LeftMargin,
								    TRUE // now in proc context
								  );
		}

	pStream->Write( ')' );
	if( !fInProc )
		pStream->Write( ';' );
	pStream->IndentDec();
	pStream->SetPreferredIndent( CurPref );
}

void
expr_proc_call::Print(
	ISTREAM	*	pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

	Emit expression for a procedure call.

 Arguments:

 	pStream	- A pointer to the stream.
	
 Return Value:
	
 	None.

 Notes:

----------------------------------------------------------------------------*/
{
	pStream->Write( GetName() );
	pStream->Write( '(' );
	if( GetFirstParam())
		GetFirstParam()->Print( pStream );
	pStream->Write( ')' );
}

void
expr_param::Print(
	ISTREAM	*	pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

	Emit expression for a parameter.

 Arguments:

 	pStream	- A pointer to the stream.
	
 Return Value:
	
 	None.

 Notes:

----------------------------------------------------------------------------*/
{
	expr_node * pNextParam;

	GetLeft()->Print( pStream );

	if( pNextParam = GetNextParam() )
		{
		pStream->Write( ',' );
//		pStream->NewLine();
		pNextParam->Print( pStream );
		}
}

void
expr_param::PrintCall(
	ISTREAM	*	pStream,
	short		LeftMargin,
	BOOL		fInProc )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

	Emit expression as part of a procedure call.

 Arguments:

 	pStream	- A pointer to the stream.
 	LeftMargin	- The start margin for the procedure call.
 	fInProc	- Is it in a proc context already ?
 	pStream	- A pointer to the stream.
	
 Return Value:
	
 	None.

 Notes:

----------------------------------------------------------------------------*/
{
	expr_node * pNextParam;

	GetLeft()->PrintCall( pStream, LeftMargin, fInProc );

	if( pNextParam = GetNextParam() )
		{
		pStream->Write( ',' );
		pStream->NewLine();
		pNextParam->PrintCall( pStream, LeftMargin, fInProc );
		}
}

void
expr_ternary::Print(
	ISTREAM * pStream )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Emit a ternary expression.

 Arguments:
 	
 	pStream	- A pointer to the stream.
	
 Return Value:

 	None.
	
 Notes:

 	Not implemented for now.

----------------------------------------------------------------------------*/
{
	PrintSubExpr( GetRelational(), pStream );
	pStream->Write( " ? " );
	PrintSubExpr( GetLeft(), pStream );
	pStream->Write( " : " );
	PrintSubExpr( GetRight(), pStream );
}
/****************************************************************************
 	utilities
 ****************************************************************************/
char *
OperatorToString(
	OPERATOR	Op )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 Routine Description:

 	Translate the operator to its string.

 Arguments:
	
	Op	- The operator value.

 Return Value:
	
 Notes:

----------------------------------------------------------------------------*/
{
    char *p;

    switch( Op )
        {
        case OP_PLUS:           p = " + ";  break;
        case OP_MINUS:          p = " - ";  break;
        case OP_STAR:           p = " * ";  break;
        case OP_SLASH:          p = " / ";  break;
        case OP_MOD:            p = " % ";  break;
        case OP_LEFT_SHIFT:     p = " << "; break;
        case OP_RIGHT_SHIFT:    p = " >> "; break;
        case OP_LESS:           p = " < ";  break;
        case OP_LESS_EQUAL:     p = " <= "; break;
        case OP_GREATER_EQUAL:  p = " >= "; break;
        case OP_GREATER:        p = " > ";  break;
        case OP_EQUAL:          p = " == "; break;
        case OP_NOT_EQUAL:      p = " != "; break;
        case OP_AND:            p = " & ";  break;
        case OP_OR:             p = " | ";  break;
        case OP_XOR:            p = " ^ ";  break;
        case OP_LOGICAL_AND:    p = " && "; break;
        case OP_LOGICAL_OR:     p = " || "; break;
        case OP_QM:             p = " ? ";  break;
        case OP_COLON:          p = " : ";  break;
        case OP_ASSIGN:         p = " = ";  break;
        case OP_DOT:            p = " . ";  break;
        case OP_POINTSTO:       p = " -> "; break;
        case OP_COMMA:          p = " , ";  break;
        case OP_UNARY_NOT:      p = " ! ";  break;
        default:                p = " X ";  break;
        }

    return p;
}