public static Expr Parse(ParserContext pcon, ISeq form, string name)
{
ISeq origForm = form;
FnExpr fn = new FnExpr(Compiler.TagOf(form));
fn.Src = form;
Keyword retKey = Keyword.intern(null, "rettag"); // TODO: make static
object retTag = RT.get(RT.meta(form), retKey);
ObjMethod enclosingMethod = (ObjMethod)Compiler.MethodVar.deref();
fn._hasEnclosingMethod = enclosingMethod != null;
if (((IMeta)form.first()).meta() != null)
{
fn.OnceOnly = RT.booleanCast(RT.get(RT.meta(form.first()), KW_ONCE));
}
fn.ComputeNames(form, name);
List<string> prims = new List<string>();
//arglist might be preceded by symbol naming this fn
Symbol nm = RT.second(form) as Symbol;
if (nm != null)
{
fn.ThisName = nm.Name;
form = RT.cons(Compiler.FnSym, RT.next(RT.next(form)));
}
// Normalize body
//now (fn [args] body...) or (fn ([args] body...) ([args2] body2...) ...)
//turn former into latter
if (RT.second(form) is IPersistentVector)
form = RT.list(Compiler.FnSym, RT.next(form));
fn.SpanMap = (IPersistentMap)Compiler.SourceSpanVar.deref();
GenContext newContext = null;
GenContext context = Compiler.CompilerContextVar.deref() as GenContext ?? Compiler.EvalContext;
newContext = context.WithNewDynInitHelper(fn.InternalName + "__dynInitHelper_" + RT.nextID().ToString());
Var.pushThreadBindings(RT.map(Compiler.CompilerContextVar, newContext));
try
{
try
{
Var.pushThreadBindings(RT.mapUniqueKeys(
Compiler.ConstantsVar, PersistentVector.EMPTY,
Compiler.ConstantIdsVar, new IdentityHashMap(),
Compiler.KeywordsVar, PersistentHashMap.EMPTY,
Compiler.VarsVar, PersistentHashMap.EMPTY,
Compiler.KeywordCallsitesVar, PersistentVector.EMPTY,
Compiler.ProtocolCallsitesVar, PersistentVector.EMPTY,
Compiler.VarCallsitesVar, Compiler.EmptyVarCallSites(),
Compiler.NoRecurVar, null));
SortedDictionary<int, FnMethod> methods = new SortedDictionary<int, FnMethod>();
FnMethod variadicMethod = null;
bool usesThis = false;
for (ISeq s = RT.next(form); s != null; s = RT.next(s))
{
FnMethod f = FnMethod.Parse(fn, (ISeq)RT.first(s), retTag);
if ( f.UsesThis)
{
//Console.WriteLine("{0} uses this",fn.Name);
usesThis = true;
}
if (f.IsVariadic)
{
if (variadicMethod == null)
variadicMethod = f;
else
throw new ParseException("Can't have more than 1 variadic overload");
}
else if (!methods.ContainsKey(f.RequiredArity))
methods[f.RequiredArity] = f;
else
throw new ParseException("Can't have 2 overloads with the same arity.");
if (f.Prim != null)
prims.Add(f.Prim);
}
if (variadicMethod != null && methods.Count > 0 && methods.Keys.Max() >= variadicMethod.NumParams)
throw new ParseException("Can't have fixed arity methods with more params than the variadic method.");
fn.CanBeDirect = !fn._hasEnclosingMethod && fn.Closes.count() == 0 && !usesThis;
IPersistentCollection allMethods = null;
foreach (FnMethod method in methods.Values)
allMethods = RT.conj(allMethods, method);
if (variadicMethod != null)
allMethods = RT.conj(allMethods, variadicMethod);
if ( fn.CanBeDirect )
{
for (ISeq s = RT.seq(allMethods); s != null; s = s.next())
{
FnMethod fm = s.first() as FnMethod;
if ( fm.Locals != null)
{
for (ISeq sl = RT.seq(RT.keys(fm.Locals)); sl != null; sl = sl.next())
{
LocalBinding lb = sl.first() as LocalBinding;
if ( lb.IsArg)
lb.Index -= 1;
}
}
}
}
fn.Methods = allMethods;
fn._variadicMethod = variadicMethod;
fn.Keywords = (IPersistentMap)Compiler.KeywordsVar.deref();
fn.Vars = (IPersistentMap)Compiler.VarsVar.deref();
fn.Constants = (PersistentVector)Compiler.ConstantsVar.deref();
fn.KeywordCallsites = (IPersistentVector)Compiler.KeywordCallsitesVar.deref();
fn.ProtocolCallsites = (IPersistentVector)Compiler.ProtocolCallsitesVar.deref();
fn.VarCallsites = (IPersistentSet)Compiler.VarCallsitesVar.deref();
fn.ConstantsID = RT.nextID();
}
finally
{
Var.popThreadBindings();
}
IPersistentMap fmeta = RT.meta(origForm);
if (fmeta != null)
fmeta = fmeta.without(RT.LineKey).without(RT.ColumnKey).without(RT.SourceSpanKey).without(RT.FileKey).without(retKey);
fn._hasMeta = RT.count(fmeta) > 0;
IPersistentVector primTypes = PersistentVector.EMPTY;
foreach (string typename in prims)
primTypes = primTypes.cons(Type.GetType(typename));
fn.Compile(
fn.IsVariadic ? typeof(RestFn) : typeof(AFunction),
null,
primTypes,
fn.OnceOnly,
newContext);
if (fn.SupportsMeta)
return new MetaExpr(fn, MapExpr.Parse(pcon.EvalOrExpr(), fmeta));
else
return fn;
}
finally
{
if (newContext != null)
Var.popThreadBindings();
}
}