Skip to content

Commit 1ed6f4e

Browse files
committed
Spreads in arrays
1 parent ad5d55c commit 1ed6f4e

File tree

13 files changed

+155
-40
lines changed

13 files changed

+155
-40
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ A typical set of operators is supported:
136136
* Wildcard indexing - `a[?]` any, and `a[*]` all
137137
* Conditional `if a then b else c` (all branches required)
138138

139-
Comparision operators that act on text all accept an optional post-fix `ci` modifier to select case-insensitive comparisons:
139+
Comparision operators that act on text all accept an optional postfix `ci` modifier to select case-insensitive comparisons:
140140

141141
```
142142
User.Name like 'n%' ci

src/Serilog.Expressions/Expressions/Ast/ArrayExpression.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
using System.Linq;
33

44
namespace Serilog.Expressions.Ast
5-
{
5+
{
66
class ArrayExpression : Expression
77
{
8-
public ArrayExpression(Expression[] elements)
8+
public ArrayExpression(Element[] elements)
99
{
1010
Elements = elements ?? throw new ArgumentNullException(nameof(elements));
1111
}
1212

13-
public Expression[] Elements { get; }
13+
public Element[] Elements { get; }
1414

1515
public override string ToString()
1616
{
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Serilog.Expressions.Ast
2+
{
3+
abstract class Element
4+
{
5+
}
6+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Serilog.Expressions.Ast
2+
{
3+
class ItemElement : Element
4+
{
5+
public Expression Value { get; }
6+
7+
public ItemElement(Expression value)
8+
{
9+
Value = value;
10+
}
11+
12+
public override string ToString()
13+
{
14+
return Value.ToString();
15+
}
16+
}
17+
}

src/Serilog.Expressions/Expressions/Ast/Property.cs renamed to src/Serilog.Expressions/Expressions/Ast/PropertyMember.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
namespace Serilog.Expressions.Ast
44
{
5-
class Property : Member
5+
class PropertyMember : Member
66
{
77
public string Name { get; }
88
public Expression Value { get; }
99

10-
public Property(string name, Expression value)
10+
public PropertyMember(string name, Expression value)
1111
{
1212
Name = name;
1313
Value = value;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Serilog.Expressions.Ast
2+
{
3+
class SpreadElement : Element
4+
{
5+
public Expression Content { get; }
6+
7+
public SpreadElement(Expression content)
8+
{
9+
Content = content;
10+
}
11+
12+
public override string ToString()
13+
{
14+
return $"..{Content}";
15+
}
16+
}
17+
}

src/Serilog.Expressions/Expressions/Ast/Spread.cs renamed to src/Serilog.Expressions/Expressions/Ast/SpreadMember.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
namespace Serilog.Expressions.Ast
22
{
3-
class Spread : Member
3+
class SpreadMember : Member
44
{
55
public Expression Content { get; }
66

7-
public Spread(Expression content)
7+
public SpreadMember(Expression content)
88
{
99
Content = content;
1010
}

src/Serilog.Expressions/Expressions/Compilation/Arrays/ConstantArrayEvaluator.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ protected override Expression Transform(ArrayExpression ax)
1818
{
1919
// This should probably go depth-first.
2020

21-
if (ax.Elements.All(el => el is ConstantExpression))
21+
if (ax.Elements.All(el => el is ItemElement item &&
22+
item.Value is ConstantExpression))
2223
{
2324
return new ConstantExpression(
2425
new SequenceValue(ax.Elements
25-
.Cast<ConstantExpression>()
26-
.Select(ce => ce.Constant)));
26+
.Cast<ItemElement>()
27+
.Select(item => ((ConstantExpression)item.Value).Constant)));
2728
}
2829

2930
return base.Transform(ax);

src/Serilog.Expressions/Expressions/Compilation/Linq/Intrinsics.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Text.RegularExpressions;
77
using Serilog.Events;
88
using Serilog.Formatting.Display;
9+
910
// ReSharper disable ParameterTypeCanBeEnumerable.Global
1011

1112
namespace Serilog.Expressions.Compilation.Linq
@@ -16,7 +17,31 @@ static class Intrinsics
1617
static readonly LogEventPropertyValue Tombstone = new ScalarValue("😬 (if you see this you have found a bug.)");
1718
static readonly MessageTemplateTextFormatter MessageFormatter = new MessageTemplateTextFormatter("{Message:lj}");
1819

19-
public static LogEventPropertyValue ConstructSequenceValue(LogEventPropertyValue?[] elements)
20+
public static List<LogEventPropertyValue?> CollectSequenceElements(LogEventPropertyValue?[] elements)
21+
{
22+
return elements.ToList();
23+
}
24+
25+
public static List<LogEventPropertyValue?> ExtendSequenceValueWithItem(List<LogEventPropertyValue?> elements,
26+
LogEventPropertyValue? element)
27+
{
28+
// Mutates the list; returned so we can nest calls instead of emitting a block.
29+
if (element != null)
30+
elements.Add(element);
31+
return elements;
32+
}
33+
34+
public static List<LogEventPropertyValue?> ExtendSequenceValueWithSpread(List<LogEventPropertyValue?> elements,
35+
LogEventPropertyValue? content)
36+
{
37+
if (content is SequenceValue sequence)
38+
foreach (var element in sequence.Elements)
39+
elements.Add(element);
40+
41+
return elements;
42+
}
43+
44+
public static LogEventPropertyValue ConstructSequenceValue(List<LogEventPropertyValue?> elements)
2045
{
2146
if (elements.Any(el => el == null))
2247
return new SequenceValue(elements.Where(el => el != null));

src/Serilog.Expressions/Expressions/Compilation/Linq/LinqExpressionCompiler.cs

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ class LinqExpressionCompiler : SerilogExpressionTransformer<ExpressionBody>
1818
{
1919
readonly NameResolver _nameResolver;
2020

21+
static readonly MethodInfo CollectSequenceElementsMethod = typeof(Intrinsics)
22+
.GetMethod(nameof(Intrinsics.CollectSequenceElements), BindingFlags.Static | BindingFlags.Public)!;
23+
24+
static readonly MethodInfo ExtendSequenceValueWithSpreadMethod = typeof(Intrinsics)
25+
.GetMethod(nameof(Intrinsics.ExtendSequenceValueWithSpread), BindingFlags.Static | BindingFlags.Public)!;
26+
27+
static readonly MethodInfo ExtendSequenceValueWithItemMethod = typeof(Intrinsics)
28+
.GetMethod(nameof(Intrinsics.ExtendSequenceValueWithItem), BindingFlags.Static | BindingFlags.Public)!;
29+
2130
static readonly MethodInfo ConstructSequenceValueMethod = typeof(Intrinsics)
2231
.GetMethod(nameof(Intrinsics.ConstructSequenceValue), BindingFlags.Static | BindingFlags.Public)!;
2332

@@ -176,9 +185,33 @@ protected override ExpressionBody Transform(IndexerWildcardExpression wx)
176185

177186
protected override ExpressionBody Transform(ArrayExpression ax)
178187
{
179-
var elements = ax.Elements.Select(Transform).ToArray();
180-
var arr = LX.NewArrayInit(typeof(LogEventPropertyValue), elements);
181-
return LX.Call(ConstructSequenceValueMethod, arr);
188+
var elements = new List<ExpressionBody>(ax.Elements.Length);
189+
var i = 0;
190+
for (; i < ax.Elements.Length; ++i)
191+
{
192+
var element = ax.Elements[i];
193+
if (element is ItemElement item)
194+
elements.Add(Transform(item.Value));
195+
else
196+
break;
197+
}
198+
199+
var arr = LX.NewArrayInit(typeof(LogEventPropertyValue), elements.ToArray());
200+
var collected = LX.Call(CollectSequenceElementsMethod, arr);
201+
202+
for (; i < ax.Elements.Length; ++i)
203+
{
204+
var element = ax.Elements[i];
205+
if (element is ItemElement item)
206+
collected = LX.Call(ExtendSequenceValueWithItemMethod, collected, Transform(item.Value));
207+
else
208+
{
209+
var spread = (SpreadElement) element;
210+
collected = LX.Call(ExtendSequenceValueWithSpreadMethod, collected, Transform(spread.Content));
211+
}
212+
}
213+
214+
return LX.Call(ConstructSequenceValueMethod, collected);
182215
}
183216

184217
protected override ExpressionBody Transform(ObjectExpression ox)
@@ -190,7 +223,7 @@ protected override ExpressionBody Transform(ObjectExpression ox)
190223
for (; i < ox.Members.Length; ++i)
191224
{
192225
var member = ox.Members[i];
193-
if (member is Property property)
226+
if (member is PropertyMember property)
194227
{
195228
if (names.Contains(property.Name))
196229
{
@@ -211,37 +244,36 @@ protected override ExpressionBody Transform(ObjectExpression ox)
211244

212245
var namesConstant = LX.Constant(names.ToArray(), typeof(string[]));
213246
var valuesArr = LX.NewArrayInit(typeof(LogEventPropertyValue), values.ToArray());
214-
var collect = LX.Call(CollectStructurePropertiesMethod, namesConstant, valuesArr);
247+
var properties = LX.Call(CollectStructurePropertiesMethod, namesConstant, valuesArr);
215248

216249
if (i == ox.Members.Length)
217250
{
218-
// No spreads
219-
return LX.Call(ConstructStructureValueMethod, collect);
251+
// No spreads; more efficient than `Complete*` because erasure is not required.
252+
return LX.Call(ConstructStructureValueMethod, properties);
220253
}
221254

222-
var extended = collect;
223255
for (; i < ox.Members.Length; ++i)
224256
{
225257
var member = ox.Members[i];
226-
if (member is Property property)
258+
if (member is PropertyMember property)
227259
{
228-
extended = LX.Call(
260+
properties = LX.Call(
229261
ExtendStructureValueWithPropertyMethod,
230-
extended,
262+
properties,
231263
LX.Constant(property.Name),
232264
Transform(property.Value));
233265
}
234266
else
235267
{
236-
var spread = (Spread) member;
237-
extended = LX.Call(
268+
var spread = (SpreadMember) member;
269+
properties = LX.Call(
238270
ExtendStructureValueWithSpreadMethod,
239-
extended,
271+
properties,
240272
Transform(spread.Content));
241273
}
242274
}
243275

244-
return LX.Call(CompleteStructureValueMethod, extended);
276+
return LX.Call(CompleteStructureValueMethod, properties);
245277
}
246278

247279
protected override ExpressionBody Transform(IndexerExpression ix)

0 commit comments

Comments
 (0)