/// <summary> /// Yields all of the nodes in the tree represented by <paramref name="value"/> in a breadth-first traversal order. /// /// <para> /// This is a breadth-first pre-order traversal. /// </para> /// /// </summary> /// <typeparam name="T">The rewritable tree type</typeparam> /// <param name="rewriter">The rewriter</param> /// <param name="value">The value to traverse</param> /// <returns>An enumerable containing all of the nodes in the tree represented by <paramref name="value"/> in a breadth-first traversal order.</returns> public static IEnumerable <T> SelfAndDescendantsBreadthFirst <T>(this IRewriter <T> rewriter, T value) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } IEnumerable <T> Iterator() { var queue = new PooledQueue <T>(); queue.AllocateRight(1)[0] = value; try { while (queue.Count != 0) { var x = queue.PopLeft(); yield return(x); var count = rewriter.CountChildren(x); var span = queue.AllocateRight(count); rewriter.GetChildren(span, x); } } finally { queue.Dispose(); } } return(Iterator()); }
/// <summary> /// Yields all of the nodes in the tree represented by <paramref name="value"/>, starting at the top. /// /// <para> /// This is a depth-first pre-order traversal. /// </para> /// /// <seealso cref="DescendantsAndSelf"/> /// </summary> /// <example> /// <code> /// Expr expr = new Add( /// new Add( /// new Lit(1), /// new Lit(2) /// ), /// new Lit(3) /// ); /// Expr[] expected = new[] /// { /// expr, /// new Add(new Lit(1), new Lit(2)), /// new Lit(1), /// new Lit(2), /// new Lit(3), /// }; /// Assert.Equal(expected, rewriter.SelfAndDescendants(expr)); /// </code> /// </example> /// <typeparam name="T">The rewritable tree type</typeparam> /// <param name="rewriter">The rewriter</param> /// <param name="value">The value to traverse</param> /// <returns>An enumerable containing all of the nodes in the tree represented by <paramref name="value"/>, starting at the top.</returns> public static IEnumerable <T> SelfAndDescendants <T>(this IRewriter <T> rewriter, T value) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } IEnumerable <T> Iterator() { var stack = new ChunkStack <T>(); stack.Allocate(1)[0] = value; try { while (!stack.IsEmpty) { var x = stack.Pop(); yield return(x); var count = rewriter.CountChildren(x); var span = stack.Allocate(count); rewriter.GetChildren(span, x); span.Reverse(); // pop them in left to right order } } finally { stack.Dispose(); } } return(Iterator()); }
/// <summary> /// Get the immediate children of the value. /// <seealso cref="IRewritable{T}.GetChildren"/> /// </summary> /// <example> /// Given a representation of the expression <c>(1+2)+3</c>, /// <code> /// Expr expr = new Add( /// new Add( /// new Lit(1), /// new Lit(2) /// ), /// new Lit(3) /// ); /// </code> /// <see cref="GetChildren"/> returns the immediate children of the topmost node. /// <code> /// Expr[] expected = new[] /// { /// new Add( /// new Lit(1), /// new Lit(2) /// ), /// new Lit(3) /// }; /// Assert.Equal(expected, rewriter.GetChildren(expr)); /// </code> /// </example> /// <typeparam name="T">The rewritable tree type</typeparam> /// <param name="rewriter">The rewriter</param> /// <param name="value">The value</param> /// <returns>The immediate children of <paramref name="value"/></returns> public static T[] GetChildren <T>(this IRewriter <T> rewriter, T value) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } var count = rewriter.CountChildren(value); var array = new T[count]; rewriter.GetChildren(array, value); return(array); }
/// <summary> /// Flatten all of the nodes in the trees represented by <paramref name="values"/> /// into a single value at the same time, using an aggregation function to combine /// nodes with the results of aggregating their children. /// The trees are iterated in lock-step, much like an n-ary /// <see cref="Enumerable.Zip{TFirst, TSecond, TResult}(IEnumerable{TFirst}, IEnumerable{TSecond}, Func{TFirst, TSecond, TResult})"/>. /// /// When trees are not the same size, the larger ones are /// truncated both horizontally and vertically. /// That is, if a pair of nodes have a different number of children, /// the rightmost children of the larger of the two nodes are discarded. /// </summary> /// <example> /// Here's an example of using <see cref="ZipFold{T, U}(IRewriter{T}, Func{T[], IEnumerable{U}, U}, T[])"/> to test if two trees are syntactically equal. /// <code> /// static bool Equals(this Expr left, Expr right) /// => left.ZipFold<Expr, bool>( /// right, /// (xs, results) => /// { /// switch (xs[0]) /// { /// case Add a1 when xs[1] is Add a2: /// return results.All(x => x); /// case Lit l1 when xs[1] is Lit l2: /// return l1.Value == l2.Value; /// default: /// return false; /// } /// } /// ); /// </code> /// </example> /// <typeparam name="T">The rewritable tree type</typeparam> /// <typeparam name="U">The return type of the aggregation</typeparam> /// <param name="rewriter">The rewriter</param> /// <param name="func">The aggregation function</param> /// <param name="values">The trees to fold</param> /// <returns>The result of aggregating the two trees</returns> public static U ZipFold <T, U>( this IRewriter <T> rewriter, Func <T[], IEnumerable <U>, U> func, // todo: should this be a ReadOnlySpanFunc? params T[] values ) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } if (func == null) { throw new ArgumentNullException(nameof(func)); } if (values == null) { throw new ArgumentNullException(nameof(values)); } U Go(T[] xs) => func(xs, ZipChildren(xs)); IEnumerable <U> ZipChildren(T[] xs) { var enumerators = new IEnumerator <T> [xs.Length]; for (var i = 0; i < xs.Length; i++) { enumerators[i] = rewriter.GetChildren(xs[i]).AsEnumerable().GetEnumerator(); } while (true) { var currents = new T[xs.Length]; for (var i = 0; i < enumerators.Length; i++) { var e = enumerators[i]; var hasNext = e.MoveNext(); if (!hasNext) { yield break; } currents[i] = e.Current; } yield return(Go(currents)); } } return(Go(values)); }
internal static async ValueTask <R> WithChildren <T, R>( this IRewriter <T> rewriter, Func <Memory <T>, ValueTask <R> > action, T value, Box <ChunkStack <T> > chunks ) { var count = rewriter.CountChildren(value); var memory = chunks.Value.AllocateMemory(count); rewriter.GetChildren(memory.Span, value); var result = await action(memory).ConfigureAwait(false); chunks.Value.Free(memory); return(result); }
internal static R WithChildren <T, R>( this IRewriter <T> rewriter, SpanFunc <T, R> action, T value, ref ChunkStack <T> chunks ) { var count = rewriter.CountChildren(value); if (count <= 4) { return(WithChildren_Fast(rewriter, action, value, count)); } var span = chunks.Allocate(count); rewriter.GetChildren(span, value); var result = action(span); chunks.Free(span); return(result); }
public void TestGetChildren_NoChildren() { var lit = new Lit(3); Assert.Empty(_rewriter.GetChildren(lit)); }