/// Dedicated to the public domain by Christopher Diggins
/// http://creativecommons.org/licenses/publicdomain/
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace Cat
{
///
/// The base class for all Cat functions. All functions can be invoked like one would
/// invoke a MethodInfo object. This is because each one contains its own private
/// executor;
///
public abstract class Function : CatBase
{
public Function(string sName, string sDesc, string sTags)
{
msName = sName;
msDesc = sDesc;
msTags = sTags;
}
public Function(string sName, string sDesc)
: this(sName, sDesc, "")
{
}
public Function(string sName)
: this(sName, "")
{
}
#region Fields
public string msName = "_unnamed_";
public string msDesc = "";
public string msTags = "";
public CatFxnType mpFxnType;
CatMetaDataBlock mpMetaData;
#endregion
public Function()
{
}
public string GetDesc()
{
return msDesc;
}
public string GetName()
{
return msName;
}
public string GetRawTags()
{
return msTags;
}
public string[] GetTags()
{
return msTags.Split(new char[] { ',' });
}
public override string ToString()
{
return msName;
}
public string GetFxnTypeString()
{
if (GetFxnType() == null)
return "untyped";
else
return GetFxnType().ToPrettyString();
}
public CatFxnType GetFxnType()
{
return mpFxnType;
}
public void SetMetaData(CatMetaDataBlock meta)
{
mpMetaData = meta;
CatMetaData desc = meta.Find("desc");
if (desc != null)
{
msDesc = desc.msContent;
}
CatMetaData tags = meta.Find("tags");
if (tags != null)
{
msTags = tags.msContent;
}
}
public CatMetaDataBlock GetMetaData()
{
return mpMetaData;
}
public bool HasMetaData()
{
return ((mpMetaData != null) && (mpMetaData.Count > 0));
}
public void WriteTo(StreamWriter sw)
{
sw.Write("define ");
sw.Write(msName);
if (mpFxnType != null)
{
sw.Write(" : ");
sw.Write(mpFxnType);
}
sw.WriteLine();
if (mpMetaData != null)
{
sw.WriteLine("{{");
sw.WriteLine(mpMetaData.ToString());
sw.WriteLine("}}");
}
sw.WriteLine("{");
sw.Write(" ");
sw.WriteLine(ToString());
sw.WriteLine("}");
}
#region virtual functions
// TODO: rename to Execute
public abstract void Eval(Executor exec);
#endregion
#region static functions
public static Type GetReturnType(MethodBase m)
{
if (m is ConstructorInfo)
return (m as ConstructorInfo).DeclaringType;
if (!(m is MethodInfo))
throw new Exception("Expected ConstructorInfo or MethodInfo");
return (m as MethodInfo).ReturnType;
}
public static bool HasReturnType(MethodBase m)
{
Type t = GetReturnType(m);
return (t != null) && (!t.Equals(typeof(void)));
}
public static bool HasThisType(MethodBase m)
{
if (m is ConstructorInfo)
return false;
return !m.IsStatic;
}
public static Type GetThisType(MethodBase m)
{
if (m is ConstructorInfo)
return null;
if (!(m is MethodInfo))
throw new Exception("Expected ConstructorInfo or MethodInfo");
if (m.IsStatic)
return null;
return (m as MethodInfo).DeclaringType;
}
public static string MethodToTypeString(MethodBase m)
{
string s = "('R ";
if (HasThisType(m))
s += CatKind.TypeToString(m.DeclaringType) + " ";
foreach (ParameterInfo pi in m.GetParameters())
s += CatKind.TypeToString(pi.ParameterType) + " ";
s += "-> 'R";
if (HasThisType(m))
s += " this";
if (HasReturnType(m))
s += " " + CatKind.TypeToString(GetReturnType(m));
s += ")";
return s;
}
#endregion
public virtual CatExpr GetSubFxns()
{
return null;
}
public IEnumerable GetDescendantFxns()
{
if (GetSubFxns() != null)
{
foreach (Function f in GetSubFxns())
{
yield return f;
foreach (Function g in f.GetDescendantFxns())
yield return g;
}
}
}
public string GetImplString()
{
return GetImplString(false);
}
public string GetImplString(bool bHtml)
{
string sBegin = "define ";
if (bHtml)
{
string sName = Util.ToHtml(GetName());
sBegin += "" + sName + "";
}
else
{
sBegin += GetName();
}
sBegin += " : " + GetFxnTypeString() + "\n";
string s = "";
if (GetMetaData() != null)
{
s += "{{\n";
s += GetMetaData().ToString();
s += "\n}}\n";
}
else if (GetDesc() != null)
{
s += "{{\n";
if (GetDesc() != null && GetDesc().Length > 0)
s += " desc:\n " + GetDesc() + "\n";
if (GetRawTags().Length > 0)
s += " tags:\n " + GetRawTags() + "\n";
s += "}}\n";
}
s += "{\n ";
if (GetSubFxns() != null)
{
for (int i=0; i < GetSubFxns().Count; ++i) {
Function f = GetSubFxns()[i];
if (i >= 0) s += " ";
if (bHtml)
s += Util.ToHtml(f.ToString()); else
s += f.ToString();
}
s += "\n";
}
else
{
s += "_primitive_\n";
}
s += "}\n";
if (bHtml) s = Util.ToHtml(s);
return sBegin + s;
}
}
///
/// Used to push stacks of values on a stack.
/// Note: Not a PushValueBase subclass!
/// This is used in the "pull" experimental function
///
public class PushStack : Function
{
Executor stk;
public PushStack(Executor x)
{
stk = x;
}
public override void Eval(Executor exec)
{
foreach (object o in stk.GetStackAsArray())
exec.Push(o);
}
public Executor GetStack()
{
return stk;
}
public override string ToString()
{
return "_stack_";
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
abstract public class PushValueBase : Function
{
}
public class PushValue : PushValueBase
{
CatMetaValue mValue;
string msValueType;
public PushValue(T x)
{
mValue = new CatMetaValue(x);
msName = mValue.GetData().ToString();
msValueType = CatKind.TypeNameFromObject(x);
mpFxnType = CatFxnType.Create("( -> " + msValueType + ")");
}
public T GetValue()
{
return mValue.GetData();
}
public override string ToString()
{
return mValue.ToString();
}
#region overrides
public override void Eval(Executor exec)
{
exec.Push(GetValue());
}
public override bool Equals(object obj)
{
if (!(obj is PushValue))
return false;
return (obj as PushValue).GetValue().Equals(GetValue());
}
public override int GetHashCode()
{
return base.GetHashCode();
}
#endregion
}
public class PushInt : PushValue
{
public PushInt(int x)
: base(x)
{ }
#region overrides
public override void Eval(Executor exec)
{
exec.PushInt(GetValue());
}
#endregion
}
public class PushBool : PushValue
{
public PushBool(bool x)
: base(x)
{ }
#region overrides
public override void Eval(Executor exec)
{
exec.PushBool(GetValue());
}
#endregion
}
///
/// Represents a a function literal. In other words a function that pushes an anonymous function onto a stack.
///
public class PushFunction : Function
{
CatExpr mSubFxns;
QuotedFunction mQF;
public PushFunction(CatExpr children)
{
mSubFxns = children.GetRange(0, children.Count);
msDesc = "pushes an anonymous function onto the stack";
msName = "_function_";
if (Config.gbTypeChecking)
{
if (Config.gbVerboseInference)
Output.WriteLine("inferring type of quoted function " + msName);
try
{
// Quotations can be unclear?
CatFxnType childType = CatTypeReconstructor.Infer(mSubFxns);
// Honestly this should never be true.
if (childType == null)
throw new Exception("unknown type error");
mpFxnType = new CatQuotedType(childType);
mpFxnType = CatVarRenamer.RenameVars(mpFxnType);
}
catch (Exception e)
{
Output.WriteLine("Could not type quotation: " + msName);
Output.WriteLine("Type error: " + e.Message);
mpFxnType = null;
}
}
else
{
mpFxnType = null;
}
}
public QuotedFunction GetQuotedFxn()
{
// An important optimization: this can be very slow.
if (mQF == null)
mQF = new QuotedFunction(mSubFxns, CatFxnType.Unquote(mpFxnType));
return mQF;
}
public override void Eval(Executor exec)
{
exec.Push(GetQuotedFxn());
}
public CatExpr GetChildren()
{
return mSubFxns;
}
public override string ToString()
{
string ret = "[";
for (int i = 0; i < mSubFxns.Count; ++i)
{
if (i > 0) ret += " ";
ret += mSubFxns[i].ToString();
}
return ret + "]";
}
public override bool Equals(object obj)
{
if (!(obj is PushFunction))
return false;
return (obj as PushFunction).GetChildren().Equals(mSubFxns);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
///
/// Represents a function that is on the stack.
///
public class QuotedFunction : Function
{
CatExpr mSubFxns;
public QuotedFunction(CatExpr children, CatFxnType pFxnType)
{
mSubFxns = new CatExpr(children.ToArray());
msDesc = "anonymous function";
msName = "_anonymous_";
mpFxnType = new CatQuotedType(pFxnType);
}
public QuotedFunction(CatExpr children)
: this(children, CatTypeReconstructor.Infer(children))
{
}
public QuotedFunction()
{
mSubFxns = new CatExpr();
}
/*
public CatFxnType GetUnquotedFxnType()
{
CatKind k = GetUnquotedKind();
if (!(k is CatFxnType))
throw new Exception("illegal type for a quoted function, should produce a single function : " + mpFxnType.ToString());
return k as CatFxnType;
}
*/
public CatKind GetUnquotedKind()
{
if (mpFxnType.GetCons().GetKinds().Count != 0)
throw new Exception("illegal type for a quoted function, should have no consumption : " + mpFxnType.ToString());
if (mpFxnType.GetProd().GetKinds().Count != 1)
throw new Exception("illegal type for a quoted function, should have a single production : " + mpFxnType.ToString());
CatKind k = mpFxnType.GetProd().GetKinds()[0];
return k;
}
public QuotedFunction(QuotedFunction first, QuotedFunction second)
{
mSubFxns = new CatExpr(first.GetSubFxns().ToArray());
mSubFxns.AddRange(second.GetSubFxns().ToArray());
msDesc = "anonymous composed function";
msName = "";
for (int i = 0; i < mSubFxns.Count; ++i)
{
if (i > 0) msName += " ";
msName += mSubFxns[i].GetName();
}
try
{
mpFxnType = new CatQuotedType(CatTypeReconstructor.ComposeTypes(first.GetFxnType(), second.GetFxnType()));
// TODO: remove once everythign tests okay.
//mpFxnType = new CatQuotedType(CatTypeReconstructor.ComposeTypes(first.GetUnquotedFxnType(), second.GetUnquotedFxnType()));
}
catch (Exception e)
{
Output.WriteLine("unable to type quotation: " + ToString());
Output.WriteLine("type error: " + e.Message);
mpFxnType = null;
}
}
public override void Eval(Executor exec)
{
exec.Execute(mSubFxns);
}
public override string ToString()
{
string ret = "[";
for (int i = 0; i < mSubFxns.Count; ++i)
{
if (i > 0) ret += " ";
ret += mSubFxns[i].ToString();
}
ret += "]";
return ret;
}
public override CatExpr GetSubFxns()
{
return mSubFxns;
}
public override bool Equals(object obj)
{
if (!(obj is QuotedFunction))
return false;
return (obj as QuotedFunction).GetSubFxns().Equals(mSubFxns);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
///
/// This class represents a dynamically created function,
/// e.g. the result of calling the quote function.
///
public class QuotedValue : QuotedFunction
{
Function mFxn;
public QuotedValue(Object x)
{
mFxn = new PushValue