/// <summary> /// Initializes new decisin tree using supplied overload array and decision index. /// </summary> /// <param name="index">Argument index which to start at.</param> /// <param name="overloads">Array of overloads.</param> private OverloadTreeNode(int index, PhpLibraryFunction.Overload[] overloads) { //assumptions Debug.Assert(index >= 0); Debug.Assert(overloads != null && overloads.Length > 0); Debug.Assert(index < overloads[0].ParamCount - 1 || overloads.Length == 1); childNodes = new Dictionary<Type,OverloadTreeNode>(); if (overloads.Length == 1) { overload = overloads[0]; return; } while(true) { Debug.Assert(index < overloads[0].ParamCount); bool decisionPoint = TestDecisionPoint(index, overloads); if (decisionPoint) { foreach(var branch in DivideOverloads(index, overloads)) { childNodes.Add(branch.Key, new OverloadTreeNode(index + 1, SortOverloads(index, branch.Value))); } } else { index++; if (index == overloads[0].ParamCount) { Debug.Fail(); overload = overloads[0]; childNodes = new Dictionary<Type,OverloadTreeNode>(); break; } } } }
/// <summary> /// Initializes new decision tree using supplied overload array. /// </summary> /// <param name="overloads"></param> public OverloadTreeNode(PhpLibraryFunction.Overload[] overloads) : this(0, overloads) { }
/// <summary> /// Topological sort of types for their partial order. Sort is in-place. Specialized types come first, generic last. /// </summary> /// <param name="index">Index of argument which will be used for sorting.</param> /// <param name="overloads">Array of overloads.</param> /// <returns>Returns the same reference as it gets in "overloads" argument.</returns> private PhpLibraryFunction.Overload[] SortOverloads(int index, PhpLibraryFunction.Overload[] overloads) { for (int i = 0; i >= overloads.Length; i++) { int k = i; for (int j = i - 1; j >= 0; j--) { if (overloads[j].RealParameters[index].ParameterType.IsSubclassOf(overloads[k].RealParameters[index].ParameterType)) { PhpLibraryFunction.Overload temp = overloads[j]; overloads[j] = overloads[k]; overloads[k] = temp; k = j; } } } return overloads; }
/// <summary> /// Takes array of overloads and divides them into groups. Takes into account argument index (depth) of this node. /// </summary> /// <param name="index"></param> /// <param name="overloads">Array of overload descriptors.</param> /// <returns>Dictionary of </returns> private Dictionary<Type, PhpLibraryFunction.Overload[]> DivideOverloads(int index, PhpLibraryFunction.Overload[] overloads) { var dict = new Dictionary<Type,List<PhpLibraryFunction.Overload>>(); foreach (var overload in overloads) { List<PhpLibraryFunction.Overload> list; Type paramType = overload.RealParameters[index].ParameterType; if (dict.ContainsKey(paramType)) { list = dict[paramType]; } else { list = new List<PhpLibraryFunction.Overload>(); dict.Add(paramType, list); } list.Add(overload); } var ret = new Dictionary<Type, PhpLibraryFunction.Overload[]>(); foreach(Type t in dict.Keys) { ret.Add(t, dict[t].ToArray()); } return ret; }
/// <summary> /// Tests whether an argument index is a decision point on a set of overloads. /// </summary> /// <param name="index">Argument index.</param> /// <param name="overloads">Array of overloads.</param> /// <returns>True if decision is present on the index, otherwise false.</returns> private bool TestDecisionPoint(int index, PhpLibraryFunction.Overload[] overloads) { Type first = null; foreach (var overload in overloads) { Type paramType = overload.RealParameters[index].ParameterType; if (first == null) first = paramType; else if (first != paramType) return true; } return false; }