diff --git a/Build.ps1 b/Build.ps1
index 7c5a85f..8283851 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -12,40 +12,49 @@ if(Test-Path .\artifacts) {
$branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$env:APPVEYOR_REPO_BRANCH -ne $NULL];
$revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:APPVEYOR_BUILD_NUMBER, 10); $false = "local" }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL];
$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "master" -and $revision -ne "local"]
+$commitHash = $(git rev-parse --short HEAD)
+$buildSuffix = @{ $true = "$($suffix)-$($commitHash)"; $false = "$($branch)-$($commitHash)" }[$suffix -ne ""]
-echo "build: Version suffix is $suffix"
+echo "build: Package version suffix is $suffix"
+echo "build: Build version suffix is $buildSuffix"
foreach ($src in ls src/*) {
Push-Location $src
echo "build: Packaging project in $src"
- & dotnet pack -c Release -o ..\..\artifacts --version-suffix=$suffix
+ & dotnet build -c Release --version-suffix=$buildSuffix
+
+ if($suffix) {
+ & dotnet pack -c Release --include-source --no-build -o ..\..\artifacts --version-suffix=$suffix
+ } else {
+ & dotnet pack -c Release --include-source --no-build -o ..\..\artifacts
+ }
if($LASTEXITCODE -ne 0) { exit 1 }
Pop-Location
}
-foreach ($test in ls test/*.PerformanceTests) {
+foreach ($test in ls test/*.Tests) {
Push-Location $test
- echo "build: Building performance test project in $test"
+ echo "build: Testing project in $test"
- & dotnet build -c Release
- if($LASTEXITCODE -ne 0) { exit 2 }
+ & dotnet test -c Release
+ if($LASTEXITCODE -ne 0) { exit 3 }
Pop-Location
}
-foreach ($test in ls test/*.Tests) {
+foreach ($test in ls test/*.PerformanceTests) {
Push-Location $test
- echo "build: Testing project in $test"
+ echo "build: Building performance test project in $test"
- & dotnet test -c Release
- if($LASTEXITCODE -ne 0) { exit 3 }
+ & dotnet build -c Release
+ if($LASTEXITCODE -ne 0) { exit 2 }
Pop-Location
}
-Pop-Location
+Pop-Location
\ No newline at end of file
diff --git a/README.md b/README.md
index f0395e9..6cfbd34 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Serilog.Filters.Expressions [![Build status](https://ci.appveyor.com/api/projects/status/wnh0ig2udlld9oe4?svg=true)](https://ci.appveyor.com/project/serilog/serilog-filters-expressions) [![NuGet Pre Release](https://img.shields.io/nuget/vpre/Serilog.Filters.Expressions.svg)](https://nuget.org/packages/serilog.filters.expressions)
+# Serilog.Filters.Expressions [![Build status](https://ci.appveyor.com/api/projects/status/wnh0ig2udlld9oe4?svg=true)](https://ci.appveyor.com/project/serilog/serilog-filters-expressions) [![NuGet Release](https://img.shields.io/nuget/v/Serilog.Filters.Expressions.svg)](https://nuget.org/packages/serilog.filters.expressions)
Expression-based event filtering for [Serilog](https://serilog.net).
@@ -8,7 +8,7 @@ var expr = "@Level = 'Information' and AppId is not null and Items[?] like 'C%'"
Log.Logger = new LoggerConfiguration()
.Enrich.WithProperty("AppId", 10)
.Filter.ByIncludingOnly(expr)
- .WriteTo.LiterateConsole()
+ .WriteTo.Console()
.CreateLogger();
// Printed
@@ -40,10 +40,10 @@ The syntax is based on SQL, with added support for object structures, arrays, an
| :--- | :--- |
| **Literals** | `123`, `123.4`, `'Hello'`, `true`, `false`, `null` |
| **Properties** | `A`, `A.B`, `@Level`, `@Timestamp`, `@Exception`, `@Message`, `@MessageTemplate`, `@Properties['A-b-c']` |
-| **Comparisons** | `A = B`, `A <> B`, `A > B`, `A >= B`, `A is null`, `A is not null` |
-| **Text** | `A like 'H%'`, `A not like 'H%'`, `A like 'Hel_o'`, `Contains(A, 'H')`, `StartsWith(A, 'H')`, `EndsWith(A, 'H')`, `IndexOf(A, 'H')` |
+| **Comparisons** | `A = B`, `A <> B`, `A > B`, `A >= B`, `A is null`, `A is not null`, `A in [1, 2]` |
+| **Text** | `A like 'H%'`, `A not like 'H%'`, `A like 'Hel_o'`, `Contains(A, 'H')`, `StartsWith(A, 'H')`, `EndsWith(A, 'H')`, `IndexOf(A, 'H')`, `Length(A)` |
| **Regular expressions** | `A = /H.*o/`, `Contains(A, /[lL]/)`, other string functions |
-| **Collections** | `A[0] = 'Hello'`, `A[?] = 'Hello'` (any), `StartsWith(A[*], 'H')` (all) |
+| **Collections** | `A[0] = 'Hello'`, `A[?] = 'Hello'` (any), `StartsWith(A[*], 'H')` (all), `Length(A)` |
| **Maths** | `A + 2`, `A - 2`, `A * 2`, `A % 2` |
| **Logic** | `not A`, `A and B`, `A or B` |
| **Grouping** | `A * (B + C)` |
diff --git a/appveyor.yml b/appveyor.yml
index a33021a..cf14c5c 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,13 +1,6 @@
version: '{build}'
skip_tags: true
-image: Visual Studio 2015
-configuration: Release
-install:
- - ps: mkdir -Force ".\build\" | Out-Null
- - ps: Invoke-WebRequest "https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0-preview2/scripts/obtain/dotnet-install.ps1" -OutFile ".\build\installcli.ps1"
- - ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetcli"
- - ps: '& .\build\installcli.ps1 -InstallDir "$env:DOTNET_INSTALL_DIR" -NoPath -Version 1.0.0-preview2-003131'
- - ps: $env:Path = "$env:DOTNET_INSTALL_DIR;$env:Path"
+image: Visual Studio 2017
build_script:
- ps: ./Build.ps1
test: off
diff --git a/example/Sample/Sample.csproj b/example/Sample/Sample.csproj
new file mode 100644
index 0000000..e397cba
--- /dev/null
+++ b/example/Sample/Sample.csproj
@@ -0,0 +1,22 @@
+
+
+
+ netcoreapp1.0
+ Sample
+ Exe
+ Sample
+ $(PackageTargetFallback);dnxcore50
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/Sample/Sample.xproj b/example/Sample/Sample.xproj
deleted file mode 100644
index 5ff8cdd..0000000
--- a/example/Sample/Sample.xproj
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- 14.0
- $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
-
-
-
-
- 776eecac-3c50-45ea-847d-0ebe5158e51e
- Sample
- .\obj
- .\bin\
- v4.5.2
-
-
-
- 2.0
-
-
-
diff --git a/example/Sample/project.json b/example/Sample/project.json
deleted file mode 100644
index 415afe6..0000000
--- a/example/Sample/project.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "version": "1.0.0-*",
- "buildOptions": {
- "emitEntryPoint": true
- },
-
- "dependencies": {
- "Microsoft.NETCore.App": {
- "type": "platform",
- "version": "1.0.1"
- },
- "Serilog.Filters.Expressions": {"target": "project"},
- "Serilog.Sinks.Literate": "2.0.0"
- },
-
- "frameworks": {
- "netcoreapp1.0": {
- "imports": "dnxcore50"
- }
- }
-}
diff --git a/global.json b/global.json
deleted file mode 100644
index d1816c2..0000000
--- a/global.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "projects": [ "src", "test", "example" ],
- "sdk": {
- "version": "1.0.0-preview2-003131"
- }
-}
diff --git a/serilog-filters-expressions.sln b/serilog-filters-expressions.sln
index a1f5d19..98eb56a 100644
--- a/serilog-filters-expressions.sln
+++ b/serilog-filters-expressions.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25420.1
+# Visual Studio 15
+VisualStudioVersion = 15.0.26430.15
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{91E482DE-E1E7-4CE1-9511-C0AF07F3648A}"
EndProject
@@ -11,7 +11,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{24B112
.gitignore = .gitignore
appveyor.yml = appveyor.yml
Build.ps1 = Build.ps1
- global.json = global.json
LICENSE = LICENSE
README.md = README.md
RunPerfTests.ps1 = RunPerfTests.ps1
@@ -19,15 +18,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{24B112
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "example", "example", "{BD94A77E-34B1-478E-B921-E87A5F71B574}"
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Sample", "example\Sample\Sample.xproj", "{776EECAC-3C50-45EA-847D-0EBE5158E51E}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B03B3086-D197-4B32-9AE2-8536C345EA2D}"
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Serilog.Filters.Expressions", "src\Serilog.Filters.Expressions\Serilog.Filters.Expressions.xproj", "{A420C4E3-3A2D-4369-88EB-77E4DB1D0219}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "example\Sample\Sample.csproj", "{776EECAC-3C50-45EA-847D-0EBE5158E51E}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B03B3086-D197-4B32-9AE2-8536C345EA2D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Filters.Expressions", "src\Serilog.Filters.Expressions\Serilog.Filters.Expressions.csproj", "{A420C4E3-3A2D-4369-88EB-77E4DB1D0219}"
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Serilog.Filters.Expressions.Tests", "test\Serilog.Filters.Expressions.Tests\Serilog.Filters.Expressions.Tests.xproj", "{3C2D8E01-5580-426A-BDD9-EC59CD98E618}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Filters.Expressions.Tests", "test\Serilog.Filters.Expressions.Tests\Serilog.Filters.Expressions.Tests.csproj", "{3C2D8E01-5580-426A-BDD9-EC59CD98E618}"
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Serilog.Filters.Expressions.PerformanceTests", "test\Serilog.Filters.Expressions.PerformanceTests\Serilog.Filters.Expressions.PerformanceTests.xproj", "{D7A37F73-BBA3-4DAE-9648-1A753A86F968}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Filters.Expressions.PerformanceTests", "test\Serilog.Filters.Expressions.PerformanceTests\Serilog.Filters.Expressions.PerformanceTests.csproj", "{D7A37F73-BBA3-4DAE-9648-1A753A86F968}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterArrayExpression.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterArrayExpression.cs
new file mode 100644
index 0000000..1880fb8
--- /dev/null
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterArrayExpression.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Linq;
+
+namespace Serilog.Filters.Expressions.Ast
+{
+ class FilterArrayExpression : FilterExpression
+ {
+ public FilterArrayExpression(FilterExpression[] elements)
+ {
+ Elements = elements ?? throw new ArgumentNullException(nameof(elements));
+ }
+
+ public FilterExpression[] Elements { get; }
+
+ public override string ToString()
+ {
+ return "[" + string.Join(",", Elements.Select(o => o.ToString())) + "]";
+ }
+ }
+}
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterCallExpression.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterCallExpression.cs
index 19ca1ea..a2f6d2e 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterCallExpression.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterCallExpression.cs
@@ -5,30 +5,19 @@ namespace Serilog.Filters.Expressions.Ast
{
class FilterCallExpression : FilterExpression
{
- readonly string _operatorName;
- readonly FilterExpression[] _operands;
-
public FilterCallExpression(string operatorName, params FilterExpression[] operands)
{
- if (operatorName == null) throw new ArgumentNullException(nameof(operatorName));
- if (operands == null) throw new ArgumentNullException(nameof(operands));
- _operatorName = operatorName;
- _operands = operands;
+ OperatorName = operatorName ?? throw new ArgumentNullException(nameof(operatorName));
+ Operands = operands ?? throw new ArgumentNullException(nameof(operands));
}
- public string OperatorName
- {
- get { return _operatorName; }
- }
+ public string OperatorName { get; }
- public FilterExpression[] Operands
- {
- get { return _operands; }
- }
+ public FilterExpression[] Operands { get; }
public override string ToString()
{
- if (OperatorName == "ElementAt" && Operands.Length == 2)
+ if (OperatorName == Operators.OpElementAt && Operands.Length == 2)
{
return Operands[0] + "[" + Operands[1] + "]";
}
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterExpressionType.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterExpressionType.cs
index c9deba9..8eba462 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterExpressionType.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Ast/FilterExpressionType.cs
@@ -10,6 +10,7 @@ enum FilterExpressionType
Subproperty,
Wildcard,
Parameter,
- Lambda
+ Lambda,
+ Array
}
}
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Arrays/FilterExpressionConstantArrayEvaluator.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Arrays/FilterExpressionConstantArrayEvaluator.cs
new file mode 100644
index 0000000..dbdbc2a
--- /dev/null
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Arrays/FilterExpressionConstantArrayEvaluator.cs
@@ -0,0 +1,32 @@
+using System.Linq;
+using Serilog.Events;
+using Serilog.Filters.Expressions.Ast;
+using Serilog.Filters.Expressions.Compilation.Transformations;
+using Serilog.Filters.Expressions.Runtime;
+
+namespace Serilog.Filters.Expressions.Compilation.Arrays
+{
+ class FilterExpressionConstantArrayEvaluator : FilterExpressionIdentityTransformer
+ {
+ public static FilterExpression Evaluate(FilterExpression expression)
+ {
+ var evaluator = new FilterExpressionConstantArrayEvaluator();
+ return evaluator.Transform(expression);
+ }
+
+ protected override FilterExpression Transform(FilterArrayExpression ax)
+ {
+ // This should probably go depth-first.
+
+ if (ax.Elements.All(el => el is FilterConstantExpression))
+ {
+ return new FilterConstantExpression(
+ new SequenceValue(ax.Elements
+ .Cast()
+ .Select(ce => Representation.Recapture(ce.ConstantValue))));
+ }
+
+ return base.Transform(ax);
+ }
+ }
+}
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Costing/FilterExpressionCostReordering.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Costing/FilterExpressionCostReordering.cs
index 08caf65..81d38fa 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Costing/FilterExpressionCostReordering.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Costing/FilterExpressionCostReordering.cs
@@ -1,6 +1,5 @@
using Serilog.Filters.Expressions.Ast;
using Serilog.Filters.Expressions.Compilation.Transformations;
-using Superpower;
using System.Collections.Generic;
using System.Linq;
@@ -134,5 +133,10 @@ protected override FilterExpressionCosting Transform(FilterCallExpression lx)
new FilterCallExpression(lx.OperatorName, operands.Select(o => o.Expression).ToArray()),
operands.Sum(o => o.Costing) + 0.1);
}
+
+ protected override FilterExpressionCosting Transform(FilterArrayExpression ax)
+ {
+ return new FilterExpressionCosting(ax, 0.2);
+ }
}
}
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/FilterExpressionCompiler.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/FilterExpressionCompiler.cs
index e0ecaea..dfd32b4 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/FilterExpressionCompiler.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/FilterExpressionCompiler.cs
@@ -7,8 +7,10 @@
using Serilog.Filters.Expressions.Compilation.Text;
using Serilog.Filters.Expressions.Compilation.Wildcards;
using Serilog.Filters.Expressions.Compilation.Properties;
-using Serilog.Serilog.Filters.Expressions.Runtime;
+using Serilog.Filters.Expressions.Runtime;
using System;
+using Serilog.Filters.Expressions.Compilation.Arrays;
+using Serilog.Filters.Expressions.Compilation.In;
namespace Serilog.Filters.Expressions.Compilation
{
@@ -18,6 +20,8 @@ public static Func CompileAndExpose(FilterExpression expressio
{
var actual = expression;
actual = PropertiesObjectAccessorTransformer.Rewrite(actual);
+ actual = FilterExpressionConstantArrayEvaluator.Evaluate(actual);
+ actual = FilterExpressionNotInRewriter.Rewrite(actual);
actual = WildcardComprehensionTransformer.Expand(actual);
actual = LikeOperatorTransformer.Rewrite(actual);
actual = IsOperatorTransformer.Rewrite(actual);
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/In/FilterExpressionNotInRewriter.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/In/FilterExpressionNotInRewriter.cs
new file mode 100644
index 0000000..1f5e033
--- /dev/null
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/In/FilterExpressionNotInRewriter.cs
@@ -0,0 +1,22 @@
+using Serilog.Filters.Expressions.Ast;
+using Serilog.Filters.Expressions.Compilation.Transformations;
+
+namespace Serilog.Filters.Expressions.Compilation.In
+{
+ class FilterExpressionNotInRewriter : FilterExpressionIdentityTransformer
+ {
+ public static FilterExpression Rewrite(FilterExpression expression)
+ {
+ var rewriter = new FilterExpressionNotInRewriter();
+ return rewriter.Transform(expression);
+ }
+
+ protected override FilterExpression Transform(FilterCallExpression lx)
+ {
+ if (Operators.SameOperator(Operators.IntermediateOpSqlNotIn, lx.OperatorName))
+ return new FilterCallExpression(Operators.RuntimeOpStrictNot,
+ new FilterCallExpression(Operators.RuntimeOpSqlIn, lx.Operands));
+ return base.Transform(lx);
+ }
+ }
+}
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Linq/LinqExpressionCompiler.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Linq/LinqExpressionCompiler.cs
index e7e0674..c38836f 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Linq/LinqExpressionCompiler.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Linq/LinqExpressionCompiler.cs
@@ -2,7 +2,6 @@
using Serilog.Filters.Expressions.Ast;
using Serilog.Filters.Expressions.Compilation.Transformations;
using Serilog.Filters.Expressions.Runtime;
-using Serilog.Serilog.Filters.Expressions.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -161,7 +160,7 @@ protected override Expression Transform(FilterProperty
if (px.IsBuiltIn)
{
if (px.PropertyName == "Level")
- return context => context.Level.ToString() ?? "Information";
+ return context => context.Level.ToString();
if (px.PropertyName == "Message")
return context => NormalizeBaseDocumentProperty(context.RenderMessage(null));
@@ -257,5 +256,14 @@ protected override Expression Transform(FilterWildcard
{
return context => Undefined.Value;
}
+
+ protected override Expression Transform(FilterArrayExpression ax)
+ {
+ var context = Expression.Parameter(typeof(LogEvent));
+ var elements = ax.Elements.Select(Transform).Select(ex => Splice(ex, context)).ToArray();
+ var arr = Expression.NewArrayInit(typeof(object), elements);
+ var sv = Expression.Call(OperatorMethods[Operators.RuntimeOpNewSequence], arr);
+ return Expression.Lambda(Expression.Convert(sv, typeof(object)), context);
+ }
}
}
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionIdentityTransformer.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionIdentityTransformer.cs
index 2dc7743..8f93618 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionIdentityTransformer.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionIdentityTransformer.cs
@@ -47,5 +47,10 @@ protected override FilterExpression Transform(FilterWildcardExpression wx)
{
return wx;
}
+
+ protected override FilterExpression Transform(FilterArrayExpression ax)
+ {
+ return new FilterArrayExpression(ax.Elements.Select(Transform).ToArray());
+ }
}
}
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs
index dc0dff3..45e4c6a 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs
@@ -25,6 +25,8 @@ public TResult Transform(FilterExpression expression)
return Transform((FilterParameterExpression)expression);
case FilterExpressionType.Wildcard:
return Transform((FilterWildcardExpression)expression);
+ case FilterExpressionType.Array:
+ return Transform((FilterArrayExpression)expression);
default:
throw new ArgumentException(expression.Type + " is not a valid expression type");
}
@@ -38,5 +40,6 @@ public TResult Transform(FilterExpression expression)
protected abstract TResult Transform(FilterLambdaExpression lmx);
protected abstract TResult Transform(FilterParameterExpression prx);
protected abstract TResult Transform(FilterWildcardExpression wx);
+ protected abstract TResult Transform(FilterArrayExpression ax);
}
}
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Wildcards/FilterExpressionWildcardSearch.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Wildcards/FilterExpressionWildcardSearch.cs
index 6ece0f2..cacd66e 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Wildcards/FilterExpressionWildcardSearch.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Compilation/Wildcards/FilterExpressionWildcardSearch.cs
@@ -59,5 +59,10 @@ protected override FilterCallExpression Transform(FilterWildcardExpression wx)
// Must be RHS of ElementAt()
return null;
}
+
+ protected override FilterCallExpression Transform(FilterArrayExpression ax)
+ {
+ return null;
+ }
}
}
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/FilterExpressionLanguage.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/FilterExpressionLanguage.cs
index 464ddf7..11f6c23 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/FilterExpressionLanguage.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/FilterExpressionLanguage.cs
@@ -3,6 +3,7 @@
using Serilog.Filters.Expressions.Compilation;
using System;
using System.Linq;
+using Serilog.Filters.Expressions.Parsing;
namespace Serilog.Filters.Expressions
{
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Operators.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Operators.cs
index fedd94d..f0690b4 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Operators.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Operators.cs
@@ -62,8 +62,11 @@ static class Operators
public const string IntermediateOpSqlLike = "_Internal_Like";
public const string IntermediateOpSqlNotLike = "_Internal_NotLike";
public const string IntermediateOpSqlIs = "_Internal_Is";
+ public const string RuntimeOpSqlIn = "_Internal_In";
+ public const string IntermediateOpSqlNotIn = "_Internal_NotIn";
public const string RuntimeOpStrictNot = "_Internal_StrictNot";
public const string OpSubstring = "Substring";
+ public const string RuntimeOpNewSequence = "_Internal_NewSequence";
// Breaks the symmetry because there's no other way to express this in SQL.
public const string OpIndexOfIgnoreCase = "IndexOfIgnoreCase";
@@ -81,6 +84,8 @@ static class Operators
OpGreaterThanOrEqual,
IntermediateOpSqlLike,
IntermediateOpSqlNotLike,
+ RuntimeOpSqlIn,
+ IntermediateOpSqlNotIn,
IntermediateOpSqlIs
};
@@ -105,6 +110,8 @@ static class Operators
OpGreaterThanOrEqual,
IntermediateOpSqlLike,
IntermediateOpSqlNotLike,
+ RuntimeOpSqlIn,
+ IntermediateOpSqlNotIn,
IntermediateOpSqlIs
};
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionParser.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionParser.cs
index 36a3b4d..52d3970 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionParser.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionParser.cs
@@ -1,15 +1,11 @@
-using Serilog.Filters.Expressions.Ast;
-using Serilog.Filters.Expressions.Parsing;
+using System;
+using Serilog.Filters.Expressions.Ast;
using Superpower;
-using System;
-using System.Collections.Generic;
-namespace Serilog.Filters.Expressions
+namespace Serilog.Filters.Expressions.Parsing
{
static class FilterExpressionParser
{
- static readonly FilterExpressionTokenizer _tokenizer = new FilterExpressionTokenizer();
-
public static FilterExpression Parse(string filterExpression)
{
FilterExpression root;
@@ -24,9 +20,9 @@ public static bool TryParse(string filterExpression, out FilterExpression root,
{
if (filterExpression == null) throw new ArgumentNullException(nameof(filterExpression));
- var tokenList = _tokenizer.TryTokenize(filterExpression);
-
- var errorList = new List();
+ var tokenizer = new FilterExpressionTokenizer();
+ var tokenList = tokenizer.TryTokenize(filterExpression);
+
if (!tokenList.HasValue)
{
error = tokenList.ToString();
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionToken.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionToken.cs
index 28560a1..42b99b1 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionToken.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionToken.cs
@@ -81,7 +81,10 @@ enum FilterExpressionToken
[Token(Category = "keyword", Example = "and")]
And,
-
+
+ [Token(Category = "keyword", Example = "in")]
+ In,
+
[Token(Category = "keyword", Example = "is")]
Is,
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenParsers.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenParsers.cs
index a5c29ba..f214aff 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenParsers.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenParsers.cs
@@ -27,6 +27,8 @@ static class FilterExpressionTokenParsers
static readonly TokenListParser Not = Token.EqualTo(FilterExpressionToken.Not).Value(Operators.OpNot);
static readonly TokenListParser Like = Token.EqualTo(FilterExpressionToken.Like).Value(Operators.IntermediateOpSqlLike);
static readonly TokenListParser NotLike = Not.IgnoreThen(Like).Value(Operators.IntermediateOpSqlNotLike);
+ static readonly TokenListParser In = Token.EqualTo(FilterExpressionToken.In).Value(Operators.RuntimeOpSqlIn);
+ static readonly TokenListParser NotIn = Not.IgnoreThen(In).Value(Operators.IntermediateOpSqlNotIn);
static readonly TokenListParser Is = Token.EqualTo(FilterExpressionToken.Is).Value(Operators.IntermediateOpSqlIs);
static readonly TokenListParser> PropertyPathStep =
@@ -46,10 +48,16 @@ from close in Token.EqualTo(FilterExpressionToken.RBracket)
static readonly TokenListParser Function =
(from name in Token.EqualTo(FilterExpressionToken.Identifier)
- from lparen in Token.EqualTo(FilterExpressionToken.LParen)
- from expr in Parse.Ref(() => Expr).ManyDelimitedBy(Token.EqualTo(FilterExpressionToken.Comma))
+ from lparen in Token.EqualTo(FilterExpressionToken.LParen)
+ from expr in Parse.Ref(() => Expr).ManyDelimitedBy(Token.EqualTo(FilterExpressionToken.Comma))
from rparen in Token.EqualTo(FilterExpressionToken.RParen)
- select (FilterExpression)new FilterCallExpression(name.ToStringValue(), expr)).Named("function");
+ select (FilterExpression)new FilterCallExpression(name.ToStringValue(), expr)).Named("function");
+
+ static readonly TokenListParser ArrayLiteral =
+ (from lbracket in Token.EqualTo(FilterExpressionToken.LBracket)
+ from expr in Parse.Ref(() => Expr).ManyDelimitedBy(Token.EqualTo(FilterExpressionToken.Comma))
+ from rbracket in Token.EqualTo(FilterExpressionToken.RBracket)
+ select (FilterExpression)new FilterArrayExpression(expr)).Named("array");
static readonly TokenListParser RootProperty =
Token.EqualTo(FilterExpressionToken.BuiltInIdentifier).Select(b => (FilterExpression)new FilterPropertyExpression(b.ToStringValue().Substring(1), true))
@@ -96,7 +104,7 @@ from path in PropertyPathStep.Or(PropertyPathIndexerStep).Many()
.Or(Token.EqualTo(FilterExpressionToken.Null).Value((FilterExpression)new FilterConstantExpression(null)))
.Named("literal");
- static readonly TokenListParser Item = Literal.Or(PropertyPath).Or(Function);
+ static readonly TokenListParser Item = Literal.Or(PropertyPath).Or(Function).Or(ArrayLiteral);
static readonly TokenListParser Factor =
(from lparen in Token.EqualTo(FilterExpressionToken.LParen)
@@ -116,7 +124,7 @@ from factor in Factor
static readonly TokenListParser Comparand = Parse.Chain(Add.Or(Subtract), Term, MakeBinary);
- static readonly TokenListParser Comparison = Parse.Chain(Is.Or(NotLike.Try().Or(Like)).Or(Lte.Or(Neq).Or(Lt)).Or(Gte.Or(Gt)).Or(Eq), Comparand, MakeBinary);
+ static readonly TokenListParser Comparison = Parse.Chain(Is.Or(NotLike.Try().Or(Like)).Or(NotIn.Try().Or(In)).Or(Lte.Or(Neq).Or(Lt)).Or(Gte.Or(Gt)).Or(Eq), Comparand, MakeBinary);
static readonly TokenListParser Conjunction = Parse.Chain(And, Comparison, MakeBinary);
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenizer.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenizer.cs
index 66a01da..2f02a4e 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenizer.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Parsing/FilterExpressionTokenizer.cs
@@ -23,12 +23,14 @@ class FilterExpressionTokenizer : Tokenizer
FilterExpressionToken.GreaterThanOrEqual,
FilterExpressionToken.LessThan,
FilterExpressionToken.LessThanOrEqual,
+ FilterExpressionToken.In,
FilterExpressionToken.Is
};
static readonly FilterExpressionKeyword[] SqlKeywords =
{
new FilterExpressionKeyword("and", FilterExpressionToken.And),
+ new FilterExpressionKeyword("in", FilterExpressionToken.In),
new FilterExpressionKeyword("is", FilterExpressionToken.Is),
new FilterExpressionKeyword("like", FilterExpressionToken.Like),
new FilterExpressionKeyword("not", FilterExpressionToken.Not),
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/Representation.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/Representation.cs
index 1225825..7b83c08 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/Representation.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/Representation.cs
@@ -1,9 +1,8 @@
using Serilog.Events;
-using Serilog.Filters.Expressions.Runtime;
using System;
using System.Linq;
-namespace Serilog.Serilog.Filters.Expressions.Runtime
+namespace Serilog.Filters.Expressions.Runtime
{
static class Representation
{
@@ -64,5 +63,10 @@ public static object Expose(object internalValue)
return internalValue;
}
+
+ public static LogEventPropertyValue Recapture(object value)
+ {
+ return value is LogEventPropertyValue lepv ? lepv : new ScalarValue(value);
+ }
}
}
diff --git a/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/RuntimeOperators.cs b/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/RuntimeOperators.cs
index 72be8f1..e9d9ffe 100644
--- a/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/RuntimeOperators.cs
+++ b/src/Serilog.Filters.Expressions/Filters/Expressions/Runtime/RuntimeOperators.cs
@@ -1,6 +1,5 @@
using Serilog.Events;
using Serilog.Filters.Expressions.Compilation.Linq;
-using Serilog.Serilog.Filters.Expressions.Runtime;
using System;
using System.Globalization;
using System.Linq;
@@ -94,16 +93,38 @@ public static object GreaterThan(object left, object right)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static object GreaterThanOrEqual(object left, object right)
{
- return ((decimal)left) >= ((decimal)right);
+ return (decimal)left >= (decimal)right;
}
[AcceptNull]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static object Equal(object left, object right)
{
- return left == null ? right == null : left.Equals(right);
+ return UnboxedEqualHelper(left, right);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool UnboxedEqualHelper(object left, object right)
+ {
+ return left?.Equals(right) ?? right == null;
+ }
+
+ [AcceptNull]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static object _Internal_In(object item, object collection)
+ {
+ if (collection is SequenceValue arr)
+ {
+ for (var i = 0; i < arr.Elements.Count; ++i)
+ if (UnboxedEqualHelper(Representation.Represent(arr.Elements[i]), item))
+ return true;
+
+ return false;
+ }
+
+ return Undefined.Value;
+ }
+
[AcceptNull]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static object _Internal_EqualIgnoreCase(object left, object right)
@@ -138,7 +159,7 @@ public static object _Internal_EqualPattern(object left, object right)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static object NotEqual(object left, object right)
{
- return !(bool)Equal(left, right);
+ return !UnboxedEqualHelper(left, right);
}
[AcceptNull]
@@ -252,12 +273,15 @@ public static object _Internal_IndexOfPattern(object corpus, object pattern)
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static object Length(object corpus)
+ public static object Length(object arg)
{
- var ctx = corpus as string;
- if (ctx == null)
- return Undefined.Value;
- return (decimal)ctx.Length;
+ if (arg is string str)
+ return (decimal)str.Length;
+
+ if (arg is SequenceValue seq)
+ return (decimal) seq.Elements.Count;
+
+ return Undefined.Value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -335,8 +359,7 @@ public static object Has(object value)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static object ElementAt(object items, object index)
{
- var arr = items as SequenceValue;
- if (arr != null)
+ if (items is SequenceValue arr)
{
if (!(index is decimal))
return Undefined.Value;
@@ -346,14 +369,13 @@ public static object ElementAt(object items, object index)
return Undefined.Value;
var idx = (int)dec;
- if (idx >= arr.Elements.Count())
+ if (idx >= arr.Elements.Count)
return Undefined.Value;
return Representation.Represent(arr.Elements.ElementAt(idx));
}
-
- var dict = items as StructureValue;
- if (dict != null)
+
+ if (items is StructureValue dict)
{
var s = index as string;
if (s == null)
@@ -366,24 +388,23 @@ public static object ElementAt(object items, object index)
return Representation.Represent(value);
}
+ // Case for DictionaryValue is missing, here.
+
return Undefined.Value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static object _Internal_Any(object items, object predicate)
{
- var pred = predicate as Func