/*
 * Bernoulli Compiler
 * Copyright (c) Cornell University
 * Department of Computer Science
 * 
 * Kamen Yotov (kamen@yotov.org)
 * 
 * $Source: C:/CVS/kyotov/kyotov/Research/BC/C/Definitions.cs,v $
 * $Revision: 1.17 $
 * $Date: 2003/02/23 10:07:26 $
 */

using System;
using System.Collections;
using System.Diagnostics;

using XTensions.Collections;

using BC.Core.ASTs;
using BC.Core.Grammars.Symbols;
using BC.Core.Grammars.Reductions;
using Machines = BC.Core.Machines;

namespace BC.C
{
	public delegate string Stringer (object o);

	public enum JoinDelimiter
	{
		Nowhere = 0,
		Front = 1,
		Back = 2,
		Both = 3
	}

	public class Definitions
	{
		public static Machines.IntegerType IT_signed_char = Machines.IntegerType.Get(8, Machines.IntegerType.SignType.stSigned);
		public static Machines.IntegerType IT_unsigned_char = Machines.IntegerType.Get(8, Machines.IntegerType.SignType.stUnsigned);
		public static Machines.IntegerType IT_signed_short_int = Machines.IntegerType.Get(16, Machines.IntegerType.SignType.stSigned);
		public static Machines.IntegerType IT_unsigned_short_int = Machines.IntegerType.Get(16, Machines.IntegerType.SignType.stUnsigned);
		public static Machines.IntegerType IT_signed_int = Machines.IntegerType.Get(32, Machines.IntegerType.SignType.stSigned);
		public static Machines.IntegerType IT_unsigned_int = Machines.IntegerType.Get(32, Machines.IntegerType.SignType.stUnsigned);
		public static Machines.IntegerType IT_signed_long_int = IT_signed_int;
		public static Machines.IntegerType IT_unsigned_long_int = IT_unsigned_int;
		public static Machines.IntegerType IT_signed_long_long_int = Machines.IntegerType.Get(64, Machines.IntegerType.SignType.stSigned);
		public static Machines.IntegerType IT_unsigned_long_long_int = Machines.IntegerType.Get(64, Machines.IntegerType.SignType.stUnsigned);
		public static Machines.RealType RT_float = Machines.RealType.Get(32);
		public static Machines.RealType RT_double = Machines.RealType.Get(64);
		public static Machines.RealType RT_long_double = RT_double;
		public static Machines.ComplexType CT_float = Machines.ComplexType.Get(32);
		public static Machines.ComplexType CT_double = Machines.ComplexType.Get(64);
		public static Machines.ComplexType CT_long_double = CT_double;

		public static ArrayList TypeSpecifierLists = new ArrayList();

		static Hashtable TypeSpecifierList2MachineType = new Hashtable();
		static Hashtable MachineType2String = new Hashtable();

		static Hashtable Terminal2Operator = new Hashtable();
		static Hashtable Terminal2UnaryOperator = new Hashtable();
		static Hashtable Operator2Terminal = new Hashtable();

		public static readonly Operator[] AssignmentOperators = new Operator[]
		{
			Operator.Assign,
			Operator.AddAssign,
			Operator.SubtractAssign,
			Operator.MultiplyAssign,
			Operator.DivideAssign,
			Operator.ModuloAssign,
			Operator.ShiftLeftAssign,
			Operator.ShiftRightAssign,
			Operator.BitwiseAndAssign,
			Operator.BitwiseOrAssign,
			Operator.XOrAssign
		};

		static readonly Operator[][] Precedence = new Operator[][] 
		{
			new Operator[]
			{
				Operator.PostIncrement, 
				Operator.PostDecrement
			},
			new Operator[]
			{
				Operator.PreIncrement, 
				Operator.PreDecrement, 
				Operator.Address, 
				Operator.Dereference, 
				Operator.UnaryPlus,
				Operator.UnaryMinus,
				Operator.BitwiseNot,
				Operator.LogicalNot
			},
			new Operator[]
			{
			},
			new Operator[]
			{
				Operator.Multiply,
				Operator.Divide,
				Operator.Modulo
			},
			new Operator[]
			{
				Operator.Add,
				Operator.Subtract
			},
			new Operator[]
			{
				Operator.ShiftLeft,
				Operator.ShiftRight
			},
			new Operator[]
			{
				Operator.Less,
				Operator.LessOrEqual,
				Operator.Greater,
				Operator.GreaterOrEqual
			},
			new Operator[]
			{
				Operator.Equal,
				Operator.NotEqual
			},
			new Operator[]
			{
				Operator.BitwiseAnd
			},
			new Operator[]
			{
				Operator.XOr
			},
			new Operator[]
			{
				Operator.BitwiseOr
			},
			new Operator[]
			{
				Operator.LogicalAnd
			},
			new Operator[]
			{
				Operator.LogicalOr
			},
			AssignmentOperators,
			new Operator[]
			{
				Operator.Coma
			}
		};
		
