Skip to content

Commit 9d7b013

Browse files
committed
Green tests; more cleanup
1 parent a47d4c4 commit 9d7b013

File tree

9 files changed

+138
-28
lines changed

9 files changed

+138
-28
lines changed

src/Serilog.Expressions/Expressions/Compilation/ExpressionCompiler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ public static CompiledExpression Compile(Expression expression)
1515
{
1616
var actual = expression;
1717
actual = TextMatchingTransformer.Rewrite(actual);
18+
actual = LikeSyntaxTransformer.Rewrite(actual);
1819
actual = PropertiesObjectAccessorTransformer.Rewrite(actual);
1920
actual = ConstantArrayEvaluator.Evaluate(actual);
20-
actual = NotInRewriter.Rewrite(actual);
21+
actual = NotInRewriter.Rewrite(actual);
2122
actual = WildcardComprehensionTransformer.Expand(actual);
2223
actual = IsOperatorTransformer.Rewrite(actual);
2324

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Collections.Generic;
22
using System.Linq.Expressions;
3+
using Serilog.Events;
34

45
namespace Serilog.Expressions.Compilation.Linq
56
{
@@ -14,7 +15,9 @@ public ExpressionConstantMapper(IDictionary<object, Expression> mapping)
1415

1516
protected override Expression VisitConstant(ConstantExpression node)
1617
{
17-
if (node.Value != null && _mapping.TryGetValue(node.Value, out var substitute))
18+
if (node.Value != null &&
19+
node.Value is ScalarValue sv &&
20+
_mapping.TryGetValue(sv.Value, out var substitute))
1821
return substitute;
1922

2023
return base.VisitConstant(node);

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,15 +254,19 @@ protected override Expression<CompiledExpression> Transform(Ast.LambdaExpression
254254
throw new NotSupportedException("Unsupported lambda signature.");
255255

256256
var lambda = LX.Lambda(delegateType, rewritten!, parms.Select(px => px.Item2).ToArray());
257+
258+
// Unfortunately, right now, functions need to be threaded through in constant scalar values :-D
259+
var constant = LX.New(typeof(ScalarValue).GetConstructor(new[] {typeof(object)})!,
260+
LX.Convert(lambda, typeof(object)));
257261

258-
return LX.Lambda<CompiledExpression>(lambda, context);
262+
return LX.Lambda<CompiledExpression>(constant, context);
259263
}
260264

261265
protected override Expression<CompiledExpression> Transform(Ast.ParameterExpression prx)
262266
{
263267
// Will be within a lambda, which will subsequently sub-in the actual value
264268
var context = LX.Parameter(typeof(LogEvent));
265-
var constant = LX.Constant(prx, typeof(object));
269+
var constant = LX.Constant(new ScalarValue(prx), typeof(LogEventPropertyValue));
266270
return LX.Lambda<CompiledExpression>(constant, context);
267271
}
268272

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Text.RegularExpressions;
3+
using Serilog.Debugging;
4+
using Serilog.Events;
5+
using Serilog.Expressions.Ast;
6+
using Serilog.Expressions.Compilation.Transformations;
7+
8+
namespace Serilog.Expressions.Compilation.Text
9+
{
10+
class LikeSyntaxTransformer: IdentityTransformer
11+
{
12+
static readonly LikeSyntaxTransformer Instance = new LikeSyntaxTransformer();
13+
14+
public static Expression Rewrite(Expression expression)
15+
{
16+
return Instance.Transform(expression);
17+
}
18+
19+
protected override Expression Transform(CallExpression lx)
20+
{
21+
if (lx.Operands.Length != 2)
22+
return base.Transform(lx);
23+
24+
if (Operators.SameOperator(lx.OperatorName, Operators.IntermediateOpSqlLike))
25+
return TryCompileLikeExpression(lx.Operands[0], lx.Operands[1]);
26+
27+
if (Operators.SameOperator(lx.OperatorName, Operators.IntermediateOpSqlNotLike))
28+
return new CallExpression(
29+
Operators.RuntimeOpStrictNot,
30+
TryCompileLikeExpression(lx.Operands[0], lx.Operands[1]));
31+
32+
return base.Transform(lx);
33+
}
34+
35+
Expression TryCompileLikeExpression(Expression corpus, Expression like)
36+
{
37+
if (like is ConstantExpression cx &&
38+
cx.Constant is ScalarValue scalar &&
39+
scalar.Value is string s)
40+
{
41+
var regex = LikeToRegex(s);
42+
var compiled = new Regex(regex, RegexOptions.Compiled | RegexOptions.ExplicitCapture, TimeSpan.FromMilliseconds(100));
43+
var indexof = new IndexOfMatchExpression(Transform(corpus), compiled);
44+
return new CallExpression(Operators.OpNotEqual, indexof, new ConstantExpression(new ScalarValue(-1)));
45+
}
46+
47+
SelfLog.WriteLine($"Serilog.Expressions: `like` requires a constant string argument; found ${like}.");
48+
return new AmbientPropertyExpression(BuiltInProperty.Undefined, true);
49+
}
50+
51+
static string LikeToRegex(string like)
52+
{
53+
var regex = "";
54+
for (var i = 0; i < like.Length; ++i)
55+
{
56+
var ch = like[i];
57+
char? following = i == like.Length - 1 ? (char?)null : like[i + 1];
58+
if (ch == '%')
59+
{
60+
if (following == '%')
61+
{
62+
regex += '%';
63+
++i;
64+
}
65+
else
66+
{
67+
regex += "(?:.|\\r|\\n)*"; // ~= RegexOptions.Singleline
68+
}
69+
}
70+
else if (ch == '_')
71+
{
72+
if (following == '_')
73+
{
74+
regex += '_';
75+
++i;
76+
}
77+
else
78+
{
79+
regex += '.'; // Newlines aren't considered matches for _
80+
}
81+
}
82+
else
83+
regex += Regex.Escape(ch.ToString());
84+
}
85+
86+
return regex;
87+
}
88+
}
89+
}

src/Serilog.Expressions/Expressions/Compilation/Transformations/IdentityTransformer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class IdentityTransformer : SerilogExpressionTransformer<Expression>
88
bool TryTransform(Expression expr, out Expression result)
99
{
1010
result = Transform(expr);
11-
return ReferenceEquals(expr, result);
11+
return !ReferenceEquals(expr, result);
1212
}
1313

1414
protected override Expression Transform(CallExpression lx)

src/Serilog.Expressions/Expressions/Compilation/Transformations/NodeReplacer.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,13 @@ protected override Expression Transform(ArrayExpression ax)
9090

9191
return base.Transform(ax);
9292
}
93+
94+
protected override Expression Transform(IndexOfMatchExpression mx)
95+
{
96+
if (mx == _source)
97+
return _dest;
98+
99+
return base.Transform(mx);
100+
}
93101
}
94102
}

