public static Expression Reduce(Expression @object, Func <Expression, Expression> reduce, List <ParameterExpression> vars, List <Expression> stmts) { if (@object.IsPure(readOnly: true)) { if (@object is ParameterExpression p) { // NB: Trick some purity checks down the line to not introduce more temps. Patterns do not cause assignments // to variables, so a variable input can be considered to be pure. @object = new ReadOnlyTemporaryVariableExpression(p); } return(reduce(@object)); } else { var p = Expression.Parameter(@object.Type, "__obj"); var r = new ReadOnlyTemporaryVariableExpression(p); if (vars != null) { vars.Add(p); stmts.Add(Expression.Assign(p, @object)); return(reduce(r)); } else { return (Expression.Block( new[] { p }, Expression.Assign(p, @object), reduce(r) )); } } }
/// <summary> /// Reduces the pattern by applying it the specified object. /// </summary> /// <param name="object">The object to match the pattern against.</param> /// <returns>The expression representing the pattern applied to the specified object.</returns> internal override Expression Reduce(Expression @object) { Expression GetLength(Expression obj) { if (LengthAccess.Body is MemberExpression m && m.Expression == LengthAccess.Parameters[0]) { return(m.Update(obj)); } return(Expression.Invoke(LengthAccess, obj)); } Expression GetElement(Expression obj, Expression length, Index index) { var indexExpr = Expression.Constant(index); if (IndexerAccess.Body is ArrayAccessCSharpExpression a && a.Array == IndexerAccess.Parameters[0] && a.Indexes.Count == 1 && a.Indexes[0] == IndexerAccess.Parameters[1]) { var arrayAccess = a.Update(obj, new[] { indexExpr }); return(arrayAccess.Reduce(length)); } if (IndexerAccess.Body is IndexerAccessCSharpExpression i && i.Object == IndexerAccess.Parameters[0] && i.Argument == IndexerAccess.Parameters[1]) { var indexerAccess = i.Update(obj, indexExpr); return(indexerAccess.Reduce(length)); } return(Expression.Invoke(IndexerAccess, obj, indexExpr)); } bool HasSlice() { foreach (var p in Patterns) { if (p.PatternType == CSharpPatternType.Slice) { return(true); } } return(false); } return(PatternHelpers.Reduce(@object, obj => { var exit = Expression.Label(typeof(bool), "__return"); var vars = new List <ParameterExpression>(); var stmts = new List <Expression>(); obj = AddNullCheck(obj, typeCheck: null, exit, vars, stmts); var n = Patterns.Count; var length = GetLength(obj); var hasSlice = HasSlice(); if (hasSlice) { var lengthTemp = Expression.Parameter(length.Type, "__len"); vars.Add(lengthTemp); stmts.Add(Expression.Assign(lengthTemp, length)); length = new ReadOnlyTemporaryVariableExpression(lengthTemp); } var negatedLengthCheck = hasSlice ? Expression.LessThan(length, CreateConstantInt32(n - 1)) : Expression.NotEqual(length, CreateConstantInt32(n)); AddFailIf(negatedLengthCheck, exit, stmts); var hasSeenSlice = false; for (int i = 0; i < n; i++) { var p = Patterns[i]; if (p is SliceCSharpPattern slice) { hasSeenSlice = true; var rangeStart = new Index(i); var rangeEnd = new Index(n - i - 1, fromEnd: true); var range = new Range(rangeStart, rangeEnd); var sliceCheck = slice.Reduce(obj, length, range); AddFailIfNot(sliceCheck, exit, stmts); } else if (p.PatternType != CSharpPatternType.Discard) { var index = hasSeenSlice ? new Index(n - i, fromEnd: true) : new Index(i); var element = GetElement(obj, length, index); var elementCheck = p.Reduce(element); AddFailIfNot(elementCheck, exit, stmts); } } if (Variable != null) { stmts.Add(Expression.Assign(Variable, obj)); } stmts.Add(Expression.Label(exit, ConstantTrue)); return Expression.Block(vars, stmts); })); }