		static Definitions ()
		{
			AddType(Machines.VoidType.TheVoidType, "void");
			AddType(Machines.BoolType.TheBoolType, "_Bool");
			AddType(IT_signed_char, "char");
			AddType(IT_signed_char, "signed", "char");
			AddType(IT_unsigned_char, "unsigned", "char");
			AddType(IT_signed_short_int, "short", "int");
			AddType(IT_signed_short_int, "signed", "short", "int");
			AddType(IT_unsigned_short_int, "unsigned", "short", "int");
			AddType(IT_signed_short_int, "short");
			AddType(IT_signed_short_int, "signed", "short");
			AddType(IT_unsigned_short_int, "unsigned", "short");
			AddType(IT_signed_int, "int");
			AddType(IT_signed_int, "signed", "int");
			AddType(IT_signed_int, "signed");
			AddType(IT_unsigned_int, "unsigned", "int");
			AddType(IT_unsigned_int, "unsigned");
			AddType(IT_signed_long_int, "long");
			AddType(IT_signed_long_int, "signed", "long");
			AddType(IT_unsigned_long_int, "unsigned", "long");
			AddType(IT_signed_long_int, "long", "int");
			AddType(IT_signed_long_int, "signed", "long", "int");
			AddType(IT_unsigned_long_int, "unsigned", "long", "int");
			AddType(IT_signed_long_long_int, "long", "long", "int");
			AddType(IT_signed_long_long_int, "signed", "long", "long", "int");
			AddType(IT_unsigned_long_long_int, "unsigned", "long", "long", "int");
			AddType(IT_signed_long_long_int, "long", "long");
			AddType(IT_signed_long_long_int, "signed", "long", "long");
			AddType(IT_unsigned_long_long_int, "unsigned", "long", "long");
			AddType(RT_float, "float");
			AddType(RT_double, "double");
			AddType(RT_long_double, "long", "double");
			AddType(CT_float, "float", "_Complex");
			AddType(CT_double, "double", "_Complex");
			AddType(CT_long_double, "long", "double", "_Complex");
			AddType(CT_float, "float", "_Imaginery");
			AddType(CT_double, "double", "_Imaginery");
			AddType(CT_long_double, "long", "double", "_Imaginary");

			AddOperator(Operator.Add, C.T_add);
			AddOperator(Operator.Subtract, C.T_subtract);
			AddOperator(Operator.Multiply, C.T_multiply);
			AddOperator(Operator.Divide, C.T_divide);
			AddOperator(Operator.Modulo, C.T_modulo);
			AddOperator(Operator.ShiftLeft, C.T_shiftleft);
			AddOperator(Operator.ShiftRight, C.T_shiftright);
			AddOperator(Operator.Less, C.T_less);
			AddOperator(Operator.LessOrEqual, C.T_lessorequal);
			AddOperator(Operator.Greater, C.T_greater);
			AddOperator(Operator.GreaterOrEqual, C.T_greaterorequal);
			AddOperator(Operator.Equal, C.T_equal);
			AddOperator(Operator.NotEqual, C.T_notequal);
			AddOperator(Operator.LogicalAnd, C.T_logical_and);
			AddOperator(Operator.LogicalOr, C.T_logical_or);
			AddOperator(Operator.BitwiseAnd, C.T_bitwise_and);
			AddOperator(Operator.BitwiseOr, C.T_bitwise_or);
			AddOperator(Operator.XOr, C.T_xor);
			AddOperator(Operator.PostIncrement, C.T_increment);
			AddOperator(Operator.PostDecrement, C.T_decrement);
			AddOperator(Operator.Assign, C.T_assign_plain);
			AddOperator(Operator.AddAssign, C.T_assign_add);
			AddOperator(Operator.SubtractAssign, C.T_assign_subtract);
			AddOperator(Operator.MultiplyAssign, C.T_assign_multiply);
			AddOperator(Operator.DivideAssign, C.T_assign_divide);
			AddOperator(Operator.ModuloAssign, C.T_assign_modulo);
			AddOperator(Operator.ShiftLeftAssign, C.T_assign_shiftleft);
			AddOperator(Operator.ShiftRightAssign, C.T_assign_shiftright);
			AddOperator(Operator.BitwiseAndAssign, C.T_assign_and);
			AddOperator(Operator.BitwiseOrAssign, C.T_assign_or);
			AddOperator(Operator.XOrAssign, C.T_assign_xor);
			AddOperator(Operator.Coma, C.T_coma);

			AddUnaryOperator(Operator.PreIncrement, C.T_increment);
			AddUnaryOperator(Operator.PreDecrement, C.T_decrement);
			AddUnaryOperator(Operator.Address, C.T_address);
			AddUnaryOperator(Operator.Dereference, C.T_pointer);
			AddUnaryOperator(Operator.UnaryPlus, C.T_add);
			AddUnaryOperator(Operator.UnaryMinus, C.T_subtract);
			AddUnaryOperator(Operator.BitwiseNot, C.T_bitwise_not);
			AddUnaryOperator(Operator.LogicalNot, C.T_logical_not);
		}

		public static string SimpleStringer (object o)
		{
			return o.ToString();
		}

