/// Dedicated to the public domain by Christopher Diggins
/// http://creativecommons.org/licenses/publicdomain/
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace Cat
{
///
/// This class allwows us to convert Cat with named parameters,
/// to Cat without.
///
public static class CatLambdaConverter
{
public static int gnId = 0;
public static void Convert(AstRoot p)
{
foreach (AstDef x in p.Defs)
Convert(x);
}
public static string GenerateUniqueName(string sArg)
{
return sArg + "$" + gnId++.ToString();
}
public static void RenameFirstInstance(string sOld, string sNew, List terms)
{
for (int i = 0; i < terms.Count; ++i)
{
if (TermContains(terms[i], sOld))
{
if (terms[i] is AstLambda)
{
AstLambda l = terms[i] as AstLambda;
RenameFirstInstance(sOld, sNew, l.mTerms);
return;
}
else if (terms[i] is AstQuote)
{
AstQuote q = terms[i] as AstQuote;
RenameFirstInstance(sOld, sNew, q.mTerms);
return;
}
else
{
// Should never happen.
throw new Exception("expected either a lamda node or quote node");
}
}
else if (TermEquals(terms[i], sOld))
{
terms[i] = new AstName(sNew);
return;
}
}
throw new Exception(sOld + " was not found in the list of terms");
}
public static bool TermEquals(CatAstNode term, string var)
{
return term.ToString().Equals(var);
}
public static bool TermContains(CatAstNode term, string var)
{
if (term is AstQuote)
{
AstQuote q = term as AstQuote;
foreach (CatAstNode child in q.mTerms)
{
if (TermContainsOrEquals(child, var))
return true;
}
return false;
}
else if (term is AstLambda)
{
AstLambda l = term as AstLambda;
foreach (CatAstNode child in l.mTerms)
{
if (TermContainsOrEquals(child, var))
return true;
}
return false;
}
else
{
return false;
}
}
public static int CountInstancesOf(string var, List terms)
{
int ret = 0;
foreach (CatAstNode term in terms)
{
if (term is AstQuote)
{
AstQuote q = term as AstQuote;
ret += CountInstancesOf(var, q.mTerms);
}
else if (term is AstLambda)
{
AstLambda l = term as AstLambda;
ret += CountInstancesOf(var, l.mTerms);
}
else
{
if (TermEquals(term, var))
ret += 1;
}
}
return ret;
}
public static bool TermContainsOrEquals(CatAstNode term, string var)
{
return TermEquals(term, var) || TermContains(term, var);
}
public static void RemoveTerm(string var, List terms)
{
// Find the first term that either contains, or is equal to the
// free variable
int i = 0;
while (i < terms.Count)
{
if (TermContainsOrEquals(terms[i], var))
break;
++i;
}
if (i == terms.Count)
throw new Exception("error in abstraction elimination algorithm");
if (i > 0)
{
AstQuote q = new AstQuote(terms.GetRange(0, i));
terms.RemoveRange(0, i);
terms.Insert(0, q);
terms.Insert(1, new AstName("dip"));
i = 2;
}
else
{
i = 0;
}
if (TermEquals(terms[i], var))
{
terms[i] = new AstName("id");
return;
}
else if (TermContains(terms[i], var))
{
if (terms[i] is AstQuote)
{
AstQuote subExpr = terms[i] as AstQuote;
RemoveTerm(var, subExpr.mTerms);
}
else if (TermContains(terms[i], var))
{
AstLambda subExpr = terms[i] as AstLambda;
RemoveTerm(var, subExpr.mTerms);
}
else
{
throw new Exception("internal error: expected either a quotation or lambda term");
}
terms.Insert(i + 1, new AstName("papply"));
return;
}
else
{
throw new Exception("error in abstraction elimination algorithm");
}
}
/* DEBUG:
private static string VarsToString(List vars)
{
string ret = "";
foreach (string s in vars)
ret += s + " ";
return ret;
}
*/
///
/// Converts a list of terms to point-free form.
///
public static void ConvertTerms(List vars, List terms)
{
for (int i = 0; i < terms.Count; ++i)
{
CatAstNode exp = terms[i];
if (exp is AstLambda)
Convert(exp as AstLambda);
}
if (vars.Count == 0)
return;
for (int i = 0; i < vars.Count; ++i)
{
string var = vars[i];
if (CountInstancesOf(var, terms) == 0)
{
// Remove unused arguments
terms.Insert(0, new AstName("pop"));
}
else
{
int nDupCount = 0;
while (CountInstancesOf(var, terms) > 1)
{
// Create a new name for the used argument
string sNewVar = GenerateUniqueName(var);
RenameFirstInstance(var, sNewVar, terms);
RemoveTerm(sNewVar, terms);
nDupCount++;
}
RemoveTerm(var, terms);
for (int j=0; j < nDupCount; ++j)
terms.Insert(0, new AstName("dup"));
}
}
}
public static void Convert(AstLambda l)
{
ConvertTerms(l.mIdentifiers, l.mTerms);
// We won't be needing the identifiers anymore and I don't want
// to have the conversion algorithm get run potentially multiple
// times on each lambda term.
l.mIdentifiers.Clear();
}
public static void ConvertLocalCalls(List terms, List locals)
{
foreach (AstDef def in locals)
{
for (int i = terms.Count - 1; i >= 0; --i)
{
CatAstNode node = terms[i];
if (node is AstLambda)
{
ConvertLocalCalls((node as AstLambda).GetTerms(), locals);
}
else if (node is AstQuote)
{
ConvertLocalCalls((node as AstQuote).GetTerms(), locals);
}
else if (node is AstName)
{
if ((node as AstName).ToString().Equals(def.mName))
terms.Insert(i + 1, new AstName("apply"));
}
}
}
}
///
/// This is known as an abstraction algorithm. It converts from
/// a form with named parameters to point-free form.
///
public static void Convert(AstDef d)
{
List locs = new List();
foreach (AstDef loc in d.mLocals)
locs.Add(loc.mName);
ConvertLocalCalls(d.mTerms, d.mLocals);
ConvertTerms(locs, d.mTerms);
foreach (AstDef loc in d.mLocals)
d.mTerms.Insert(0, new AstQuote(loc.mTerms));
List args = new List();
foreach (AstParam p in d.mParams)
args.Add(p.ToString());
ConvertTerms(args, d.mTerms);
}
}
}