Class ExpressionExtensions
Extension methods for Expressions.
Inheritance
- Object
- ExpressionExtensions
Declaration
public static class ExpressionExtensions : Object
Methods
ChildrenInContext(Expression)
Returns an array containing each immediate child of
value paired with a function to replace the child.
This is typically useful when you need to replace a node's children one at a time,
such as during mutation testing.
The replacement function can be seen as the "context" of the child; calling the function with a new child "plugs the hole" in the context.
SelfAndDescendantsInContext<T>(IRewriter<T>, T)DescendantsAndSelfInContext<T>(IRewriter<T>, T)Declaration
public static ValueTuple<Expression, Func<Expression, Expression>>[] ChildrenInContext(this Expression value)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to get the contexts for the immediate children |
Returns
| Type | Description |
|---|---|
See Also
CountChildren(Expression)
Count the immediate children of the value. CountChildren()
Declaration
public static int CountChildren(this Expression value)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value |
Returns
| Type | Description |
|---|---|
|
Examples
Given a representation of the expression (1+2)+3,
Expr expr = new Add(
new Add(
new Lit(1),
new Lit(2)
),
new Lit(3)
);CountChildren(T) counts the immediate children of the topmost (Add) node.
Assert.Equal(2, rewriter.CountChildren(expr));
See Also
Cursor(Expression)
Create a Cursor<T>(IRewriter<T>, T) focused on the root node of value.
Declaration
public static Cursor<Expression> Cursor(this Expression value)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The root node on which the newly created Cursor<T>(IRewriter<T>, T) should be focused |
Returns
| Type | Description |
|---|---|
A Cursor<T>(IRewriter<T>, T) focused on the root node of |
See Also
DescendantAt(Expression, IEnumerable<Direction>)
Returns the descendant at a particular location in value
Declaration
public static Expression DescendantAt(this Expression value, IEnumerable<Direction> path)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The rewritable tree type |
|
path |
The route to take to find the descendant |
Returns
| Type | Description |
|---|---|
The descendant found by following the directions in |
Exceptions
| Type | Condition |
|---|---|
Thrown if |
See Also
DescendantsAndSelf(Expression)
Yields all of the nodes in the tree represented by value, starting at the bottom.
This is a depth-first post-order traversal.
SelfAndDescendants<T>(IRewriter<T>, T)Declaration
public static IEnumerable<Expression> DescendantsAndSelf(this Expression value)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to traverse |
Returns
| Type | Description |
|---|---|
An enumerable containing all of the nodes in the tree represented by |
Examples
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));
See Also
DescendantsAndSelfInContext(Expression)
Yields each node in the tree represented by 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.
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.
This is a depth-first post-order traversal.
DescendantsAndSelf<T>(IRewriter<T>, T)ChildrenInContext<T>(IRewriter<T>, T)SelfAndDescendantsInContext<T>(IRewriter<T>, T)Declaration
public static IEnumerable<ValueTuple<Expression, Func<Expression, Expression>>> DescendantsAndSelfInContext(this Expression value)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to get the contexts for the descendants |
Returns
| Type | Description |
|---|---|
IEnumerable<ValueTuple<Expression, Func<Expression, Expression>>> |
See Also
Fold<T>(Expression, SpanFunc<T, Expression, T>)
Flattens all the nodes in the tree represented by value into a single result,
using an aggregation function to combine each node with the results of folding its children.
Declaration
public static T Fold<T>(this Expression value, SpanFunc<T, Expression, T> func)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to fold |
|
SpanFunc<T, Expression, T> |
func |
The aggregation function |
Returns
| Type | Description |
|---|---|
T |
The result of aggregating the tree represented by |
Type Parameters
| Name | Description |
|---|---|
T |
See Also
Fold<T>(Expression, Func<Memory<T>, Expression, ValueTask<T>>)
Flattens all the nodes in the tree represented by value into a single result,
using an asynchronous aggregation function to combine each node with the results of folding its children.
Declaration
public static ValueTask<T> Fold<T>(this Expression value, Func<Memory<T>, Expression, ValueTask<T>> func)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to fold |
|
Func<Memory<T>, Expression, ValueTask<T>> |
func |
The asynchronous aggregation function |
Returns
| Type | Description |
|---|---|
ValueTask<T> |
The result of aggregating the tree represented by |
Type Parameters
| Name | Description |
|---|---|
T |
Remarks
This method is not available on platforms which do not support ValueTask.
See Also
GetChildren(Expression)
Get the immediate children of the value. GetChildren(Span<T>)
Declaration
public static Expression[] GetChildren(this Expression value)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value |
Returns
| Type | Description |
|---|---|
The immediate children of |
Examples
Given a representation of the expression (1+2)+3,
Expr expr = new Add(
new Add(
new Lit(1),
new Lit(2)
),
new Lit(3)
);GetChildren<T>(IRewriter<T>, T) returns the immediate children of the topmost node.
Expr[] expected = new[]
{
new Add(
new Lit(1),
new Lit(2)
),
new Lit(3)
};
Assert.Equal(expected, rewriter.GetChildren(expr));
See Also
GetChildren(Expression, Span<Expression>)
Copy the immediate children of the value into childrenReceiver.
GetChildren(Span<T>)
Declaration
public static void GetChildren(this Expression value, Span<Expression> childrenReceiver)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value |
|
childrenReceiver |
A Span<T> to copy |
Examples
Given a representation of the expression (1+2)+3,
Expr expr = new Add(
new Add(
new Lit(1),
new Lit(2)
),
new Lit(3)
);GetChildren(Span<T>, T) copies the immediate children of the topmost node into the span.
Expr[] expected = new[]
{
new Add(
new Lit(1),
new Lit(2)
),
new Lit(3)
};
var array = new Expr[rewriter.CountChildren(expr)];
rewriter.GetChildren(array, expr);
Assert.Equal(expected, array);
See Also
ReplaceDescendantAt<T>(Expression, IEnumerable<Direction>, Expression)
Replaces the descendant at a particular location in value
Declaration
public static Expression ReplaceDescendantAt<T>(this Expression value, IEnumerable<Direction> path, Expression newDescendant)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The rewritable tree type |
|
path |
The route to take to find the descendant |
|
newDescendant |
The replacement descendant |
Returns
| Type | Description |
|---|---|
A copy of |
Type Parameters
| Name | Description |
|---|---|
T |
Exceptions
| Type | Condition |
|---|---|
Thrown if |
See Also
Rewrite(Expression, Func<Expression, Expression>)
Rebuild a tree by applying a transformation function to every node from bottom to top.
Declaration
public static Expression Rewrite(this Expression value, Func<Expression, Expression> transformer)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to rewrite |
|
transformer |
The transformation function to apply to every node in the tree |
Returns
| Type | Description |
|---|---|
The result of applying |
Examples
Given a representation of the expression (1+2)+3,
Expr expr = new Add(
new Add(
new Lit(1),
new Lit(2)
),
new Lit(3)
);Rewrite<T>(IRewriter<T>, Func<T, T>, T) replaces the leaves of the tree with the result of calling transformer,
then replaces their parents with the result of calling transformer, and so on.
By the end, Rewrite<T>(IRewriter<T>, Func<T, T>, T) has traversed the whole tree.
Expr expected = transformer(new Add(
transformer(new Add(
transformer(new Lit(1)),
transformer(new Lit(2))
)),
transformer(new Lit(3))
));
Assert.Equal(expected, rewriter.Rewrite(transformer, expr));
See Also
Rewrite(Expression, Func<Expression, ValueTask<Expression>>)
Rebuild a tree by applying an asynchronous transformation function to every node from bottom to top.
Declaration
public static ValueTask<Expression> Rewrite(this Expression value, Func<Expression, ValueTask<Expression>> transformer)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to rewrite |
|
transformer |
The asynchronous transformation function to apply to every node in the tree |
Returns
| Type | Description |
|---|---|
The result of applying |
Remarks
This method is not available on platforms which do not support ValueTask.
Examples
Given a representation of the expression (1+2)+3,
Expr expr = new Add(
new Add(
new Lit(1),
new Lit(2)
),
new Lit(3)
);Rewrite<T>(IRewriter<T>, Func<T, ValueTask<T>>, T) replaces the leaves of the tree with the result of calling transformer,
then replaces their parents with the result of calling transformer, and so on.
By the end, Rewrite<T>(IRewriter<T>, Func<T, ValueTask<T>>, T) has traversed the whole tree.
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));
See Also
RewriteChildren(Expression, Func<Expression, Expression>)
Update the immediate children of the value by applying a transformation function to each one.
Declaration
public static Expression RewriteChildren(this Expression value, Func<Expression, Expression> transformer)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The old value, whose immediate children should be transformed by |
|
transformer |
A transformation function to apply to each of |
Returns
| Type | Description |
|---|---|
A copy of |
See Also
RewriteChildren(Expression, Func<Expression, ValueTask<Expression>>)
Update the immediate children of the value by applying an asynchronous transformation function to each one.
Declaration
public static ValueTask<Expression> RewriteChildren(this Expression value, Func<Expression, ValueTask<Expression>> transformer)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The old value, whose immediate children should be transformed by |
|
transformer |
An asynchronous transformation function to apply to each of |
Returns
| Type | Description |
|---|---|
A copy of |
Remarks
This method is not available on platforms which do not support ValueTask.
See Also
RewriteDescendantAt<T>(Expression, IEnumerable<Direction>, Func<Expression, Expression>)
Apply a function at a particular location in value
Declaration
public static Expression RewriteDescendantAt<T>(this Expression value, IEnumerable<Direction> path, Func<Expression, Expression> transformer)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The rewritable tree type |
|
path |
The route to take to find the descendant |
|
transformer |
A function to calculate a replacement for the descendant |
Returns
| Type | Description |
|---|---|
A copy of |
Type Parameters
| Name | Description |
|---|---|
T |
Exceptions
| Type | Condition |
|---|---|
Thrown if |
See Also
RewriteDescendantAt<T>(Expression, IEnumerable<Direction>, Func<Expression, ValueTask<Expression>>)
Apply an asynchronous function at a particular location in value
Declaration
public static ValueTask<Expression> RewriteDescendantAt<T>(this Expression value, IEnumerable<Direction> path, Func<Expression, ValueTask<Expression>> transformer)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The rewritable tree type |
|
path |
The route to take to find the descendant |
|
transformer |
An asynchronous function to calculate a replacement for the descendant |
Returns
| Type | Description |
|---|---|
A copy of |
Type Parameters
| Name | Description |
|---|---|
T |
Remarks
This method is not available on platforms which do not support ValueTask.
Exceptions
| Type | Condition |
|---|---|
Thrown if |
See Also
RewriteIter(Expression, Func<Expression, Expression>)
Rebuild a tree by repeatedly applying a transformation function to every node in the tree,
until a fixed point is reached. transformer should always eventually return
its argument unchanged, or this method will loop.
That is, x.RewriteIter(transformer).SelfAndDescendants().All(x => transformer(x) == x).
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.
Declaration
public static Expression RewriteIter(this Expression value, Func<Expression, Expression> transformer)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to rewrite |
|
transformer |
A transformation function to apply to every node in |
Returns
| Type | Description |
|---|---|
The result of applying |
See Also
RewriteIter(Expression, Func<Expression, ValueTask<Expression>>)
Rebuild a tree by repeatedly applying an asynchronous transformation function to every node in the tree,
until a fixed point is reached. transformer should always eventually return
its argument unchanged, or this method will loop.
That is, x.RewriteIter(transformer).SelfAndDescendants().All(x => await transformer(x) == x).
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.
Declaration
public static ValueTask<Expression> RewriteIter(this Expression value, Func<Expression, ValueTask<Expression>> transformer)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to rewrite |
|
transformer |
An asynchronous transformation function to apply to every node in |
Returns
| Type | Description |
|---|---|
The result of applying |
Remarks
This method is not available on platforms which do not support ValueTask.
See Also
SelfAndDescendants(Expression)
Yields all of the nodes in the tree represented by value, starting at the top.
This is a depth-first pre-order traversal.
DescendantsAndSelf<T>(IRewriter<T>, T)Declaration
public static IEnumerable<Expression> SelfAndDescendants(this Expression value)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to traverse |
Returns
| Type | Description |
|---|---|
An enumerable containing all of the nodes in the tree represented by |
Examples
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));
See Also
SelfAndDescendantsBreadthFirst(Expression)
Yields all of the nodes in the tree represented by value in a breadth-first traversal order.
This is a breadth-first pre-order traversal.
Declaration
public static IEnumerable<Expression> SelfAndDescendantsBreadthFirst(this Expression value)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to traverse |
Returns
| Type | Description |
|---|---|
An enumerable containing all of the nodes in the tree represented by |
See Also
SelfAndDescendantsInContext(Expression)
Yields each node in the tree represented by value
paired with a function to replace the node, starting at the top.
This is typically useful when you need to replace nodes one at a time,
such as during mutation testing.
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.
This is a depth-first pre-order traversal.
SelfAndDescendants<T>(IRewriter<T>, T)ChildrenInContext<T>(IRewriter<T>, T)DescendantsAndSelfInContext<T>(IRewriter<T>, T)Declaration
public static IEnumerable<ValueTuple<Expression, Func<Expression, Expression>>> SelfAndDescendantsInContext(this Expression value)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to get the contexts for the descendants |
Returns
| Type | Description |
|---|---|
IEnumerable<ValueTuple<Expression, Func<Expression, Expression>>> |
See Also
SelfAndDescendantsInContextBreadthFirst(Expression)
Yields each node in the tree represented by 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.
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.
This is a breadth-first pre-order traversal.
SelfAndDescendants<T>(IRewriter<T>, T)ChildrenInContext<T>(IRewriter<T>, T)DescendantsAndSelfInContext<T>(IRewriter<T>, T)Declaration
public static IEnumerable<ValueTuple<Expression, Func<Expression, Expression>>> SelfAndDescendantsInContextBreadthFirst(this Expression value)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The value to get the contexts for the descendants |
Returns
| Type | Description |
|---|---|
IEnumerable<ValueTuple<Expression, Func<Expression, Expression>>> |
See Also
SetChildren(Expression, ReadOnlySpan<Expression>)
Set the immediate children of the value.
Callers should ensure that newChildren contains the same number of children as was returned by
GetChildren(Span<T>, T).
Declaration
public static Expression SetChildren(this Expression value, ReadOnlySpan<Expression> newChildren)
Parameters
| Type | Name | Description |
|---|---|---|
value |
The old value, whose immediate children should be replaced |
|
newChildren |
The new children |
Returns
| Type | Description |
|---|---|
A copy of |
Examples
Given a representation of the expression (1+2)+3,
Expr expr = new Add(
new Add(
new Lit(1),
new Lit(2)
),
new Lit(3)
);SetChildren(ReadOnlySpan<T>, T) replaces the immediate children of the topmost node.
Expr expected = new Add(
new Lit(4),
new Lit(5)
);
Assert.Equal(expected, rewriter.SetChildren(Children.Two(new Lit(4), new Lit(5)), expr));
See Also
ZipFold<U>(Expression, Expression, Func<Expression, Expression, IAsyncEnumerable<U>, ValueTask<U>>)
Flatten all of the nodes in the trees represented by 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
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.
Declaration
public static ValueTask<U> ZipFold<U>(this Expression value1, Expression value2, Func<Expression, Expression, IAsyncEnumerable<U>, ValueTask<U>> func)
Parameters
| Type | Name | Description |
|---|---|---|
value1 |
||
value2 |
||
Func<Expression, Expression, IAsyncEnumerable<U>, ValueTask<U>> |
func |
The aggregation function |
Returns
| Type | Description |
|---|---|
ValueTask<U> |
The result of aggregating the two trees |
Type Parameters
| Name | Description |
|---|---|
U |
Remarks
This method is not available on platforms which do not support ValueTask and IAsyncEnumerable<T>.
Examples
Here's an example of using ZipFold<T, U>(IRewriter<T>, Func<T[], IAsyncEnumerable<U>, ValueTask<U>>, T[]) to test if two trees are syntactically equal.
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;
}
}
);
See Also
ZipFold<U>(Expression, Expression, Func<Expression, Expression, IEnumerable<U>, U>)
Flatten all of the nodes in the trees represented by 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
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.
Declaration
public static U ZipFold<U>(this Expression value1, Expression value2, Func<Expression, Expression, IEnumerable<U>, U> func)
Parameters
| Type | Name | Description |
|---|---|---|
value1 |
||
value2 |
||
Func<Expression, Expression, IEnumerable<U>, U> |
func |
The aggregation function |
Returns
| Type | Description |
|---|---|
U |
The result of aggregating the two trees |
Type Parameters
| Name | Description |
|---|---|
U |
Examples
Here's an example of using ZipFold<T, U>(IRewriter<T>, Func<T[], IEnumerable<U>, U>, T[]) to test if two trees are syntactically equal.
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;
}
}
);
See Also
ZipFold<U>(Expression[], Func<Expression[], IAsyncEnumerable<U>, ValueTask<U>>)
Flatten all of the nodes in the trees represented by 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
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.
Declaration
public static ValueTask<U> ZipFold<U>(this Expression[] values, Func<Expression[], IAsyncEnumerable<U>, ValueTask<U>> func)
Parameters
| Type | Name | Description |
|---|---|---|
values |
The trees to fold |
|
Func<Expression[], IAsyncEnumerable<U>, ValueTask<U>> |
func |
The aggregation function |
Returns
| Type | Description |
|---|---|
ValueTask<U> |
The result of aggregating the two trees |
Type Parameters
| Name | Description |
|---|---|
U |
Remarks
This method is not available on platforms which do not support ValueTask and IAsyncEnumerable<T>.
Examples
Here's an example of using ZipFold<T, U>(IRewriter<T>, Func<T[], IAsyncEnumerable<U>, ValueTask<U>>, T[]) to test if two trees are syntactically equal.
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;
}
}
);
See Also
ZipFold<U>(Expression[], Func<Expression[], IEnumerable<U>, U>)
Flatten all of the nodes in the trees represented by 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
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.
Declaration
public static U ZipFold<U>(this Expression[] values, Func<Expression[], IEnumerable<U>, U> func)
Parameters
| Type | Name | Description |
|---|---|---|
values |
The trees to fold |
|
Func<Expression[], IEnumerable<U>, U> |
func |
The aggregation function |
Returns
| Type | Description |
|---|---|
U |
The result of aggregating the two trees |
Type Parameters
| Name | Description |
|---|---|
U |
Examples
Here's an example of using ZipFold<T, U>(IRewriter<T>, Func<T[], IEnumerable<U>, U>, T[]) to test if two trees are syntactically equal.
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;
}
}
);