using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace Cat
{
public class CatFxnType : CatTypeKind
{
#region fields
protected CatTypeVector mProd;
protected CatTypeVector mCons;
bool mbSideEffects;
CatFxnType mpParent;
CatTypeVarList mpFreeVars = new CatTypeVarList();
protected int mnId = gnId++;
#endregion
#region static functions
public static CatFxnType PushSomethingType = Create("( -> 'a)");
public static CatFxnType AnyFxnType = Create("('A -> 'B)");
public static CatFxnType PushAnythingType = Create("( -> 'A)");
public static CatFxnType Create(string sType)
{
if (sType.Length == 0)
return null;
Peg.Parser p = new Peg.Parser(sType);
try
{
if (!p.Parse(CatGrammar.FxnType()))
throw new Exception("no additional information");
}
catch (Exception e)
{
throw new Exception(sType + " is not a valid function type ", e);
}
Peg.PegAstNode ast = p.GetAst();
if (ast.GetNumChildren() != 1)
throw new Exception("invalid number of children in abstract syntax tree");
AstFxnType node = new AstFxnType(ast.GetChild(0));
CatFxnType ret = new CatFxnType(node);
return ret;
}
public static CatFxnType ComposeFxnTypes(CatFxnType f, CatFxnType g)
{
CatFxnType ft = CatTypeReconstructor.ComposeTypes(f, g);
return ft;
}
public static CatFxnType Unquote(CatFxnType ft)
{
if (ft == null)
return null;
if (ft.GetCons().GetKinds().Count > 0)
throw new Exception("Can't unquote a function type with a consumption size greater than zero");
if (ft.GetProd().GetKinds().Count != 1)
throw new Exception("Can't unquote a function type which does not produce a single function");
CatKind k = ft.GetProd().GetKinds()[0];
if (!(k is CatFxnType))
throw new Exception("Can't unquote a function type which does not produce a single function");
return k as CatFxnType;
}
#endregion
#region constructors
public CatFxnType(CatTypeVector cons, CatTypeVector prod, bool bSideEffects)
{
mCons = new CatTypeVector(cons);
mProd = new CatTypeVector(prod);
mbSideEffects = bSideEffects;
SetChildFxnParents();
ComputeFreeVars();
}
public CatFxnType()
{
mbSideEffects = false;
mCons = new CatTypeVector();
mProd = new CatTypeVector();
SetChildFxnParents();
ComputeFreeVars();
}
public CatFxnType(AstFxnType node)
{
mbSideEffects = node.HasSideEffects();
mCons = new CatTypeVector(node.mCons);
mProd = new CatTypeVector(node.mProd);
SetChildFxnParents();
ComputeFreeVars();
}
#endregion
#region variable functions
private CatTypeVector AddImplicitRhoVariables(CatTypeVector v)
{
CatTypeVector ret = new CatTypeVector();
foreach (CatKind k in v.GetKinds())
{
if (k is CatFxnType)
ret.Add((k as CatFxnType).AddImplicitRhoVariables());
else if (k is CatTypeVector)
ret.Add(AddImplicitRhoVariables(k as CatTypeVector));
else
ret.Add(k);
}
return ret;
}
public virtual CatFxnType AddImplicitRhoVariables()
{
CatTypeVector cons = AddImplicitRhoVariables(GetCons());
CatTypeVector prod = AddImplicitRhoVariables(GetProd());
if (!(cons.GetBottom() is CatStackVar))
{
CatStackVar rho = CatStackVar.CreateUnique();
cons.PushKindBottom(rho);
prod.PushKindBottom(rho);
}
return new CatFxnType(cons, prod, HasSideEffects());
}
public void RemoveImplicitRhoVariables()
{
// TEMP: TODO: figure out whether or not to remove this.
// RemoveImplicitRhoVariables(this);
}
///
/// This function modifies the function
///
public void RemoveImplicitRhoVariables(CatFxnType ft)
{
foreach (CatKind k in ft.GetChildKinds())
if (k is CatFxnType)
RemoveImplicitRhoVariables(k as CatFxnType);
if (!ft.GetCons().IsEmpty() && !ft.GetProd().IsEmpty())
{
CatKind k1 = ft.GetCons().GetBottom();
CatKind k2 = ft.GetProd().GetBottom();
// Does both consumption and production share the same
// stack variable at the bottom, if so, then we might have
// an implicit Rho variables
if (k1 is CatStackVar && k1.Equals(k2))
{
// try removing the stack variable
ft.GetCons().GetKinds().RemoveAt(0);
ft.GetProd().GetKinds().RemoveAt(0);
// is the variable used anywhere else?
// if so, then we have to restore it
// otherwise we leave it taken away
if (DoesVarExist(k1))
{
ft.GetCons().GetKinds().Insert(0, k1);
ft.GetProd().GetKinds().Insert(0, k2);
}
}
}
}
public bool DoesVarExist(CatKind k)
{
Trace.Assert(k.IsKindVar());
foreach (CatKind tmp in GetDescendantKinds())
if (tmp.Equals(k))
return true;
return false;
}
public CatTypeVarList GetAllVars()
{
CatTypeVarList ret = new CatTypeVarList();
GetAllVars(ret);
return ret;
}
private void GetAllVars(CatTypeVarList vars)
{
foreach (CatKind k in GetChildKinds())
{
if (k is CatFxnType)
{
(k as CatFxnType).GetAllVars(vars);
}
else if (k.IsKindVar())
{
vars.Add(k);
}
}
}
/* TODO: LOWPRI: remove
private CatTypeVarList GetVarsExcept(CatFxnType except)
{
CatTypeVarList ret = new CatTypeVarList();
GetVarsExcept(except, ret);
return ret;
}
private void GetVarsExcept(CatFxnType except, CatTypeVarList vars)
{
foreach (CatKind k in GetChildKinds())
{
if (k is CatFxnType)
{
CatFxnType ft = k as CatFxnType;
if (ft != except)
ft.GetVarsExcept(except, vars);
}
else
{
if (k.IsKindVar())
vars.Add(k);
}
}
}
*/
///
/// Every kind variable has a scope in which it is free.
/// This allows us to compute whether a variable is free or bound.
/// The purpose of figuring whether a variable is free or bound, is so
/// that when we copy a function, we can make sure that any free variables
/// are given new unique names. Basically we want to make new names,
/// but we can't always do that.
///
///
private void ComputeVarScopes(CatVarScopes scopes, Stack visited)
{
if (visited.Contains(this))
return;
foreach (CatKind k in GetChildKinds())
{
if (k.IsKindVar())
{
scopes.Add(k, this);
}
else if (k is CatFxnType)
{
CatFxnType ft = k as CatFxnType;
visited.Push(ft);
ft.ComputeVarScopes(scopes, visited);
visited.Pop();
}
}
}
private CatVarScopes ComputeVarScopes()
{
CatVarScopes scopes = new CatVarScopes();
ComputeVarScopes(scopes, new Stack());
return scopes;
}
private void ComputeFreeVars()
{
CatVarScopes scopes = ComputeVarScopes();
SetFreeVars(scopes, new Stack());
// TODO:
// Use the scopes to find out if a function type is well-formed or not.
// i.e. CheckIsWellFormed(scopes);
}
private bool IsFreeVar(CatKind k, CatVarScopes scopes)
{
return scopes.IsFreeVar(this, k);
}
private void SetFreeVars(CatVarScopes scopes, Stack visited)
{
if (visited.Contains(this))
return;
foreach (CatKind k in GetDescendantKinds())
{
// iterate over the values
if (IsFreeVar(k, scopes))
mpFreeVars.Add(k);
else if (k is CatFxnType)
{
visited.Push(this);
(k as CatFxnType).SetFreeVars(scopes, visited);
visited.Pop();
}
}
}
public bool IsFreeVar(CatKind var)
{
if (!var.IsKindVar())
return false;
return mpFreeVars.ContainsKey(var.ToString());
}
#endregion
#region production and consumption
public int GetMaxProduction()
{
int nCnt = 0;
List list = mProd.GetKinds();
for (int i = list.Count - 1; i >= 0; --i)
{
CatKind k = list[i];
if (k is CatStackVar)
{
if ((i == 0) && k.Equals(mCons.GetBottom()))
return nCnt;
else
return -1;
}
nCnt++;
}
return nCnt;
}
public int GetMaxConsumption()
{
int nCnt = 0;
List list = mCons.GetKinds();
for (int i = list.Count - 1; i >= 0; --i)
{
CatKind k = list[i];
if (k is CatStackVar)
{
if ((i == 0) && k.Equals(mProd.GetBottom()))
return nCnt;
else
return -1;
}
nCnt++;
}
return nCnt;
}
public CatTypeVector GetProd()
{
return mProd;
}
public CatTypeVector GetCons()
{
return mCons;
}
#endregion
#region string formatting
public override string ToString()
{
if (mbSideEffects)
return "(" + GetCons().ToString() + " ~> " + GetProd().ToString() + ")"; else
return "(" + GetCons().ToString() + " -> " + GetProd().ToString() + ")";
}
public override string ToIdString()
{
string ret = "";
if (mbSideEffects)
ret = "(" + GetCons().ToIdString() + " ~> " + GetProd().ToIdString() + ")"; else
ret = "(" + GetCons().ToIdString() + " -> " + GetProd().ToIdString() + ")";
ret += "_" + mnId.ToString();
return ret;
}
public static string IntToPrettyString(int n)
{
string s = "";
if (n > 26)
s += IntToPrettyString(n / 26);
char c = 'a';
c += (char)(n % 26);
s += c.ToString();
return "'" + s;
}
public static string ToPrettyString(CatTypeVector vec, Dictionary dic)
{
string s = "";
for (int i=0; i < vec.GetKinds().Count; ++i)
{
if (i > 0) s += " ";
CatKind k = vec.GetKinds()[i];
if (k.IsKindVar())
{
if (!dic.ContainsKey(k.ToString()))
{
string sNew = IntToPrettyString(dic.Count);
if (k is CatStackVar)
sNew = sNew.ToUpper();
dic.Add(k.ToString(), sNew);
}
s += dic[k.ToString()];
}
else if (k is CatFxnType)
{
s += "(" + ToPrettyString(k as CatFxnType, dic) + ")";
}
else if (k is CatTypeVector)
{
s += ToPrettyString(k as CatFxnType, dic);
}
else
{
s += k.ToString();
}
}
return s;
}
public virtual string ToPrettyString()
{
return "(" + ToPrettyString(this, new Dictionary()) + ")";
}
public static string ToPrettyString(CatFxnType ft, Dictionary dic)
{
// remove rho variables
ft = ft.Clone();
ft.RemoveImplicitRhoVariables();
string s = ToPrettyString(ft.GetCons(), dic);
if (ft.HasSideEffects())
s += " ~> ";
else
s += " -> ";
s += ToPrettyString(ft.GetProd(), dic);
return s;
}
#endregion
#region comparison functions
///
/// This is a raw equivalency check: no normalization is done.
/// To comparse function type normally you would use CompareFxnTypes,
/// which in turn calls this function.
///
public override bool Equals(CatKind k)
{
if (!(k is CatFxnType)) return false;
CatFxnType f = k as CatFxnType;
return (GetCons().Equals(f.GetCons()) && GetProd().Equals(f.GetProd())
&& HasSideEffects() == f.HasSideEffects());
}
public override bool IsSubtypeOf(CatKind k)
{
if (k.IsAny() || k.IsDynFxn())
return IsRuntimePolymorphic();
if (k is CatTypeVar)
return true;
if (!(k is CatFxnType))
return false;
CatFxnType f = k as CatFxnType;
bool ret = GetCons().IsSubtypeOf(f.GetCons()) && GetProd().IsSubtypeOf(f.GetProd());
if (HasSideEffects())
ret = ret && f.HasSideEffects();
return ret;
}
///
/// Compares two function types, by first normalizing so that they each have
/// names of variables that correspond to the locations in the function
///
public static bool CompareFxnTypes(CatFxnType f, CatFxnType g)
{
CatFxnType f2 = CatVarRenamer.RenameVars(f.AddImplicitRhoVariables());
CatFxnType g2 = CatVarRenamer.RenameVars(g.AddImplicitRhoVariables());
return f2.IsSubtypeOf(g2);
}
#endregion
#region verifications
public bool IsValidChildFxn(List varNames, CatFxnType ft)
{
foreach (CatKind k in ft.GetCons().GetKinds())
{
if (k.IsKindVar())
varNames.Add(k.ToString());
}
return IsValidProduction(varNames, ft.GetProd());
}
public void GetConsVarNames(List varNames, CatFxnType ft)
{
foreach (CatKind k in ft.GetCons().GetKinds())
{
if (k.IsKindVar())
varNames.Add(k.ToString());
if (k is CatFxnType)
GetConsVarNames(varNames, k as CatFxnType);
}
}
public bool IsValidProduction(List varNames, CatTypeVector prod)
{
foreach (CatKind k in prod.GetKinds())
if (k is CatFxnType)
GetConsVarNames(varNames, k as CatFxnType);
foreach (CatKind k in prod.GetKinds())
if (k.IsKindVar())
if (!varNames.Contains(k.ToString()))
return false;
return true;
}
public bool IsValid()
{
// TODO : rewrite this function
return true;
/*
// TODO: check that if pure, none of the child functions are impure.
if (!GetCons().IsValid())
return false;
if (!GetProd().IsValid())
return false;
List varNames = new List();
foreach (CatKind k in GetCons().GetDescendantKinds())
{
if (k.IsKindVar())
{
string s = k.ToString();
if (!varNames.Contains(s))
varNames.Add(s);
}
}
if (!IsValidProduction(varNames, GetProd()))
return false;
return true;
*/
}
#endregion
#region utility functions
public CatFxnType Clone()
{
return new CatFxnType(mCons, mProd, mbSideEffects);
}
public bool HasSideEffects()
{
return mbSideEffects;
}
///
/// Returns true if this function can be converted to either "any" or a "fxn".
///
public bool IsRuntimePolymorphic()
{
return (GetCons().GetKinds().Count == 1) && (GetProd().GetKinds().Count == 1);
}
#endregion
#region parent / child functions
public CatFxnType GetParent()
{
return mpParent;
}
public CatFxnType GetAncestor()
{
if (mpParent == null)
return this;
return mpParent.GetAncestor();
}
public bool DescendentOf(CatFxnType ft)
{
if (this == ft)
return true;
if (mpParent == null)
return false;
return mpParent.DescendentOf(ft);
}
public void SetParent(CatFxnType parent)
{
mpParent = parent;
}
private void SetChildFxnParents()
{
foreach (CatKind k in GetChildKinds())
{
if (k is CatFxnType)
{
CatFxnType ft = k as CatFxnType;
ft.SetChildFxnParents();
ft.SetParent(this);
}
}
}
public override IEnumerable GetChildKinds()
{
foreach (CatKind k in mCons.GetChildKinds())
yield return k;
foreach (CatKind k in mProd.GetChildKinds())
yield return k;
}
public override IEnumerable GetDescendantKinds()
{
foreach (CatKind k in mCons.GetDescendantKinds())
yield return k;
foreach (CatKind k in mProd.GetDescendantKinds())
yield return k;
}
#endregion
}
public class CatQuotedType : CatFxnType
{
public CatQuotedType(CatKind k)
{
GetProd().Add(k);
}
}
public class CatRecursiveType : CatKind
{
public CatRecursiveType()
{
}
public override string ToString()
{
return "self";
}
public override bool Equals(CatKind k)
{
return k is CatRecursiveType;
}
public override IEnumerable GetChildKinds()
{
return new List();
}
public override IEnumerable GetDescendantKinds()
{
return new List();
}
}
}