/// <summary> /// Yields all of the nodes in the tree represented by <paramref name="value"/>, starting at the bottom. /// /// <para> /// This is a depth-first post-order traversal. /// </para> /// /// <seealso cref="SelfAndDescendants"/> /// </summary> /// <example> /// <code> /// Expr expr = new Add( /// new Add( /// new Lit(1), /// new Lit(2) /// ), /// new Lit(3) /// ); /// Expr[] expected = new[] /// { /// new Lit(1), /// new Lit(2), /// new Add(new Lit(1), new Lit(2)), /// new Lit(3), /// expr /// }; /// Assert.Equal(expected, rewriter.DescendantsAndSelf(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 bottom.</returns> public static IEnumerable <T> DescendantsAndSelf <T>(this IRewriter <T> rewriter, T value) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } IEnumerable <T> Iterator() { var stack = new Stack <DescendantsAndSelfFrame <T> >(); var initialArray = ArrayPool <T> .Shared.Rent(1); initialArray[0] = value; var enumerator = new DescendantsAndSelfFrame <T>(initialArray, 1); do { while (enumerator.MoveNext()) { stack.Push(enumerator); var count = rewriter.CountChildren(enumerator.Current); var array = ArrayPool <T> .Shared.Rent(count); rewriter.GetChildren(array.AsSpan()[..count], enumerator.Current);
/// <summary> /// Rewrites the specified solution. /// </summary> /// <param name="originalSolution">The solution.</param> /// <param name="rewriter">The rewriter.</param> /// <returns></returns> public Result <Solution> Rewrite(Solution originalSolution, IRewriter rewriter) => Modify( original: originalSolution, getIntermediateValues: solution => solution.ProjectIds, getEntry: (solution, projectId) => solution.GetProject(projectId), modifyEntry: project => { var projectContext = ProjectContext.Create(project); if (projectContext.IsFailure) { return(Result.Fail <Project>(projectContext.Error)); } else { var rewriteResult = Rewrite(projectContext.Value, rewriter); if (rewriteResult.IsFailure) { return(Result.Fail <Project>(rewriteResult.Error)); } else { return(Result.Ok(rewriteResult.Value.Project)); } } }, getReturnValue: project => project.Solution);
/// <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()); }
public PythonNode Rewrite(IRewriter visitor) { var result = visitor.Rewrite(this); result.Children = result.Children.Select(child => child.Rewrite(visitor)).ToList(); return(result); }
/// <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()); }
internal static T RewriteChildrenInternal <T>( this IRewriter <T> rewriter, Func <T, T> transformer, T value, ref ChunkStack <T> chunks ) => rewriter.WithChildren( (children, t) => { var(r, v, f) = t; var changed = false; for (var i = 0; i < children.Length; i++) { var child = children[i]; var newChild = f(child); children[i] = newChild; changed |= !ReferenceEquals(newChild, child); } if (changed) { return(r.SetChildren(children, v)); } return(v); }, (rewriter, value, transformer), value, ref chunks );
/// <summary> /// Rewrites the specified document context. /// </summary> /// <param name="document">The original document.</param> /// <param name="rewriter">The rewriter.</param> /// <returns></returns> public Result <DocumentContext> Rewrite(DocumentContext document, IRewriter rewriter) { //only rewrite .vb and .cs if (document.Document.SourceCodeKind != SourceCodeKind.Regular || document.Document.Name.ToLowerInvariant().EndsWith(".assemblyattributes.cs") || document.Document.Name.ToLowerInvariant().EndsWith(".assemblyinfo.cs")) { return(Result.Ok(document)); } var newDocResult = rewriter.Rewrite(document, _log); if (newDocResult.IsFailure) { return(Result.Fail <DocumentContext>(newDocResult.Error)); } var newDoc = newDocResult.Value; var newProject = newDoc.Project; var newProjectContext = ProjectContext.Create(newProject); if (newProjectContext.IsFailure) { return(Result.Fail <DocumentContext>(newProjectContext.Error)); } else { var newDocContext = newProjectContext.Value .Documents .Where(x => x.Document.Id == document.Document.Id) .Single(); return(Result.Ok(newDocContext)); } }
internal static ValueTask <T> RewriteChildrenInternal <T>( this IRewriter <T> rewriter, Func <T, ValueTask <T> > transformer, T value, Box <ChunkStack <T> > chunks ) => rewriter.WithChildren( async(children, t) => { var(r, v, f) = t; var changed = false; for (var i = 0; i < children.Length; i++) { var child = children.Span[i]; var newChild = await f(child).ConfigureAwait(false); children.Span[i] = newChild; changed |= !ReferenceEquals(newChild, child); } if (changed) { return(r.SetChildren(children.Span, v)); } return(v); }, (rewriter, value, transformer), value, chunks );
/// <summary> /// Create a <see cref="Cursor{T}"/> focused on the root node of <paramref name="value"/>. /// </summary> /// <param name="rewriter">The rewriter</param> /// <param name="value">The root node on which the newly created <see cref="Cursor{T}"/> should be focused</param> /// <returns>A <see cref="Cursor{T}"/> focused on the root node of <paramref name="value"/></returns> public static Cursor <T> Cursor <T>(this IRewriter <T> rewriter, T value) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } return(new Cursor <T>(rewriter, value)); }
internal Cursor(IRewriter <T> rewriter, T top) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } _rewriter = rewriter; _path = new Stack <Step <T> >(); _prevSiblings = new Stack <T>(); _focus = top; _nextSiblings = new Stack <T>(); }
/// <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> /// Rebuild a tree by applying an asynchronous transformation function to every node from bottom to top. /// </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="Rewrite{T}(IRewriter{T}, Func{T, ValueTask{T}}, T)"/> replaces the leaves of the tree with the result of calling <paramref name="transformer"/>, /// then replaces their parents with the result of calling <paramref name="transformer"/>, and so on. /// By the end, <see cref="Rewrite{T}(IRewriter{T}, Func{T, ValueTask{T}}, T)"/> has traversed the whole tree. /// <code> /// Expr expected = await transformer(new Add( /// await transformer(new Add( /// await transformer(new Lit(1)), /// await transformer(new Lit(2)) /// )), /// await transformer(new Lit(3)) /// )); /// Assert.Equal(expected, await rewriter.Rewrite(transformer, expr)); /// </code> /// </example> /// <typeparam name="T">The rewritable tree type</typeparam> /// <param name="rewriter">The rewriter</param> /// <param name="transformer">The asynchronous transformation function to apply to every node in the tree</param> /// <param name="value">The value to rewrite</param> /// <returns> /// The result of applying <paramref name="transformer"/> to every node in the tree represented by <paramref name="value"/>. /// </returns> /// <remarks>This method is not available on platforms which do not support <see cref="ValueTask"/>.</remarks> public static async ValueTask <T> Rewrite <T>(this IRewriter <T> rewriter, Func <T, ValueTask <T> > transformer, T value) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } if (transformer == null) { throw new ArgumentNullException(nameof(transformer)); } using var traversal = new RewriteAsyncTraversal <T>(rewriter, transformer); return(await traversal.Go(value).ConfigureAwait(false)); }
/// <summary> /// Rebuild a tree by repeatedly applying a transformation function to every node in the tree, /// until a fixed point is reached. <paramref name="transformer"/> should always eventually return /// its argument unchanged, or this method will loop. /// That is, <c>x.RewriteIter(transformer).SelfAndDescendants().All(x => transformer(x) == x)</c>. /// <para> /// This is typically useful when you want to put your tree into a normal form /// by applying a collection of rewrite rules until none of them can fire any more. /// </para> /// </summary> /// <typeparam name="T">The rewritable tree type</typeparam> /// <param name="rewriter">The rewriter</param> /// <param name="transformer"> /// A transformation function to apply to every node in <paramref name="value"/> repeatedly. /// </param> /// <param name="value">The value to rewrite</param> /// <returns> /// The result of applying <paramref name="transformer"/> to every node in the tree /// represented by <paramref name="value"/> repeatedly until a fixed point is reached. /// </returns> public static T RewriteIter <T>(this IRewriter <T> rewriter, Func <T, T> transformer, T value) where T : class { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } if (transformer == null) { throw new ArgumentNullException(nameof(transformer)); } using var traversal = new RewriteIterTraversal <T>(rewriter, transformer); return(traversal.Go(value)); }
/// <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); }
/// <summary> /// Returns the descendant at a particular location in <paramref name="value"/> /// </summary> /// <param name="rewriter">The rewriter</param> /// <param name="value">The rewritable tree type</param> /// <param name="path">The route to take to find the descendant</param> /// <exception cref="InvalidOperationException"> /// Thrown if <paramref name="path"/> leads off the edge of the tree /// </exception> /// <returns>The descendant found by following the directions in <paramref name="path"/></returns> public static T DescendantAt <T>(this IRewriter <T> rewriter, IEnumerable <Direction> path, T value) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } if (path == null) { throw new ArgumentNullException(nameof(path)); } var cursor = rewriter.Cursor(value); cursor.Follow(path); return(cursor.Focus); }
/// <summary> /// Flattens all the nodes in the tree represented by <paramref name="value"/> into a single result, /// using an aggregation function to combine each node with the results of folding its children. /// </summary> /// <typeparam name="T">The rewritable tree type</typeparam> /// <typeparam name="U">The type of the result of aggregation</typeparam> /// <param name="rewriter">The rewriter</param> /// <param name="func">The aggregation function</param> /// <param name="value">The value to fold</param> /// <returns>The result of aggregating the tree represented by <paramref name="value"/>.</returns> public static U Fold <T, U>(this IRewriter <T> rewriter, SpanFunc <U, T, U> func, T value) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } if (func == null) { throw new ArgumentNullException(nameof(func)); } var closure = new FoldClosure <T, U>(rewriter, func); var result = closure.Go(value); closure.Dispose(); return(result); }
/// <summary> /// Update the immediate children of the value by applying a transformation function to each one. /// </summary> /// <typeparam name="T">The rewritable tree type</typeparam> /// <param name="rewriter">The rewriter</param> /// <param name="transformer">A transformation function to apply to each of <paramref name="value"/>'s immediate children.</param> /// <param name="value">The old value, whose immediate children should be transformed by <paramref name="transformer"/>.</param> /// <returns>A copy of <paramref name="value"/> with updated children.</returns> public static T RewriteChildren <T>(this IRewriter <T> rewriter, Func <T, T> transformer, T value) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } if (transformer == null) { throw new ArgumentNullException(nameof(transformer)); } var chunks = new ChunkStack <T>(); var result = rewriter.RewriteChildrenInternal(transformer, value, ref chunks); chunks.Dispose(); 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); }
/// <summary> /// Update the immediate children of the value by applying an asynchronous transformation function to each one. /// </summary> /// <typeparam name="T">The rewritable tree type</typeparam> /// <param name="rewriter">The rewriter</param> /// <param name="transformer">An asynchronous transformation function to apply to each of <paramref name="value"/>'s immediate children.</param> /// <param name="value">The old value, whose immediate children should be transformed by <paramref name="transformer"/>.</param> /// <returns>A copy of <paramref name="value"/> with updated children.</returns> /// <remarks>This method is not available on platforms which do not support <see cref="ValueTask"/>.</remarks> public static async ValueTask <T> RewriteChildren <T>( this IRewriter <T> rewriter, Func <T, ValueTask <T> > transformer, T value ) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } if (transformer == null) { throw new ArgumentNullException(nameof(transformer)); } var chunks = new Box <ChunkStack <T> >(new ChunkStack <T>()); var result = await rewriter.RewriteChildrenInternal(transformer, value, chunks).ConfigureAwait(false); chunks.Value.Dispose(); return(result); }
/// <summary> /// Apply an asynchronous function at a particular location in <paramref name="value"/> /// </summary> /// <param name="rewriter">The rewriter</param> /// <param name="value">The rewritable tree type</param> /// <param name="path">The route to take to find the descendant</param> /// <param name="transformer">An asynchronous function to calculate a replacement for the descendant</param> /// <exception cref="InvalidOperationException"> /// Thrown if <paramref name="path"/> leads off the edge of the tree /// </exception> /// <returns> /// A copy of <paramref name="value"/> with the result of <paramref name="transformer"/> placed at the location indicated by <paramref name="path"/> /// </returns> /// <remarks>This method is not available on platforms which do not support <see cref="ValueTask"/>.</remarks> public static async ValueTask <T> RewriteDescendantAt <T>(this IRewriter <T> rewriter, IEnumerable <Direction> path, Func <T, ValueTask <T> > transformer, T value) { if (rewriter == null) { throw new ArgumentNullException(nameof(rewriter)); } if (path == null) { throw new ArgumentNullException(nameof(path)); } if (transformer == null) { throw new ArgumentNullException(nameof(transformer)); } var cursor = rewriter.Cursor(value); cursor.Follow(path); cursor.Focus = await transformer(cursor.Focus).ConfigureAwait(false); cursor.Top(); return(cursor.Focus); }
/// <summary> /// Yields each node in the tree represented by <paramref name="value"/> /// paired with a function to replace the node, in a breadth-first traversal order. /// This is typically useful when you need to replace nodes one at a time, /// such as during mutation testing. /// /// <para> /// The replacement function can be seen as the "context" of the node; calling the /// function with a new node "plugs the hole" in the context. /// </para> /// /// <para> /// This is a breadth-first pre-order traversal. /// </para> /// /// <seealso cref="SelfAndDescendants"/> /// <seealso cref="ChildrenInContext"/> /// <seealso cref="DescendantsAndSelfInContext"/> /// </summary> /// <param name="rewriter">The rewriter</param> /// <param name="value">The value to get the contexts for the descendants</param> public static IEnumerable <(T item, Func <T, T> replace)> SelfAndDescendantsInContextBreadthFirst <T>(this IRewriter <T> rewriter, T value) { if (rewriter == null)
public RewriteIterAsyncTraversal(IRewriter <T> rewriter, Func <T, ValueTask <T> > transformer) : base(rewriter, transformer) { }
public FoldClosure(IRewriter <T> rewriter, SpanFunc <U, T, U> func) : base(rewriter) { _func = func; }
/// <summary> /// Yields each node in the tree represented by <paramref name="value"/> /// paired with a function to replace the node, starting at the bottom. /// This is typically useful when you need to replace nodes one at a time, /// such as during mutation testing. /// /// <para> /// The replacement function can be seen as the "context" of the node; calling the /// function with a new node "plugs the hole" in the context. /// </para> /// /// <para> /// This is a depth-first post-order traversal. /// </para> /// /// <seealso cref="DescendantsAndSelf"/> /// <seealso cref="ChildrenInContext"/> /// <seealso cref="SelfAndDescendantsInContext"/> /// </summary> /// <param name="rewriter">The rewriter</param> /// <param name="value">The value to get the contexts for the descendants</param> public static IEnumerable <(T item, Func <T, T> replace)> DescendantsAndSelfInContext <T>(this IRewriter <T> rewriter, T value) { if (rewriter == null)
private static R WithChildren_Fast <T, R>(this IRewriter <T> rewriter, SpanFunc <T, R> action, T value, int count) { var buffer = new FixedSizeBuffer4 <T>(); var span = buffer.AsSpan()[..count];
public RewriteTraversal(IRewriter <T> rewriter, Func <T, T> transformer) : base(rewriter) { Transformer = transformer; }
public RewriteIterTraversal(IRewriter <T> rewriter, Func <T, T> transformer) : base(rewriter, transformer) { }
/// <summary> /// Rewrites the specified original project context. /// </summary> /// <param name="originalProjectContext">The original project context.</param> /// <param name="rewriter">The rewriter.</param> /// <returns></returns> public Result <ProjectContext> Rewrite(ProjectContext originalProjectContext, IRewriter rewriter) => Modify( original: originalProjectContext, getIntermediateValues: projectContext => projectContext.Project.DocumentIds, getEntry: (projectContext, documentId) => projectContext.Documents.Where(x => x.Document.Id == documentId).Single(), modifyEntry: documentContext => { var rewriteResult = Rewrite(documentContext, rewriter); return(rewriteResult); }, getReturnValue: documentContext => documentContext.ProjectContext);