/// <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());
    }
Ejemplo n.º 2
0
    /// <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);
    }
Ejemplo n.º 4
0
    /// <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)
    ///     =&gt; left.ZipFold&lt;Expr, bool&gt;(
    ///         right,
    ///         (xs, results) =&gt;
    ///         {
    ///             switch (xs[0])
    ///             {
    ///                 case Add a1 when xs[1] is Add a2:
    ///                     return results.All(x =&gt; 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));
    }