// Leaves the link in the same state as when it started. The out param leafExprType will be Function if // the array is empty in the first bottom-level subarray: {{}, {1,2,3}}. internal static int determineIncomingArrayDepth(IMathLink ml /*, out ExpressionType leafExprType*/) { int actualDepth = 0; ILinkMark mark = ml.CreateMark(); // // Initial value just to satisfy compiler; if depth of 0 is returned, then the leafExprType will be ignored by callers. // leafExprType = ExpressionType.Function; try { int len; string head = ml.GetFunction(out len); actualDepth = 1; while (len > 0) { ExpressionType leafExprType = ml.GetNextExpressionType(); if (leafExprType == ExpressionType.Function) { head = ml.GetFunction(out len); actualDepth++; } else { break; } } // // If the last sublist is empty (len == 0), we will walk off the end of the above loop with // // leafExprType == ExpressionType.Function, which is what we want. } catch (MathLinkException) { // Do nothing but clear it. Returning 0 is enough. ml.ClearError(); } finally { ml.SeekMark(mark); ml.DestroyMark(mark); } return(actualDepth); }
public override ILinkMark CreateMark() { return(impl.CreateMark()); }
// Can read any kind of array, except for jagged arrays that are multidimensional at the beginning, like [][,] // (jagged at start like [,][] or [,...][][] is OK, but only if we know the type, not for an Array slot). Recall // that mixed array types are read backwards, so [][,] is a 2-deep array of element type [] (i.e., it is multidimensional // at the start, not the end). One more limitation: For an Array argument slot, if the incoming array is 3-deep or deeper, // it must be rectangular, not jagged. // We know the incoming expression has head List. internal static Array readArbitraryArray(IMathLink ml, Type t) { int len; if (t == typeof(Array)) { ExpressionType leafExprType; Type leafType; // For the Array type, we have no clue about how the leaf elements will be read. Thus we need to peek into // the incoming data to decide what type it is. int depth = determineIncomingArrayDepth(ml); ILinkMark mark = ml.CreateMark(); ILoopbackLink loop = null; try { if (depth == 1) { len = ml.CheckFunction("List"); if (len == 0) { throw new MathLinkException(MathLinkException.MLE_EMPTY_ARRAY); } leafExprType = ml.GetNextExpressionType(); ml.SeekMark(mark); leafType = netTypeFromExpressionType(leafExprType, ml); // Fail if data could only be read as Expr. This means that we cannot pass a list of arbitrary expressions // to an arg slot typed as Array. It would have to be typed as Expr[]. We make this choice to provide // more meaningful error reporting for the cases where the array has bogus data in it. We assume that // this convenience outweighs the very rare cases where a programmer would want to pass an array of exprs // for an Array slot (they could always create the array separately and pass it as an object reference). if (leafType == typeof(Expr)) { if (leafExprType == ExpressionType.Complex) { throw new MathLinkException(MathLinkException.MLE_NO_COMPLEX); } else { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY); } } return((Array)ml.GetArray(leafType, 1)); } else if (depth == 2) { // The loopback link is just a utility for quickly reading expressions off the link (via TransferExpression). // Nothing is ever read back off the loopback link. loop = MathLinkFactory.CreateLoopbackLink(); // Determine if the array is rectangular or jagged. len = ml.CheckFunction("List"); bool isJagged = false; bool foundLeafType = false; // This next assignment is strictly for the compiler. The value will never be used // unless it is set to an actual value below. We have an existing function that will get // the leafExprType of the incoming array (getLeafExprType()), but we also need to check if the array // is jagged here, so we will do both tasks at once and save a little time. leafExprType = ExpressionType.Integer; int lenAtLevel2 = ml.CheckFunction("List"); if (lenAtLevel2 > 0) { leafExprType = ml.GetNextExpressionType(); foundLeafType = true; } // peel off all the elements in the first sublist. for (int j = 0; j < lenAtLevel2; j++) { loop.TransferExpression(ml); } // For each remaining sublist, check its length and peel off its members. At this point, though, we // cannot be guaranteed that all elements are lists (or, at least, guaranteed that it is an error // if they are not). They could be Null if the array is jagged: {{1, 2}, Null}. for (int i = 1; i < len; i++) { ExpressionType nextExprType = ml.GetNextExpressionType(); if (nextExprType == ExpressionType.Object) { isJagged = true; // OK to have null as an element in a jagged array. if (ml.GetObject() != null) { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE); } } else if (nextExprType == ExpressionType.Function) { int thisLength = ml.CheckFunction("List"); if (!foundLeafType && thisLength > 0) { leafExprType = ml.GetNextExpressionType(); foundLeafType = true; } if (thisLength != lenAtLevel2) { isJagged = true; break; } else { for (int j = 0; j < thisLength; j++) { loop.TransferExpression(ml); } } } else { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_DEPTH); } } // If the array is empty we cannot create a .NET array for it, as there is no type info for the array we are creating. if (!foundLeafType) { throw new MathLinkException(MathLinkException.MLE_EMPTY_ARRAY); } leafType = netTypeFromExpressionType(leafExprType, ml); ml.SeekMark(mark); if (isJagged) { ml.CheckFunction("List"); Array result = Array.CreateInstance(Array.CreateInstance(leafType, 0).GetType(), len); for (int i = 0; i < len; i++) { // Have to check if elements are lists or Null. ExpressionType nextExprType = ml.GetNextExpressionType(); if (nextExprType == ExpressionType.Function) { result.SetValue(ml.GetArray(leafType, 1), i); } else { string sym = ml.GetSymbol(); if (sym != "Null") { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE); } result.SetValue(null, i); } } return(result); } else { return(ml.GetArray(leafType, 2)); } } else { // For an Array argument slot, we only support passing >2-deep rectangular arrays, not jagged. for (int i = 0; i < depth; i++) { ml.CheckFunction("List"); } leafExprType = ml.GetNextExpressionType(); leafType = netTypeFromExpressionType(leafExprType, ml); ml.SeekMark(mark); return(ml.GetArray(leafType, depth)); } } finally { if (loop != null) { loop.Close(); } ml.DestroyMark(mark); } } else { // We have the actual array shape encoded in the type. Either [], [,..], or array-of-arrays: [][].... int arrayRank = t.GetArrayRank(); Type elementType = t.GetElementType(); if (elementType.IsArray) { if (arrayRank > 1) { // Don't support multidimensional array at start of jagged array: [][,]. Recall that mixed array types // are read backwards, so [][,] is a 2-deep array of element type []. throw new MathLinkException(MathLinkException.MLE_MULTIDIM_ARRAY_OF_ARRAY); } len = ml.CheckFunction("List"); Array result = Array.CreateInstance(elementType, len); for (int i = 0; i < len; i++) { ExpressionType nextExprType = ml.GetNextExpressionType(); if (nextExprType == ExpressionType.Function) { result.SetValue(readArbitraryArray(ml, elementType), i); } else { string sym = ml.GetSymbol(); if (sym != "Null") { throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE); } result.SetValue(null, i); } } return(result); } else if (elementType == typeof(Array)) { // Don't support Array[]. throw new MathLinkException(MathLinkException.MLE_ARRAY_OF_ARRAYCLASS); } else { return(ml.GetArray(elementType, arrayRank)); } } }