/* ASTPrintExpr.c */
/*****************************************************************************/
/*                                                                           */
/*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
/*    Copyright (C) 1994  Thomas R. Lawrence                                 */
/*                                                                           */
/*    This program is free software; you can redistribute it and/or modify   */
/*    it under the terms of the GNU General Public License as published by   */
/*    the Free Software Foundation; either version 2 of the License, or      */
/*    (at your option) any later version.                                    */
/*                                                                           */
/*    This program is distributed in the hope that it will be useful,        */
/*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
/*    GNU General Public License for more details.                           */
/*                                                                           */
/*    You should have received a copy of the GNU General Public License      */
/*    along with this program; if not, write to the Free Software            */
/*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                           */
/*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
/*                                                                           */
/*****************************************************************************/

#include "MiscInfo.h"
#include "Audit.h"
#include "Debug.h"
#include "Definitions.h"

#include "ASTPrintExpr.h"
#include "TrashTracker.h"
#include "Memory.h"
#include "ASTExpression.h"
#include "PromotableTypeCheck.h"


struct ASTPrintExprRec
	{
		ASTExpressionRec*		Value;
		long								LineNumber;
		DataTypes						ExpressionType;
	};


/* create a new AST expression print */
ASTPrintExprRec*		NewPrintExpr(struct ASTExpressionRec* Expression,
											struct TrashTrackRec* TrashTracker, long LineNumber)
	{
		ASTPrintExprRec*	PrintExpr;

		CheckPtrExistence(Expression);
		PrintExpr = (ASTPrintExprRec*)AllocTrackedBlock(sizeof(ASTPrintExprRec),TrashTracker);
		if (PrintExpr == NIL)
			{
				return NIL;
			}
		SetTag(PrintExpr,"ASTPrintExprRec");

		PrintExpr->Value = Expression;
		PrintExpr->LineNumber = LineNumber;

		return PrintExpr;
	}


/* type check the expr print node.  this returns eCompileNoError if */
/* everything is ok, and the appropriate type in *ResultingDataType. */
CompileErrors				TypeCheckPrintExpr(DataTypes* ResultingDataType,
											ASTPrintExprRec* PrintExpr, long* ErrorLineNumber,
											struct TrashTrackRec* TrashTracker)
	{
		CompileErrors			Error;

		CheckPtrExistence(PrintExpr);
		CheckPtrExistence(TrashTracker);

		Error = TypeCheckExpression(&(PrintExpr->ExpressionType),PrintExpr->Value,
			ErrorLineNumber,TrashTracker);
		if (Error != eCompileNoError)
			{
				return Error;
			}

		switch (PrintExpr->ExpressionType)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"TypeCheckPrintExpr:  unknown expression type"));
					break;
				case eBoolean:
					break;
				case eDouble:
				case eInteger:
				case eFloat:
				case eFixed:
					if (!CanRightBeMadeToMatchLeft(eDouble,PrintExpr->ExpressionType))
						{
							*ErrorLineNumber = PrintExpr->LineNumber;
							return eCompileOperandMustBeDouble;
						}
					if (MustRightBePromotedToLeft(eDouble,PrintExpr->ExpressionType))
						{
							ASTExpressionRec*		PromotedOperand;

							/* we must promote the right operand to become the left operand type */
							PromotedOperand = PromoteTheExpression(PrintExpr->ExpressionType/*orig*/,
								eDouble/*desired*/,PrintExpr->Value,PrintExpr->LineNumber,TrashTracker);
							if (PromotedOperand == NIL)
								{
									*ErrorLineNumber = PrintExpr->LineNumber;
									return eCompileOutOfMemory;
								}
							PrintExpr->Value = PromotedOperand;
							/* sanity check */
							Error = TypeCheckExpression(&(PrintExpr->ExpressionType)/*obtain new type*/,
								PrintExpr->Value,ErrorLineNumber,TrashTracker);
							ERROR((Error != eCompileNoError),PRERR(ForceAbort,
								"TypeCheckPrintExpr:  type promotion caused failure"));
							ERROR(eDouble != PrintExpr->ExpressionType,PRERR(ForceAbort,
								"TypeCheckPrintExpr:  after type promotion, types are no"
								" longer the same"));
						}
					break;
				case eArrayOfBoolean:
				case eArrayOfInteger:
				case eArrayOfFloat:
				case eArrayOfDouble:
				case eArrayOfFixed:
					*ErrorLineNumber = PrintExpr->LineNumber;
					return eCompileOperandsMustBeScalar;
			}

		*ResultingDataType = eBoolean;
		return eCompileNoError;
	}


/* generate code for an expr print.  returns True if successful, or False if it fails. */
MyBoolean						CodeGenPrintExpr(struct PcodeRec* FuncCode,
											long* StackDepthParam, ASTPrintExprRec* PrintExpr)
	{
		long							StackDepth;

		CheckPtrExistence(FuncCode);
		CheckPtrExistence(PrintExpr);
		StackDepth = *StackDepthParam;

		/* evaluate the operand */
		if (!CodeGenExpression(FuncCode,&StackDepth,PrintExpr->Value))
			{
				return False;
			}
		ERROR(StackDepth != *StackDepthParam + 1,PRERR(ForceAbort,
			"CodeGenPrintExpr:  stack depth error evaluating operand"));

		/* generate operation */
		switch (PrintExpr->ExpressionType)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"CodeGenPrintExpr:  unknown type"));
					break;
				case eBoolean:
					if (!AddPcodeInstruction(FuncCode,epPrintBool,NIL))
						{
							return False;
						}
					break;
				case eDouble:
					if (!AddPcodeInstruction(FuncCode,epPrintDouble,NIL))
						{
							return False;
						}
					break;
			}
		StackDepth -= 1;

		/* return value */
		if (!AddPcodeInstruction(FuncCode,epLoadImmediateInteger,NIL))
			{
				return False;
			}
		if (!AddPcodeOperandInteger(FuncCode,True))
			{
				return False;
			}
		StackDepth += 1;
		ERROR(StackDepth != *StackDepthParam + 1,PRERR(ForceAbort,
			"CodeGenPrintExpr:  stack depth error after pushing return value"));

		*StackDepthParam = StackDepth;
		return True;
	}