		public static string Join (ICollection c, Stringer s, string d, JoinDelimiter t)
		{
			string r = string.Empty;

			if (c != null && c.Count != 0)
			{
				bool f = true;

				foreach (object o in c)
				{
					r = string.Format("{0}{1}{2}", r, f ? string.Empty : d, s(o));
					f = false;
				}

				if ((t & JoinDelimiter.Back) != JoinDelimiter.Nowhere)
					r += d;
				if ((t & JoinDelimiter.Front) != JoinDelimiter.Nowhere)
					r = d + r;
			}

			return r;
		}

		public static string Join (ICollection c, Stringer s, string d)
		{
			return Join(c, s, d, JoinDelimiter.Nowhere);
		}

		public static string Join (ICollection c, string d, JoinDelimiter t)
		{
			return Join(c, new Stringer(SimpleStringer), d, t);
		}

		public static string Join (ICollection c, string d)
		{
			return Join(c, new Stringer(SimpleStringer), d, JoinDelimiter.Nowhere);
		}

		static string ArrayToString (string[] k)
		{
			Array.Sort(k);

			return Join(k, " ");
		}

		public static AbstractPrimitiveType ListToType (ArrayList t)
		{
			Machines.MachineType mt = (Machines.MachineType)TypeSpecifierList2MachineType[ArrayToString((string[])Common.ListToArray(t, typeof(string)))];

			return mt != null ? mt.ASTType : null;
		}

		public static string TypeToString (AbstractPrimitiveType t)
		{
			if (t is VoidType)
				return (string)MachineType2String[Machines.VoidType.TheVoidType];

			if (t is BoolType)
				return (string)MachineType2String[Machines.BoolType.TheBoolType];

			if (t is IntegerType)
			{
				int p = t.Precision > 0 ? t.Precision : IT_signed_long_long_int.Precision;

				return (string)MachineType2String[Machines.IntegerType.Get(p, ((IntegerType)t).Signed)];
			}

			if (t is RealType)
				return (string)MachineType2String[Machines.RealType.Get(t.Precision)];

			if (t is ImaginaryType)
				return (string)MachineType2String[Machines.ImaginaryType.Get(t.Precision)];

			if (t is ComplexType)
				return (string)MachineType2String[Machines.ComplexType.Get(t.Precision)];

			Debug.Assert(false, "invalid primitive type");

			return null;
		}

		public static void SetQualifiers (AbstractType t, Object _)
		{
			ArrayList q = Common.ObjectToList(_);
			t.Constant = q.Contains(C.T_keyword_const);
			t.Volatile = q.Contains(C.T_keyword_volatile);
			t.Restricted = q.Contains(C.T_keyword_restrict);
		}

		protected static void AddType (Machines.MachineType t, params string[] k)
		{
			string s = Join(k, " ");

			TypeSpecifierLists.Add(s);

			if (!MachineType2String.Contains(t))
				MachineType2String[t] = Join(k, " ");

			s = ArrayToString(k);

			TypeSpecifierList2MachineType[s] = t;
		}

		protected static void AddOperator (Operator o, Terminal t)
		{
			Operator2Terminal[o] = t;
			Terminal2Operator[t] = o;
		}

		protected static void AddUnaryOperator (Operator o, Terminal t)
		{
			Operator2Terminal[o] = t;
			Terminal2UnaryOperator[t] = o;
		}

		public static string OperatorToString (Operator o)
		{
			return ((Terminal)Operator2Terminal[o]).Value;
		}

		public static Operator TerminalToOperator (object t)
		{
			return (Operator)Terminal2Operator[(Terminal)t];
		}

		public static Operator TerminalToUnaryOperator (object t)
		{
			return (Operator)Terminal2UnaryOperator[(Terminal)t];
		}

		public static int OperationPrecedence (AbstractNode e)
		{
			if (
				e is AbstractConstantExpression ||
				e is NamedExpression)
				return -1;
			else if (
				e is ArrayExpression ||
				e is FunctionExpression || 
				e is FieldExpression)
				return 0;
			else if (
				e is SizeOfExpression ||
				e is SizeOfTypeExpression)
				return 1;
			else if (
				e is CastExpression ||
				e is CompositeExpression
				)
				return 2;
			else if (e is AbstractOperatorExpression)
			{
				AbstractOperatorExpression oe = (AbstractOperatorExpression)e;

				for (int i = 0; i < Precedence.Length; i++)
					if (Array.IndexOf(Precedence[i], oe.Operator) >= 0)
						return i;

				Debug.Assert(false, "invalid operator");

				return -2;
			}
			else
				return int.MaxValue;
		}

		public static bool PostOperation (AbstractNode e)
		{
			return e is UnaryExpression && (((UnaryExpression)e).Operator == Operator.PostIncrement || ((UnaryExpression)e).Operator == Operator.PostDecrement);
		}

		public static bool AssignmentOperation (AbstractNode e)
		{
			return e is BinaryExpression && Array.IndexOf(AssignmentOperators, ((BinaryExpression)e).Operator) >= 0;
		}

		public static bool BackwardOperation (AbstractNode e)
		{
			return
				e is UnaryExpression && !PostOperation(e) ||
				e is SizeOfExpression ||
				e is TrinaryExpression ||
				AssignmentOperation(e);
		}
	}
}