src/Serilog.Expressions/Expressions/Compilation/Wildcards/WildcardComprehensionTransformer.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,30 @@ public static Expression Expand(Expression root)
1515

1616
protected override Expression Transform(CallExpression lx)
1717
{
18-
if (!Operators.WildcardComparators.Contains(lx.OperatorName) || lx.Operands.Length != 2)
18+
var target = lx;
19+
20+
if (!Operators.WildcardComparators.Contains(target.OperatorName) || target.Operands.Length != 2)
1921
return base.Transform(lx);
2022

21-
var lhsIs = WildcardSearch.FindElementAtWildcard(lx.Operands[0]);
22-
var rhsIs = WildcardSearch.FindElementAtWildcard(lx.Operands[1]);
23+
var lhsIs = WildcardSearch.FindElementAtWildcard(target.Operands[0]);
24+
var rhsIs = WildcardSearch.FindElementAtWildcard(target.Operands[1]);
2325
if (lhsIs != null && rhsIs != null || lhsIs == null && rhsIs == null)
2426
return base.Transform(lx); // N/A, or invalid
2527

26-
var wcpath = lhsIs != null ? lx.Operands[0] : lx.Operands[1];
27-
var comparand = lhsIs != null ? lx.Operands[1] : lx.Operands[0];
28-
var elmat = lhsIs ?? rhsIs;
28+
var wildcardPath = lhsIs != null ? target.Operands[0] : target.Operands[1];
29+
var comparand = lhsIs != null ? target.Operands[1] : target.Operands[0];
30+
var indexer = lhsIs ?? rhsIs;
2931

30-
var parm = new ParameterExpression("p" + _nextParameter++);
31-
var nestedComparand = NodeReplacer.Replace(wcpath, elmat, parm);
32+
var px = new ParameterExpression("p" + _nextParameter++);
33+
var nestedComparand = NodeReplacer.Replace(wildcardPath, indexer, px);
3234

33-
var coll = elmat.Receiver;
34-
var wc = ((IndexerWildcardExpression)elmat.Index).Wildcard;
35+
var coll = indexer.Receiver;
36+
var wc = ((IndexerWildcardExpression)indexer.Index).Wildcard;
3537

3638
var comparisonArgs = lhsIs != null ? new[] { nestedComparand, comparand } : new[] { comparand, nestedComparand };
37-
var body = new CallExpression(lx.OperatorName, comparisonArgs);
38-
var lambda = new LambdaExpression(new[] { parm }, body);
39+
var body = new CallExpression(target.OperatorName, comparisonArgs);
40+
41+
var lambda = new LambdaExpression(new[] { px }, body);
3942

4043
var op = Operators.ToRuntimeWildcardOperator(wc);
4144
var call = new CallExpression(op, coll, lambda);

src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenParsers.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,10 @@ from rbracket in Token.EqualTo(ExpressionToken.RBracket)
6868
select (Expression)new ArrayExpression(expr)).Named("array");
6969

