/* ASTExpression.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 "ASTExpression.h"
#include "TrashTracker.h"
#include "Memory.h"
#include "ASTArrayDeclaration.h"
#include "ASTAssignment.h"
#include "ASTBinaryOperator.h"
#include "ASTConditional.h"
#include "ASTExpressionList.h"
#include "ASTFuncCall.h"
#include "ASTLoop.h"
#include "ASTOperand.h"
#include "ASTUnaryOperator.h"
#include "ASTVariableDeclaration.h"
#include "ASTErrorForm.h"
#include "ASTWaveGetter.h"
#include "ASTPrintString.h"
#include "ASTPrintExpr.h"
#include "SymbolTableEntry.h"


struct ASTExpressionRec
	{
		ExprTypes						ElementType;
		DataTypes						WhatIsTheExpressionType;
		long								LineNumber;
		union
			{
				ASTArrayDeclRec*		ArrayDeclaration;
				ASTAssignRec*				Assignment;
				ASTBinaryOpRec*			BinaryOperator;
				ASTCondRec*					Conditional;
				ASTExprListRec*			ExpressionList;
				ASTFuncCallRec*			FunctionCall;
				ASTLoopRec*					Loop;
				ASTOperandRec*			Operand;
				ASTUnaryOpRec*			UnaryOperator;
				ASTVarDeclRec*			VariableDeclaration;
				ASTErrorFormRec*		ErrorForm;
				ASTWaveGetterRec*		WaveGetter;
				ASTPrintStringRec*	PrintString;
				ASTPrintExprRec*		PrintExpr;
			} u;
	};


/* construct a generic expression around an array declaration */
ASTExpressionRec*		NewExprArrayDecl(struct ASTArrayDeclRec* TheArrayDeclaration,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(TheArrayDeclaration);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprArrayDecl");

		Expr->ElementType = eExprArrayDeclaration;
		Expr->u.ArrayDeclaration = TheArrayDeclaration;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around an assignment statement */
ASTExpressionRec*		NewExprAssignment(struct ASTAssignRec* TheAssignment,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(TheAssignment);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprAssignment");

		Expr->ElementType = eExprAssignment;
		Expr->u.Assignment = TheAssignment;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around a binary operator */
ASTExpressionRec*		NewExprBinaryOperator(struct ASTBinaryOpRec* TheBinaryOperator,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(TheBinaryOperator);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprBinaryOperator");

		Expr->ElementType = eExprBinaryOperator;
		Expr->u.BinaryOperator = TheBinaryOperator;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around a conditional. */
ASTExpressionRec*		NewExprConditional(struct ASTCondRec* TheConditional,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(TheConditional);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprConditional");

		Expr->ElementType = eExprConditional;
		Expr->u.Conditional = TheConditional;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around a list of expressions. */
ASTExpressionRec*		NewExprSequence(struct ASTExprListRec* TheExpressionList,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		if (TheExpressionList != NIL)
			{
				CheckPtrExistence(TheExpressionList);
			}
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprSequence");

		Expr->ElementType = eExprExpressionList;
		Expr->u.ExpressionList = TheExpressionList;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around a function call */
ASTExpressionRec*		NewExprFunctionCall(struct ASTFuncCallRec* TheFunctionCall,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(TheFunctionCall);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprFunctionCall");

		Expr->ElementType = eExprFunctionCall;
		Expr->u.FunctionCall = TheFunctionCall;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around a loop */
ASTExpressionRec*		NewExprLoop(struct ASTLoopRec* TheLoop,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(TheLoop);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprLoop");

		Expr->ElementType = eExprLoop;
		Expr->u.Loop = TheLoop;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around an operand */
ASTExpressionRec*		NewExprOperand(struct ASTOperandRec* TheOperand,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(TheOperand);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprOperand");

		Expr->ElementType = eExprOperand;
		Expr->u.Operand = TheOperand;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around a unary operator */
ASTExpressionRec*		NewExprUnaryOperator(struct ASTUnaryOpRec* TheUnaryOperator,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(TheUnaryOperator);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprUnaryOperator");

		Expr->ElementType = eExprUnaryOperator;
		Expr->u.UnaryOperator = TheUnaryOperator;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around a variable declaration */
ASTExpressionRec*		NewExprVariableDeclaration(struct ASTVarDeclRec* TheVariableDecl,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(TheVariableDecl);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprVariableDeclaration");

		Expr->ElementType = eExprVariableDeclaration;
		Expr->u.VariableDeclaration = TheVariableDecl;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around an error form */
ASTExpressionRec*		NewExprErrorForm(struct ASTErrorFormRec* TheErrorForm,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(TheErrorForm);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprErrorForm");

		Expr->ElementType = eExprErrorForm;
		Expr->u.ErrorForm = TheErrorForm;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around a wave getter */
ASTExpressionRec*		NewExprWaveGetter(struct ASTWaveGetterRec* TheWaveGetter,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(TheWaveGetter);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprWaveGetter");

		Expr->ElementType = eExprWaveGetter;
		Expr->u.WaveGetter = TheWaveGetter;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around an error form */
ASTExpressionRec*		NewExprPrintString(struct ASTPrintStringRec* ThePrintString,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(ThePrintString);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprPrintString");

		Expr->ElementType = eExprPrintString;
		Expr->u.PrintString = ThePrintString;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* construct a generic expression around an error form */
ASTExpressionRec*		NewExprPrintExpr(struct ASTPrintExprRec* ThePrintExpr,
											struct TrashTrackRec* TrashTracker, long TheLineNumber)
	{
		ASTExpressionRec*	Expr;

		CheckPtrExistence(ThePrintExpr);
		CheckPtrExistence(TrashTracker);
		Expr = (ASTExpressionRec*)AllocTrackedBlock(sizeof(ASTExpressionRec),TrashTracker);
		if (Expr == NIL)
			{
				return NIL;
			}
		SetTag(Expr,"ASTExpressionRec: NewExprPrintExpr");

		Expr->ElementType = eExprPrintExpr;
		Expr->u.PrintExpr = ThePrintExpr;
		Expr->LineNumber = TheLineNumber;

		return Expr;
	}


/* type check an expression.  returns eCompileNoError and the resulting value */
/* type if it checks correctly. */
CompileErrors				TypeCheckExpression(DataTypes* ResultTypeOut,
											ASTExpressionRec* TheExpression, long* ErrorLineNumber,
											struct TrashTrackRec* TrashTracker)
	{
		CompileErrors			ReturnValue;

		CheckPtrExistence(TheExpression);
		CheckPtrExistence(TrashTracker);

		switch (TheExpression->ElementType)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"TypeCheckExpression:  unknown AST node type"));
					break;
				case eExprArrayDeclaration:
					ReturnValue = TypeCheckArrayConstruction(ResultTypeOut,
						TheExpression->u.ArrayDeclaration,ErrorLineNumber,TrashTracker);
					break;
				case eExprAssignment:
					ReturnValue = TypeCheckAssignment(ResultTypeOut,TheExpression->u.Assignment,
						ErrorLineNumber,TrashTracker);
					break;
				case eExprBinaryOperator:
					ReturnValue = TypeCheckBinaryOperator(ResultTypeOut,
						TheExpression->u.BinaryOperator,ErrorLineNumber,TrashTracker);
					break;
				case eExprConditional:
					ReturnValue = TypeCheckConditional(ResultTypeOut,TheExpression->u.Conditional,
						ErrorLineNumber,TrashTracker);
					break;
				case eExprExpressionList:
					/* this needs to be done specially */
					if (TheExpression->u.ExpressionList == NIL)
						{
							*ErrorLineNumber = TheExpression->LineNumber;
							ReturnValue = eCompileVoidExpressionIsNotAllowed;
						}
					 else
						{
							ReturnValue = TypeCheckExprList(ResultTypeOut,
								TheExpression->u.ExpressionList,ErrorLineNumber,TrashTracker);
						}
					break;
				case eExprFunctionCall:
					ReturnValue = TypeCheckFunctionCall(ResultTypeOut,TheExpression->u.FunctionCall,
						ErrorLineNumber,TrashTracker);
					break;
				case eExprLoop:
					ReturnValue = TypeCheckLoop(ResultTypeOut,TheExpression->u.Loop,ErrorLineNumber,
						TrashTracker);
					break;
				case eExprOperand:
					ReturnValue = TypeCheckOperand(ResultTypeOut,TheExpression->u.Operand,
						ErrorLineNumber,TrashTracker);
					break;
				case eExprUnaryOperator:
					ReturnValue = TypeCheckUnaryOperator(ResultTypeOut,
						TheExpression->u.UnaryOperator,ErrorLineNumber,TrashTracker);
					break;
				case eExprVariableDeclaration:
					ReturnValue = TypeCheckVariableDeclaration(ResultTypeOut,
						TheExpression->u.VariableDeclaration,ErrorLineNumber,TrashTracker);
					break;
				case eExprErrorForm:
					ReturnValue = TypeCheckErrorForm(ResultTypeOut,TheExpression->u.ErrorForm,
						ErrorLineNumber,TrashTracker);
					break;
				case eExprWaveGetter:
					ReturnValue = TypeCheckWaveGetter(ResultTypeOut,TheExpression->u.WaveGetter,
						ErrorLineNumber,TrashTracker);
					break;
				case eExprPrintString:
					ReturnValue = TypeCheckPrintString(ResultTypeOut,TheExpression->u.PrintString,
						ErrorLineNumber,TrashTracker);
					break;
				case eExprPrintExpr:
					ReturnValue = TypeCheckPrintExpr(ResultTypeOut,TheExpression->u.PrintExpr,
						ErrorLineNumber,TrashTracker);
					break;
			}

		TheExpression->WhatIsTheExpressionType = *ResultTypeOut;
		return ReturnValue;
	}


/* get a symbol table entry out of an expression.  this is used for getting */
/* function generation stuff. */
CompileErrors				ExpressionGetFunctionCallSymbol(struct SymbolRec** SymbolOut,
											ASTExpressionRec* TheExpression)
	{
		SymbolRec*				FunctionThing;

		CheckPtrExistence(TheExpression);
		if (TheExpression->ElementType != eExprOperand)
			{
				return eCompileFunctionIdentifierRequired;
			}
		if (!IsOperandASymbol(TheExpression->u.Operand))
			{
				return eCompileFunctionIdentifierRequired;
			}
		FunctionThing = GetSymbolFromOperand(TheExpression->u.Operand);
		CheckPtrExistence(FunctionThing);
		if (eSymbolFunction != WhatIsThisSymbol(FunctionThing))
			{
				return eCompileFunctionIdentifierRequired;
			}
		*SymbolOut = FunctionThing;
		return eCompileNoError;
	}


/* find out if the expression is a valid lvalue */
MyBoolean						IsExpressionValidLValue(ASTExpressionRec* TheExpression)
	{
		CheckPtrExistence(TheExpression);

		/* to be a valid lvalue, the expression must be one of */
		/*  - variable */
		/*  - array subscription operation */
		switch (TheExpression->ElementType)
			{
				default:
					return False;

				case eExprBinaryOperator:
					return (BinaryOperatorWhichOne(TheExpression->u.BinaryOperator)
						== eBinaryArraySubscripting);

				case eExprOperand:
					if (IsOperandASymbol(TheExpression->u.Operand))
						{
							SymbolRec*			TheOperandThing;

							TheOperandThing = GetSymbolFromOperand(TheExpression->u.Operand);
							return WhatIsThisSymbol(TheOperandThing) == eSymbolVariable;
						}
					 else
						{
							return False;
						}
			}
		EXECUTE(PRERR(ForceAbort,"IsExpressionValidLValue:  control reached end"));
	}


/* find out what kind of expression it is */
ExprTypes						WhatKindOfExpressionIsThis(ASTExpressionRec* TheExpression)
	{
		CheckPtrExistence(TheExpression);
		return TheExpression->ElementType;
	}


/* get the operand from the generic expression */
struct ASTOperandRec*	GetOperandOutOfExpression(ASTExpressionRec* TheExpression)
	{
		CheckPtrExistence(TheExpression);
		ERROR(TheExpression->ElementType != eExprOperand,PRERR(ForceAbort,
			"GetOperandOutOfExpression:  expression isn't an operand"));
		return TheExpression->u.Operand;
	}


/* get the binary operator out of the generic expression */
struct ASTBinaryOpRec*	GetBinaryOperatorOutOfExpression(ASTExpressionRec* TheExpression)
	{
		CheckPtrExistence(TheExpression);
		ERROR(TheExpression->ElementType != eExprBinaryOperator,PRERR(ForceAbort,
			"GetBinaryOperatorOutOfExpression:  expression isn't an operand"));
		return TheExpression->u.BinaryOperator;
	}


/* get the type of value that is returned by this expression */
DataTypes						GetExpressionsResultantType(ASTExpressionRec* TheExpression)
	{
		CheckPtrExistence(TheExpression);
		return TheExpression->WhatIsTheExpressionType;
	}


/* generate code for an expression.  returns True if successful, or False if it fails. */
MyBoolean						CodeGenExpression(struct PcodeRec* FuncCode,
											long* StackDepthParam, ASTExpressionRec* Expression)
	{
		long							StackDepth;

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

		switch (Expression->ElementType)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"CodeGenExpression:  unknown expression type"));
					break;
				case eExprArrayDeclaration:
					if (!CodeGenArrayConstruction(FuncCode,&StackDepth,
						Expression->u.ArrayDeclaration))
						{
							return False;
						}
					break;
				case eExprAssignment:
					if (!CodeGenAssignment(FuncCode,&StackDepth,Expression->u.Assignment))
						{
							return False;
						}
					break;
				case eExprBinaryOperator:
					if (!CodeGenBinaryOperator(FuncCode,&StackDepth,Expression->u.BinaryOperator))
						{
							return False;
						}
					break;
				case eExprConditional:
					if (!CodeGenConditional(FuncCode,&StackDepth,Expression->u.Conditional))
						{
							return False;
						}
					break;
				case eExprExpressionList:
					if (!CodeGenExpressionListSequence(FuncCode,&StackDepth,
						Expression->u.ExpressionList))
						{
							return False;
						}
					break;
				case eExprFunctionCall:
					if (!CodeGenFunctionCall(FuncCode,&StackDepth,Expression->u.FunctionCall))
						{
							return False;
						}
					break;
				case eExprLoop:
					if (!CodeGenLoop(FuncCode,&StackDepth,Expression->u.Loop))
						{
							return False;
						}
					break;
				case eExprOperand:
					if (!CodeGenOperand(FuncCode,&StackDepth,Expression->u.Operand))
						{
							return False;
						}
					break;
				case eExprUnaryOperator:
					if (!CodeGenUnaryOperator(FuncCode,&StackDepth,Expression->u.UnaryOperator))
						{
							return False;
						}
					break;
				case eExprVariableDeclaration:
					if (!CodeGenVarDecl(FuncCode,&StackDepth,Expression->u.VariableDeclaration))
						{
							return False;
						}
					break;
				case eExprErrorForm:
					if (!CodeGenErrorForm(FuncCode,&StackDepth,Expression->u.ErrorForm))
						{
							return False;
						}
					break;
				case eExprWaveGetter:
					if (!CodeGenWaveGetter(FuncCode,&StackDepth,Expression->u.WaveGetter))
						{
							return False;
						}
					break;
				case eExprPrintString:
					if (!CodeGenPrintString(FuncCode,&StackDepth,Expression->u.PrintString))
						{
							return False;
						}
					break;
				case eExprPrintExpr:
					if (!CodeGenPrintExpr(FuncCode,&StackDepth,Expression->u.PrintExpr))
						{
							return False;
						}
					break;
			}
		ERROR(
			(((Expression->ElementType != eExprVariableDeclaration)
				&& (Expression->ElementType != eExprArrayDeclaration))
				&& (StackDepth != *StackDepthParam + 1))
			||
			(((Expression->ElementType == eExprVariableDeclaration)
				|| (Expression->ElementType == eExprArrayDeclaration))
				&& (StackDepth != *StackDepthParam + 2)),
			PRERR(ForceAbort,"CodeGenExpression:  stack depth error"));

		*StackDepthParam = StackDepth;
		return True;
	}