7070
static readonly TokenListParser<ExpressionToken, Expression> RootProperty =
71-
Token.EqualTo(ExpressionToken.BuiltInIdentifier).Select(b => (Expression)new AmbientPropertyExpression(b.ToStringValue().Substring(1), true))
72-
.Or(Token.EqualTo(ExpressionToken.Identifier).Select(t => (Expression)new AmbientPropertyExpression(t.ToStringValue(), false)));
73-
74-
static readonly TokenListParser<ExpressionToken, Expression> PropertyPath =
7571
(from notFunction in Parse.Not(Token.EqualTo(ExpressionToken.Identifier).IgnoreThen(Token.EqualTo(ExpressionToken.LParen)))
76-
from root in RootProperty
77-
from path in PropertyPathStep.Or(PropertyPathIndexerStep).Many()
78-
select path.Aggregate(root, (o, f) => f(o))).Named("property");
72+
from p in Token.EqualTo(ExpressionToken.BuiltInIdentifier).Select(b => (Expression) new AmbientPropertyExpression(b.ToStringValue().Substring(1), true))
73+
.Or(Token.EqualTo(ExpressionToken.Identifier).Select(t => (Expression) new AmbientPropertyExpression(t.ToStringValue(), false)))
74+
select p).Named("property");
7975

8076
static readonly TokenListParser<ExpressionToken, Expression> String =
8177
Token.EqualTo(ExpressionToken.String)
@@ -103,7 +99,7 @@ from path in PropertyPathStep.Or(PropertyPathIndexerStep).Many()
10399
.Or(Token.EqualTo(ExpressionToken.Null).Value((Expression)new ConstantExpression(new ScalarValue(null))))
104100
.Named("literal");
105101

106-
static readonly TokenListParser<ExpressionToken, Expression> Item = Literal.Or(PropertyPath).Or(Function).Or(ArrayLiteral);
102+
static readonly TokenListParser<ExpressionToken, Expression> Item = Literal.Or(RootProperty).Or(Function).Or(ArrayLiteral);
107103

108104
static readonly TokenListParser<ExpressionToken, Expression> Factor =
109105
(from lparen in Token.EqualTo(ExpressionToken.LParen)
@@ -112,10 +108,15 @@ from rparen in Token.EqualTo(ExpressionToken.RParen)
112108
select expr)
113109
.Or(Item);
114110

111+
static readonly TokenListParser<ExpressionToken, Expression> Path =
112+
from root in Factor
113+
from path in PropertyPathStep.Or(PropertyPathIndexerStep).Many()
114+
select path.Aggregate(root, (o, f) => f(o));
115+
115116
static readonly TokenListParser<ExpressionToken, Expression> Operand =
116117
(from op in Negate.Or(Not)
117-
from factor in Factor
118-
select MakeUnary(op, factor)).Or(Factor).Named("expression");
118+
from path in Path
119+
select MakeUnary(op, path)).Or(Path).Named("expression");
119120

120121
static readonly TokenListParser<ExpressionToken, Expression> InnerTerm = Parse.Chain(Power, Operand, MakeBinary);
121122

test/Serilog.Expressions.Tests/ExpressionCompilerTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@ public void ExpressionsEvaluateStringSuffix()
5252
}
5353

5454
[Fact]
55-
public void LikeIsCaseInsensitive()
55+
public void LikeIsCaseSensitive()
5656
{
5757
AssertEvaluation("Fruit like 'apple'",
58+
Some.InformationEvent("Snacking on {Fruit}", "apple"),
5859
Some.InformationEvent("Snacking on {Fruit}", "Apple"));
5960
}
6061

0 commit comments

Comments
 (0)